/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Tests that generational garbage collection post-barriers are correctly * implemented for nsTArrays that contain JavaScript Values. */ #include "jsapi.h" #include "nsTArray.h" #include "gtest/gtest.h" #include "js/TracingAPI.h" #include "js/HeapAPI.h" #include "mozilla/CycleCollectedJSContext.h" using namespace JS; using namespace mozilla; template static void TraceArray(JSTracer* trc, void* data) { ArrayT* array = static_cast(data); for (unsigned i = 0; i < array->Length(); ++i) JS::TraceEdge(trc, &array->ElementAt(i), "array-element"); } /* * Use arrays with initial size much smaller than the final number of elements * to test that moving Heap elements works correctly. */ const size_t ElementCount = 100; const size_t InitialElements = ElementCount / 10; template static void RunTest(JSContext* cx, ArrayT* array) { JS_GC(cx); ASSERT_TRUE(array != nullptr); JS_AddExtraGCRootsTracer(cx, TraceArray, array); /* * Create the array and fill it with new JS objects. With GGC these will be * allocated in the nursery. */ RootedValue value(cx); const char* property = "foo"; for (size_t i = 0; i < ElementCount; ++i) { RootedObject obj(cx, JS_NewPlainObject(cx)); ASSERT_FALSE(JS::ObjectIsTenured(obj)); value = Int32Value(i); ASSERT_TRUE(JS_SetProperty(cx, obj, property, value)); ASSERT_TRUE(array->AppendElement(obj, fallible)); } /* * If postbarriers are not working, we will crash here when we try to mark * objects that have been moved to the tenured heap. */ JS_GC(cx); /* * Sanity check that our array contains what we expect. */ for (size_t i = 0; i < ElementCount; ++i) { RootedObject obj(cx, array->ElementAt(i)); ASSERT_TRUE(JS::ObjectIsTenured(obj)); ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value)); ASSERT_TRUE(value.isInt32()); ASSERT_EQ(static_cast(i), value.toInt32()); } JS_RemoveExtraGCRootsTracer(cx, TraceArray, array); } static void CreateGlobalAndRunTest(JSContext* cx) { static const JSClassOps GlobalClassOps = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; static const JSClass GlobalClass = { "global", JSCLASS_GLOBAL_FLAGS, &GlobalClassOps }; JS::CompartmentOptions options; options.behaviors().setVersion(JSVERSION_LATEST); JS::PersistentRootedObject global(cx); global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options); ASSERT_TRUE(global != nullptr); JSCompartment *oldCompartment = JS_EnterCompartment(cx, global); typedef Heap ElementT; { nsTArray* array = new nsTArray(InitialElements); RunTest(cx, array); delete array; } { FallibleTArray* array = new FallibleTArray(InitialElements); RunTest(cx, array); delete array; } { AutoTArray array; RunTest(cx, &array); } JS_LeaveCompartment(cx, oldCompartment); } TEST(GCPostBarriers, nsTArray) { CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get(); ASSERT_TRUE(ccjscx != nullptr); JSContext* cx = ccjscx->Context(); ASSERT_TRUE(cx != nullptr); JS_BeginRequest(cx); CreateGlobalAndRunTest(cx); JS_EndRequest(cx); }