michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ 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: /* michael@0: * Tests that generational garbage collection post-barriers are correctly michael@0: * implemented for nsTArrays that contain JavaScript Values. michael@0: */ michael@0: michael@0: #include "jsapi.h" michael@0: #include "nsTArray.h" michael@0: michael@0: #include "gtest/gtest.h" michael@0: michael@0: #include "js/TracingAPI.h" michael@0: #include "js/HeapAPI.h" michael@0: michael@0: #include "mozilla/CycleCollectedJSRuntime.h" michael@0: michael@0: using namespace JS; michael@0: using namespace mozilla; michael@0: michael@0: template michael@0: static void michael@0: TraceArray(JSTracer* trc, void* data) michael@0: { michael@0: ArrayT* array = static_cast(data); michael@0: for (unsigned i = 0; i < array->Length(); ++i) michael@0: JS_CallHeapObjectTracer(trc, &array->ElementAt(i), "array-element"); michael@0: } michael@0: michael@0: /* michael@0: * Use arrays with initial size much smaller than the final number of elements michael@0: * to test that moving Heap elements works correctly. michael@0: */ michael@0: const size_t ElementCount = 100; michael@0: const size_t InitialElements = ElementCount / 10; michael@0: michael@0: template michael@0: static void michael@0: RunTest(JSRuntime* rt, JSContext* cx, ArrayT* array) michael@0: { michael@0: JS_GC(rt); michael@0: michael@0: ASSERT_TRUE(array != nullptr); michael@0: JS_AddExtraGCRootsTracer(rt, TraceArray, array); michael@0: michael@0: /* michael@0: * Create the array and fill it with new JS objects. With GGC these will be michael@0: * allocated in the nursery. michael@0: */ michael@0: RootedValue value(cx); michael@0: const char* property = "foo"; michael@0: JS::shadow::Runtime* srt = reinterpret_cast(rt); michael@0: for (size_t i = 0; i < ElementCount; ++i) { michael@0: RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: #ifdef JSGC_GENERATIONAL michael@0: ASSERT_TRUE(js::gc::IsInsideNursery(srt, obj)); michael@0: #else michael@0: ASSERT_FALSE(js::gc::IsInsideNursery(srt, obj)); michael@0: #endif michael@0: value = Int32Value(i); michael@0: ASSERT_TRUE(JS_SetProperty(cx, obj, property, value)); michael@0: array->AppendElement(obj); michael@0: } michael@0: michael@0: /* michael@0: * If postbarriers are not working, we will crash here when we try to mark michael@0: * objects that have been moved to the tenured heap. michael@0: */ michael@0: JS_GC(rt); michael@0: michael@0: /* michael@0: * Sanity check that our array contains what we expect. michael@0: */ michael@0: for (size_t i = 0; i < ElementCount; ++i) { michael@0: RootedObject obj(cx, array->ElementAt(i)); michael@0: ASSERT_FALSE(js::gc::IsInsideNursery(srt, obj)); michael@0: ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value)); michael@0: ASSERT_TRUE(value.isInt32()); michael@0: ASSERT_EQ(static_cast(i), value.toInt32()); michael@0: } michael@0: michael@0: JS_RemoveExtraGCRootsTracer(rt, TraceArray, array); michael@0: } michael@0: michael@0: static void michael@0: CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx) michael@0: { michael@0: static const JSClass GlobalClass = { michael@0: "global", JSCLASS_GLOBAL_FLAGS, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, michael@0: nullptr, nullptr, nullptr, nullptr, michael@0: JS_GlobalObjectTraceHook michael@0: }; michael@0: michael@0: JS::CompartmentOptions options; michael@0: options.setVersion(JSVERSION_LATEST); michael@0: JS::PersistentRootedObject global(cx); michael@0: global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options); michael@0: ASSERT_TRUE(global != nullptr); michael@0: michael@0: JSCompartment *oldCompartment = JS_EnterCompartment(cx, global); michael@0: michael@0: typedef Heap ElementT; michael@0: michael@0: { michael@0: nsTArray* array = new nsTArray(InitialElements); michael@0: RunTest(rt, cx, array); michael@0: delete array; michael@0: } michael@0: michael@0: { michael@0: FallibleTArray* array = new FallibleTArray(InitialElements); michael@0: RunTest(rt, cx, array); michael@0: delete array; michael@0: } michael@0: michael@0: { michael@0: nsAutoTArray array; michael@0: RunTest(rt, cx, &array); michael@0: } michael@0: michael@0: { michael@0: AutoFallibleTArray array; michael@0: RunTest(rt, cx, &array); michael@0: } michael@0: michael@0: JS_LeaveCompartment(cx, oldCompartment); michael@0: } michael@0: michael@0: TEST(GCPostBarriers, nsTArray) { michael@0: CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get(); michael@0: ASSERT_TRUE(ccrt != nullptr); michael@0: JSRuntime* rt = ccrt->Runtime(); michael@0: ASSERT_TRUE(rt != nullptr); michael@0: michael@0: JSContext *cx = JS_NewContext(rt, 8192); michael@0: ASSERT_TRUE(cx != nullptr); michael@0: JS_BeginRequest(cx); michael@0: michael@0: CreateGlobalAndRunTest(rt, cx); michael@0: michael@0: JS_EndRequest(cx); michael@0: JS_DestroyContext(cx); michael@0: }