michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if defined(JSGC_USE_EXACT_ROOTING) michael@0: michael@0: #include "js/Class.h" michael@0: #include "jsapi-tests/tests.h" michael@0: michael@0: using namespace JS; michael@0: michael@0: struct BarkWhenTracedClass { michael@0: static int finalizeCount; michael@0: static int traceCount; michael@0: michael@0: static const JSClass class_; michael@0: static void finalize(JSFreeOp *fop, JSObject *obj) { finalizeCount++; } michael@0: static void trace(JSTracer *trc, JSObject *obj) { traceCount++; } michael@0: static void reset() { finalizeCount = 0; traceCount = 0; } michael@0: }; michael@0: michael@0: int BarkWhenTracedClass::finalizeCount; michael@0: int BarkWhenTracedClass::traceCount; michael@0: michael@0: const JSClass BarkWhenTracedClass::class_ = { michael@0: "BarkWhenTracedClass", 0, michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: finalize, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: trace michael@0: }; michael@0: michael@0: struct Kennel { michael@0: PersistentRootedObject obj; michael@0: Kennel(JSContext *cx) : obj(cx) { } michael@0: Kennel(JSContext *cx, const HandleObject &woof) : obj(cx, woof) { }; michael@0: }; michael@0: michael@0: // A function for allocating a Kennel and a barker. Only allocating michael@0: // PersistentRooteds on the heap, and in this function, helps ensure that the michael@0: // conservative GC doesn't find stray references to the barker. Ugh. michael@0: MOZ_NEVER_INLINE static Kennel * michael@0: Allocate(JSContext *cx) michael@0: { michael@0: RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, JS::NullPtr(), JS::NullPtr())); michael@0: if (!barker) michael@0: return nullptr; michael@0: michael@0: return new Kennel(cx, barker); michael@0: } michael@0: michael@0: // Do a GC, expecting |n| barkers to be finalized. michael@0: static bool michael@0: GCFinalizesNBarkers(JSContext *cx, int n) michael@0: { michael@0: int preGCTrace = BarkWhenTracedClass::traceCount; michael@0: int preGCFinalize = BarkWhenTracedClass::finalizeCount; michael@0: michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: michael@0: return (BarkWhenTracedClass::finalizeCount == preGCFinalize + n && michael@0: BarkWhenTracedClass::traceCount > preGCTrace); michael@0: } michael@0: michael@0: // PersistentRooted instances protect their contents from being recycled. michael@0: BEGIN_TEST(test_PersistentRooted) michael@0: { michael@0: BarkWhenTracedClass::reset(); michael@0: michael@0: Kennel *kennel = Allocate(cx); michael@0: CHECK(kennel); michael@0: michael@0: // GC should be able to find our barker. michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: delete(kennel); michael@0: michael@0: // Now GC should not be able to find the barker. michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: CHECK(BarkWhenTracedClass::finalizeCount == 1); michael@0: michael@0: return true; michael@0: } michael@0: END_TEST(test_PersistentRooted) michael@0: michael@0: // GC should not be upset by null PersistentRooteds. michael@0: BEGIN_TEST(test_PersistentRootedNull) michael@0: { michael@0: BarkWhenTracedClass::reset(); michael@0: michael@0: Kennel kennel(cx); michael@0: CHECK(!kennel.obj); michael@0: michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: CHECK(BarkWhenTracedClass::finalizeCount == 0); michael@0: michael@0: return true; michael@0: } michael@0: END_TEST(test_PersistentRootedNull) michael@0: michael@0: // Copy construction works. michael@0: BEGIN_TEST(test_PersistentRootedCopy) michael@0: { michael@0: BarkWhenTracedClass::reset(); michael@0: michael@0: Kennel *kennel = Allocate(cx); michael@0: CHECK(kennel); michael@0: michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: // Copy construction! AMAZING! michael@0: Kennel *newKennel = new Kennel(*kennel); michael@0: michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: delete(kennel); michael@0: michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: delete(newKennel); michael@0: michael@0: // Now that kennel and nowKennel are both deallocated, GC should not be michael@0: // able to find the barker. michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: CHECK(BarkWhenTracedClass::finalizeCount == 1); michael@0: michael@0: return true; michael@0: } michael@0: END_TEST(test_PersistentRootedCopy) michael@0: michael@0: // Assignment works. michael@0: BEGIN_TEST(test_PersistentRootedAssign) michael@0: { michael@0: BarkWhenTracedClass::reset(); michael@0: michael@0: Kennel *kennel = Allocate(cx); michael@0: CHECK(kennel); michael@0: michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: // Allocate a new, empty kennel. michael@0: Kennel *kennel2 = new Kennel(cx); michael@0: michael@0: // Assignment! ASTONISHING! michael@0: *kennel2 = *kennel; michael@0: michael@0: // With both kennels referring to the same barker, it is held alive. michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: delete(kennel2); michael@0: michael@0: // The destination of the assignment alone holds the barker alive. michael@0: CHECK(GCFinalizesNBarkers(cx, 0)); michael@0: michael@0: // Allocate a second barker. michael@0: kennel2 = Allocate(cx); michael@0: CHECK(kennel); michael@0: michael@0: *kennel = *kennel2; michael@0: michael@0: // Nothing refers to the first kennel any more. michael@0: CHECK(GCFinalizesNBarkers(cx, 1)); michael@0: michael@0: delete(kennel); michael@0: delete(kennel2); michael@0: michael@0: // Now that kennel and kennel2 are both deallocated, GC should not be michael@0: // able to find the barker. michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: CHECK(BarkWhenTracedClass::finalizeCount == 2); michael@0: michael@0: return true; michael@0: } michael@0: END_TEST(test_PersistentRootedAssign) michael@0: michael@0: #endif // defined(JSGC_USE_EXACT_ROOTING)