ongoing testconfig work
[tamarin-stm.git] / extensions / ST_mmgc_finalize_uninit.cpp
blob6e8cd7507448d0549834f9b10ea1c470ae3381cd
1 // Generated from ST_mmgc_finalize_uninit.st
2 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
3 // vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
5 // Bugzilla 573737 - a throw from an argument to a constructor can
6 // cause an object to be allocated on the gc-heap before it has been
7 // fully initialized. (In particular, its vtable could be missing.)
8 // Since finalizers use the virtual destructor method, a missing
9 // vtable is a problem.
11 // Its a little tricky to test this because the "order of evaluation
12 // to an operator new() to get memory and the evaluation of arguments
13 // to constructors is undefined"; see details below.
15 #include "avmshell.h"
16 #ifdef VMCFG_SELFTEST
17 namespace avmplus {
19 using namespace MMgc;
21 class D : public GCFinalizedObject
23 public:
24 D(int dummyArgument) { (void)dummyArgument; }
25 ~D() { ++finalized; }
26 static int finalized_count() { return D::finalized; }
27 static void reset_finalized_count() { finalized = 0; }
28 // "usual" overload of placement new used in mmgc client code
29 void * operator new(size_t size, GC *gc) {
30 return GCFinalizedObject::operator new(size, gc);
32 // hack to explicitly order new-allocation + ctor-arg eval; see below
33 // (but reallys its just good ol' placement new!)
34 void * operator new(size_t size, GC *gc, void *raw) {
35 (void)size; (void)gc;
36 return raw;
38 private:
39 static int finalized;
42 /*static*/
43 int D::finalized;
45 int deathValue(AvmCore *core)
47 core->throwAtom(atomFromIntptrValue(1)); /* won't return */
49 return 0; /* silence compiler warnings */
52 // Test checks that finalizers themselves have not broken; here,
53 // constructor argument subexpression evaluates cleanly.
54 class ST_mmgc_finalize_uninit : public Selftest {
55 public:
56 ST_mmgc_finalize_uninit(AvmCore* core);
57 virtual void run(int n);
58 private:
59 static const char* ST_names[];
60 static const bool ST_explicits[];
61 void test0();
62 void test1();
63 void test2();
64 void test3();
66 ST_mmgc_finalize_uninit::ST_mmgc_finalize_uninit(AvmCore* core)
67 : Selftest(core, "mmgc", "finalize_uninit", ST_mmgc_finalize_uninit::ST_names,ST_mmgc_finalize_uninit::ST_explicits)
69 const char* ST_mmgc_finalize_uninit::ST_names[] = {"check_finalizers_still_work","original_death","desugared_check_finalizers_still_work","desugared_death", NULL };
70 const bool ST_mmgc_finalize_uninit::ST_explicits[] = {false,false,false,false, false };
71 void ST_mmgc_finalize_uninit::run(int n) {
72 switch(n) {
73 case 0: test0(); return;
74 case 1: test1(); return;
75 case 2: test2(); return;
76 case 3: test3(); return;
79 void ST_mmgc_finalize_uninit::test0() {
81 D::reset_finalized_count();
82 D* d;
83 int i;
85 // loop to alloc many (and subsequently reclaim >=expected percentage)
86 for (i = 0; i < 100; i++) {
87 d = new (core->gc) D(1);
89 (void) d;
91 core->gc->Collect(); // finish any prior incremental work ...
92 core->gc->Collect(); // ... and ensure we got fresh + complete gc.
94 // printf("D::finalized_count(): %d\n", D::finalized_count());
96 #line 71 "ST_mmgc_finalize_uninit.st"
97 verifyPass((D::finalized_count() > 90), "(D::finalized_count() > 90)", __FILE__, __LINE__);
100 // Test illustrates of the kind of code that exposed the original bug;
101 // here, constructor argument subexpression throws.
103 void ST_mmgc_finalize_uninit::test1() {
105 D* d;
106 volatile int i;
108 // Here, constructor argument subexpression throws
110 // Just one allocation attempt alone would risk false retention of
111 // intermediate values, so loop a bit to ensure that *some* D's,
112 // if allocated at all, will be considered garbage.
113 for (i = 0; i < 5; i++) {
114 TRY (core, kCatchAction_Ignore) {
115 d = new (core->gc) D(deathValue(core));
116 } CATCH (Exception *e) {
117 (void)e;
119 END_CATCH
120 END_TRY
122 (void) d;
124 // if things go badly, one of the collections below will segfault
125 // during finalization.
126 core->gc->Collect();
127 core->gc->Collect();
129 // (not dying is passing.)
130 #line 103 "ST_mmgc_finalize_uninit.st"
131 verifyPass(true, "true", __FILE__, __LINE__);
134 // C++ standard says "The order of evaluation to an operator new() to
135 // get memory and the evaluation of arguments to constructors is
136 // undefined."
138 // Unfortunately, it is difficult to directly express the particular
139 // order of evaluation that exposes the bug in question, because
140 // allocation and construction are tied together.
142 // So, here we manually decompose the tests above to control
143 // evaluation order of operator new() and constructor arguments, to
144 // express suitable evil (namely: allocation; args eval; construction)
145 // for selftest.
147 // The desugaring iteself is:
148 // desugar[[ new (gc-exp) D(arg-exp) ]]
149 // ==> mem = D::operator new(sizeof(D), arg), new (gc-exp, mem) D(arg-exp)
151 // Test illustrates desugaring is "sound"; keep in sync with
152 // check_finalizers_still_work above.
154 void ST_mmgc_finalize_uninit::test2() {
156 D::reset_finalized_count();
157 D* d;
158 int i;
160 // loop to alloc many (and subsequently reclaim >=expected percentage)
161 for (i = 0; i < 100; i++) {
162 // d = new (core->gc) D(s);
163 void *mem = D::operator new(sizeof(D), core->gc);
164 d = new (core->gc, mem) D(1);
166 (void) d;
168 core->gc->Collect(); // finish any prior incremental work ...
169 core->gc->Collect(); // ... and ensure we got fresh + complete gc.
171 // printf("D::finalized_count(): %d\n", D::finalized_count());
173 #line 144 "ST_mmgc_finalize_uninit.st"
174 verifyPass((D::finalized_count() > 90), "(D::finalized_count() > 90)", __FILE__, __LINE__);
178 // Test forces evil order of evaluation via desugaring of
179 // construction; keep in sync with original_death above.
181 void ST_mmgc_finalize_uninit::test3() {
183 D* d;
184 volatile int i;
186 // Here, constructor argument subexpression throws
188 // Just one allocation attempt alone would risk false retention of
189 // intermediate values, so loop a bit to ensure that *some* D's,
190 // which are forcibly allocated here, will be considered garbage.
191 for (i = 0; i < 5; i++) {
192 TRY (core, kCatchAction_Ignore) {
193 // d = new (core->gc) D(deathValue());
194 void *mem = D::operator new(sizeof(D), core->gc);
195 d = new (core->gc, mem) D(deathValue(core));
196 } CATCH (Exception *e) {
197 (void)e;
199 END_CATCH
200 END_TRY
202 (void) d;
204 // if things go badly, one of the collections below will segfault
205 // during finalization.
206 core->gc->Collect();
207 core->gc->Collect();
209 // (not dying is passing.)
210 #line 179 "ST_mmgc_finalize_uninit.st"
211 verifyPass(true, "true", __FILE__, __LINE__);
215 void create_mmgc_finalize_uninit(AvmCore* core) { new ST_mmgc_finalize_uninit(core); }
217 #endif