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/. */
6 #include "jsapi-tests/tests.h"
10 struct BarkWhenTracedClass
{
11 static int finalizeCount
;
12 static int traceCount
;
14 static const JSClass class_
;
15 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
) { finalizeCount
++; }
16 static void trace(JSTracer
* trc
, JSObject
* obj
) { traceCount
++; }
23 int BarkWhenTracedClass::finalizeCount
;
24 int BarkWhenTracedClass::traceCount
;
26 static const JSClassOps BarkWhenTracedClassClassOps
= {
27 nullptr, // addProperty
28 nullptr, // delProperty
30 nullptr, // newEnumerate
32 nullptr, // mayResolve
33 BarkWhenTracedClass::finalize
, // finalize
36 BarkWhenTracedClass::trace
, // trace
39 const JSClass
BarkWhenTracedClass::class_
= {"BarkWhenTracedClass",
40 JSCLASS_FOREGROUND_FINALIZE
,
41 &BarkWhenTracedClassClassOps
};
44 PersistentRootedObject obj
;
46 explicit Kennel(JSContext
* cx
) : obj(cx
) {}
47 Kennel(JSContext
* cx
, const HandleObject
& woof
) : obj(cx
, woof
) {}
48 void init(JSContext
* cx
, const HandleObject
& woof
) { obj
.init(cx
, woof
); }
49 void clear() { obj
= nullptr; }
52 // A function for allocating a Kennel and a barker. Only allocating
53 // PersistentRooteds on the heap, and in this function, helps ensure that the
54 // conservative GC doesn't find stray references to the barker. Ugh.
55 MOZ_NEVER_INLINE
static Kennel
* Allocate(JSContext
* cx
) {
56 RootedObject
barker(cx
, JS_NewObject(cx
, &BarkWhenTracedClass::class_
));
61 return new Kennel(cx
, barker
);
64 // Do a GC, expecting |n| barkers to be finalized.
65 static bool GCFinalizesNBarkers(JSContext
* cx
, int n
) {
66 int preGCTrace
= BarkWhenTracedClass::traceCount
;
67 int preGCFinalize
= BarkWhenTracedClass::finalizeCount
;
71 return (BarkWhenTracedClass::finalizeCount
== preGCFinalize
+ n
&&
72 BarkWhenTracedClass::traceCount
> preGCTrace
);
75 // PersistentRooted instances protect their contents from being recycled.
76 BEGIN_TEST(test_PersistentRooted
) {
77 BarkWhenTracedClass::reset();
79 mozilla::UniquePtr
<Kennel
> kennel(Allocate(cx
));
82 // GC should be able to find our barker.
83 CHECK(GCFinalizesNBarkers(cx
, 0));
87 // Now GC should not be able to find the barker.
89 CHECK(BarkWhenTracedClass::finalizeCount
== 1);
93 END_TEST(test_PersistentRooted
)
95 // GC should not be upset by null PersistentRooteds.
96 BEGIN_TEST(test_PersistentRootedNull
) {
97 BarkWhenTracedClass::reset();
103 CHECK(BarkWhenTracedClass::finalizeCount
== 0);
107 END_TEST(test_PersistentRootedNull
)
109 // Copy construction works.
110 BEGIN_TEST(test_PersistentRootedCopy
) {
111 BarkWhenTracedClass::reset();
113 mozilla::UniquePtr
<Kennel
> kennel(Allocate(cx
));
116 CHECK(GCFinalizesNBarkers(cx
, 0));
118 // Copy construction! AMAZING!
119 mozilla::UniquePtr
<Kennel
> newKennel(new Kennel(*kennel
));
121 CHECK(GCFinalizesNBarkers(cx
, 0));
125 CHECK(GCFinalizesNBarkers(cx
, 0));
129 // Now that kennel and nowKennel are both deallocated, GC should not be
130 // able to find the barker.
132 CHECK(BarkWhenTracedClass::finalizeCount
== 1);
136 END_TEST(test_PersistentRootedCopy
)
139 BEGIN_TEST(test_PersistentRootedAssign
) {
140 BarkWhenTracedClass::reset();
142 mozilla::UniquePtr
<Kennel
> kennel(Allocate(cx
));
145 CHECK(GCFinalizesNBarkers(cx
, 0));
147 // Allocate a new, empty kennel.
148 mozilla::UniquePtr
<Kennel
> kennel2(new Kennel(cx
));
150 // Assignment! ASTONISHING!
153 // With both kennels referring to the same barker, it is held alive.
154 CHECK(GCFinalizesNBarkers(cx
, 0));
158 // The destination of the assignment alone holds the barker alive.
159 CHECK(GCFinalizesNBarkers(cx
, 0));
161 // Allocate a second barker.
162 kennel2
= mozilla::UniquePtr
<Kennel
>(Allocate(cx
));
163 CHECK(kennel2
.get());
167 // Nothing refers to the first kennel any more.
168 CHECK(GCFinalizesNBarkers(cx
, 1));
173 // Now that kennel and kennel2 are both deallocated, GC should not be
174 // able to find the barker.
176 CHECK(BarkWhenTracedClass::finalizeCount
== 2);
180 END_TEST(test_PersistentRootedAssign
)
182 static PersistentRootedObject gGlobalRoot
;
184 // PersistentRooted instances can initialized in a separate step to allow for
185 // global PersistentRooteds.
186 BEGIN_TEST(test_GlobalPersistentRooted
) {
187 BarkWhenTracedClass::reset();
189 CHECK(!gGlobalRoot
.initialized());
192 RootedObject
barker(cx
, JS_NewObject(cx
, &BarkWhenTracedClass::class_
));
195 gGlobalRoot
.init(cx
, barker
);
198 CHECK(gGlobalRoot
.initialized());
200 // GC should be able to find our barker.
201 CHECK(GCFinalizesNBarkers(cx
, 0));
204 CHECK(!gGlobalRoot
.initialized());
206 // Now GC should not be able to find the barker.
208 CHECK(BarkWhenTracedClass::finalizeCount
== 1);
212 END_TEST(test_GlobalPersistentRooted
)