Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #if defined(JSGC_USE_EXACT_ROOTING)
7 #include "js/Class.h"
8 #include "jsapi-tests/tests.h"
10 using namespace JS;
12 struct BarkWhenTracedClass {
13 static int finalizeCount;
14 static int traceCount;
16 static const JSClass class_;
17 static void finalize(JSFreeOp *fop, JSObject *obj) { finalizeCount++; }
18 static void trace(JSTracer *trc, JSObject *obj) { traceCount++; }
19 static void reset() { finalizeCount = 0; traceCount = 0; }
20 };
22 int BarkWhenTracedClass::finalizeCount;
23 int BarkWhenTracedClass::traceCount;
25 const JSClass BarkWhenTracedClass::class_ = {
26 "BarkWhenTracedClass", 0,
27 JS_PropertyStub,
28 JS_DeletePropertyStub,
29 JS_PropertyStub,
30 JS_StrictPropertyStub,
31 JS_EnumerateStub,
32 JS_ResolveStub,
33 JS_ConvertStub,
34 finalize,
35 nullptr,
36 nullptr,
37 nullptr,
38 trace
39 };
41 struct Kennel {
42 PersistentRootedObject obj;
43 Kennel(JSContext *cx) : obj(cx) { }
44 Kennel(JSContext *cx, const HandleObject &woof) : obj(cx, woof) { };
45 };
47 // A function for allocating a Kennel and a barker. Only allocating
48 // PersistentRooteds on the heap, and in this function, helps ensure that the
49 // conservative GC doesn't find stray references to the barker. Ugh.
50 MOZ_NEVER_INLINE static Kennel *
51 Allocate(JSContext *cx)
52 {
53 RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, JS::NullPtr(), JS::NullPtr()));
54 if (!barker)
55 return nullptr;
57 return new Kennel(cx, barker);
58 }
60 // Do a GC, expecting |n| barkers to be finalized.
61 static bool
62 GCFinalizesNBarkers(JSContext *cx, int n)
63 {
64 int preGCTrace = BarkWhenTracedClass::traceCount;
65 int preGCFinalize = BarkWhenTracedClass::finalizeCount;
67 JS_GC(JS_GetRuntime(cx));
69 return (BarkWhenTracedClass::finalizeCount == preGCFinalize + n &&
70 BarkWhenTracedClass::traceCount > preGCTrace);
71 }
73 // PersistentRooted instances protect their contents from being recycled.
74 BEGIN_TEST(test_PersistentRooted)
75 {
76 BarkWhenTracedClass::reset();
78 Kennel *kennel = Allocate(cx);
79 CHECK(kennel);
81 // GC should be able to find our barker.
82 CHECK(GCFinalizesNBarkers(cx, 0));
84 delete(kennel);
86 // Now GC should not be able to find the barker.
87 JS_GC(JS_GetRuntime(cx));
88 CHECK(BarkWhenTracedClass::finalizeCount == 1);
90 return true;
91 }
92 END_TEST(test_PersistentRooted)
94 // GC should not be upset by null PersistentRooteds.
95 BEGIN_TEST(test_PersistentRootedNull)
96 {
97 BarkWhenTracedClass::reset();
99 Kennel kennel(cx);
100 CHECK(!kennel.obj);
102 JS_GC(JS_GetRuntime(cx));
103 CHECK(BarkWhenTracedClass::finalizeCount == 0);
105 return true;
106 }
107 END_TEST(test_PersistentRootedNull)
109 // Copy construction works.
110 BEGIN_TEST(test_PersistentRootedCopy)
111 {
112 BarkWhenTracedClass::reset();
114 Kennel *kennel = Allocate(cx);
115 CHECK(kennel);
117 CHECK(GCFinalizesNBarkers(cx, 0));
119 // Copy construction! AMAZING!
120 Kennel *newKennel = new Kennel(*kennel);
122 CHECK(GCFinalizesNBarkers(cx, 0));
124 delete(kennel);
126 CHECK(GCFinalizesNBarkers(cx, 0));
128 delete(newKennel);
130 // Now that kennel and nowKennel are both deallocated, GC should not be
131 // able to find the barker.
132 JS_GC(JS_GetRuntime(cx));
133 CHECK(BarkWhenTracedClass::finalizeCount == 1);
135 return true;
136 }
137 END_TEST(test_PersistentRootedCopy)
139 // Assignment works.
140 BEGIN_TEST(test_PersistentRootedAssign)
141 {
142 BarkWhenTracedClass::reset();
144 Kennel *kennel = Allocate(cx);
145 CHECK(kennel);
147 CHECK(GCFinalizesNBarkers(cx, 0));
149 // Allocate a new, empty kennel.
150 Kennel *kennel2 = new Kennel(cx);
152 // Assignment! ASTONISHING!
153 *kennel2 = *kennel;
155 // With both kennels referring to the same barker, it is held alive.
156 CHECK(GCFinalizesNBarkers(cx, 0));
158 delete(kennel2);
160 // The destination of the assignment alone holds the barker alive.
161 CHECK(GCFinalizesNBarkers(cx, 0));
163 // Allocate a second barker.
164 kennel2 = Allocate(cx);
165 CHECK(kennel);
167 *kennel = *kennel2;
169 // Nothing refers to the first kennel any more.
170 CHECK(GCFinalizesNBarkers(cx, 1));
172 delete(kennel);
173 delete(kennel2);
175 // Now that kennel and kennel2 are both deallocated, GC should not be
176 // able to find the barker.
177 JS_GC(JS_GetRuntime(cx));
178 CHECK(BarkWhenTracedClass::finalizeCount == 2);
180 return true;
181 }
182 END_TEST(test_PersistentRootedAssign)
184 #endif // defined(JSGC_USE_EXACT_ROOTING)