|
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/. */ |
|
4 |
|
5 #if defined(JSGC_USE_EXACT_ROOTING) |
|
6 |
|
7 #include "js/Class.h" |
|
8 #include "jsapi-tests/tests.h" |
|
9 |
|
10 using namespace JS; |
|
11 |
|
12 struct BarkWhenTracedClass { |
|
13 static int finalizeCount; |
|
14 static int traceCount; |
|
15 |
|
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 }; |
|
21 |
|
22 int BarkWhenTracedClass::finalizeCount; |
|
23 int BarkWhenTracedClass::traceCount; |
|
24 |
|
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 }; |
|
40 |
|
41 struct Kennel { |
|
42 PersistentRootedObject obj; |
|
43 Kennel(JSContext *cx) : obj(cx) { } |
|
44 Kennel(JSContext *cx, const HandleObject &woof) : obj(cx, woof) { }; |
|
45 }; |
|
46 |
|
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; |
|
56 |
|
57 return new Kennel(cx, barker); |
|
58 } |
|
59 |
|
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; |
|
66 |
|
67 JS_GC(JS_GetRuntime(cx)); |
|
68 |
|
69 return (BarkWhenTracedClass::finalizeCount == preGCFinalize + n && |
|
70 BarkWhenTracedClass::traceCount > preGCTrace); |
|
71 } |
|
72 |
|
73 // PersistentRooted instances protect their contents from being recycled. |
|
74 BEGIN_TEST(test_PersistentRooted) |
|
75 { |
|
76 BarkWhenTracedClass::reset(); |
|
77 |
|
78 Kennel *kennel = Allocate(cx); |
|
79 CHECK(kennel); |
|
80 |
|
81 // GC should be able to find our barker. |
|
82 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
83 |
|
84 delete(kennel); |
|
85 |
|
86 // Now GC should not be able to find the barker. |
|
87 JS_GC(JS_GetRuntime(cx)); |
|
88 CHECK(BarkWhenTracedClass::finalizeCount == 1); |
|
89 |
|
90 return true; |
|
91 } |
|
92 END_TEST(test_PersistentRooted) |
|
93 |
|
94 // GC should not be upset by null PersistentRooteds. |
|
95 BEGIN_TEST(test_PersistentRootedNull) |
|
96 { |
|
97 BarkWhenTracedClass::reset(); |
|
98 |
|
99 Kennel kennel(cx); |
|
100 CHECK(!kennel.obj); |
|
101 |
|
102 JS_GC(JS_GetRuntime(cx)); |
|
103 CHECK(BarkWhenTracedClass::finalizeCount == 0); |
|
104 |
|
105 return true; |
|
106 } |
|
107 END_TEST(test_PersistentRootedNull) |
|
108 |
|
109 // Copy construction works. |
|
110 BEGIN_TEST(test_PersistentRootedCopy) |
|
111 { |
|
112 BarkWhenTracedClass::reset(); |
|
113 |
|
114 Kennel *kennel = Allocate(cx); |
|
115 CHECK(kennel); |
|
116 |
|
117 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
118 |
|
119 // Copy construction! AMAZING! |
|
120 Kennel *newKennel = new Kennel(*kennel); |
|
121 |
|
122 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
123 |
|
124 delete(kennel); |
|
125 |
|
126 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
127 |
|
128 delete(newKennel); |
|
129 |
|
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); |
|
134 |
|
135 return true; |
|
136 } |
|
137 END_TEST(test_PersistentRootedCopy) |
|
138 |
|
139 // Assignment works. |
|
140 BEGIN_TEST(test_PersistentRootedAssign) |
|
141 { |
|
142 BarkWhenTracedClass::reset(); |
|
143 |
|
144 Kennel *kennel = Allocate(cx); |
|
145 CHECK(kennel); |
|
146 |
|
147 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
148 |
|
149 // Allocate a new, empty kennel. |
|
150 Kennel *kennel2 = new Kennel(cx); |
|
151 |
|
152 // Assignment! ASTONISHING! |
|
153 *kennel2 = *kennel; |
|
154 |
|
155 // With both kennels referring to the same barker, it is held alive. |
|
156 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
157 |
|
158 delete(kennel2); |
|
159 |
|
160 // The destination of the assignment alone holds the barker alive. |
|
161 CHECK(GCFinalizesNBarkers(cx, 0)); |
|
162 |
|
163 // Allocate a second barker. |
|
164 kennel2 = Allocate(cx); |
|
165 CHECK(kennel); |
|
166 |
|
167 *kennel = *kennel2; |
|
168 |
|
169 // Nothing refers to the first kennel any more. |
|
170 CHECK(GCFinalizesNBarkers(cx, 1)); |
|
171 |
|
172 delete(kennel); |
|
173 delete(kennel2); |
|
174 |
|
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); |
|
179 |
|
180 return true; |
|
181 } |
|
182 END_TEST(test_PersistentRootedAssign) |
|
183 |
|
184 #endif // defined(JSGC_USE_EXACT_ROOTING) |