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.
21 class D
: public GCFinalizedObject
24 D(int dummyArgument
) { (void)dummyArgument
; }
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
) {
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
{
56 ST_mmgc_finalize_uninit(AvmCore
* core
);
57 virtual void run(int n
);
59 static const char* ST_names
[];
60 static const bool ST_explicits
[];
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
) {
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();
85 // loop to alloc many (and subsequently reclaim >=expected percentage)
86 for (i
= 0; i
< 100; i
++) {
87 d
= new (core
->gc
) D(1);
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() {
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
) {
124 // if things go badly, one of the collections below will segfault
125 // during finalization.
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
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)
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();
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);
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() {
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
) {
204 // if things go badly, one of the collections below will segfault
205 // during finalization.
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
); }