Bug 1922904 - Fix bug 1780701 in a different approach. r=botond
[gecko.git] / xpcom / tests / gtest / TestGCPostBarriers.cpp
blobc32cd48c869fffc6d44406e60c82c854ae016491
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Tests that generational garbage collection post-barriers are correctly
9 * implemented for nsTArrays that contain JavaScript Values.
12 #include "mozilla/UniquePtr.h"
14 #include "jsapi.h"
15 #include "nsTArray.h"
17 #include "gtest/gtest.h"
19 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty
20 #include "js/TracingAPI.h"
21 #include "js/HeapAPI.h"
23 #include "mozilla/CycleCollectedJSContext.h"
25 using namespace mozilla;
27 template <class ArrayT>
28 static void TraceArray(JSTracer* trc, void* data) {
29 ArrayT* array = static_cast<ArrayT*>(data);
30 for (unsigned i = 0; i < array->Length(); ++i) {
31 JS::TraceEdge(trc, &array->ElementAt(i), "array-element");
36 * Use arrays with initial size much smaller than the final number of elements
37 * to test that moving Heap<T> elements works correctly.
39 const size_t ElementCount = 100;
40 const size_t InitialElements = ElementCount / 10;
42 template <class ArrayT>
43 static void TestGrow(JSContext* cx) {
44 JS_GC(cx);
46 auto array = MakeUnique<ArrayT>();
47 ASSERT_TRUE(array != nullptr);
49 JS_AddExtraGCRootsTracer(cx, TraceArray<ArrayT>, array.get());
52 * Create the array and fill it with new JS objects. With GGC these will be
53 * allocated in the nursery.
55 JS::RootedValue value(cx);
56 const char* property = "foo";
57 for (size_t i = 0; i < ElementCount; ++i) {
58 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
59 ASSERT_FALSE(JS::ObjectIsTenured(obj));
60 value = JS::Int32Value(static_cast<int32_t>(i));
61 ASSERT_TRUE(JS_SetProperty(cx, obj, property, value));
62 ASSERT_TRUE(array->AppendElement(obj, fallible));
66 * If postbarriers are not working, we will crash here when we try to mark
67 * objects that have been moved to the tenured heap.
69 JS_GC(cx);
72 * Sanity check that our array contains what we expect.
74 ASSERT_EQ(array->Length(), ElementCount);
75 for (size_t i = 0; i < array->Length(); i++) {
76 JS::RootedObject obj(cx, array->ElementAt(i));
77 ASSERT_TRUE(JS::ObjectIsTenured(obj));
78 ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value));
79 ASSERT_TRUE(value.isInt32());
80 ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
83 JS_RemoveExtraGCRootsTracer(cx, TraceArray<ArrayT>, array.get());
86 template <class ArrayT>
87 static void TestShrink(JSContext* cx) {
88 JS_GC(cx);
90 auto array = MakeUnique<ArrayT>();
91 ASSERT_TRUE(array != nullptr);
93 JS_AddExtraGCRootsTracer(cx, TraceArray<ArrayT>, array.get());
96 * Create the array and fill it with new JS objects. With GGC these will be
97 * allocated in the nursery.
99 JS::RootedValue value(cx);
100 const char* property = "foo";
101 for (size_t i = 0; i < ElementCount; ++i) {
102 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
103 ASSERT_FALSE(JS::ObjectIsTenured(obj));
104 value = JS::Int32Value(static_cast<int32_t>(i));
105 ASSERT_TRUE(JS_SetProperty(cx, obj, property, value));
106 ASSERT_TRUE(array->AppendElement(obj, fallible));
109 /* Shrink and compact the array */
110 array->RemoveElementsAt(InitialElements, ElementCount - InitialElements);
111 array->Compact();
113 JS_GC(cx);
115 ASSERT_EQ(array->Length(), InitialElements);
116 for (size_t i = 0; i < array->Length(); i++) {
117 JS::RootedObject obj(cx, array->ElementAt(i));
118 ASSERT_TRUE(JS::ObjectIsTenured(obj));
119 ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value));
120 ASSERT_TRUE(value.isInt32());
121 ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
124 JS_RemoveExtraGCRootsTracer(cx, TraceArray<ArrayT>, array.get());
127 template <class ArrayT>
128 static void TestArrayType(JSContext* cx) {
129 TestGrow<ArrayT>(cx);
130 TestShrink<ArrayT>(cx);
133 static void CreateGlobalAndRunTest(JSContext* cx) {
134 static const JSClass GlobalClass = {"global", JSCLASS_GLOBAL_FLAGS,
135 &JS::DefaultGlobalClassOps};
137 JS::RealmOptions options;
138 // dummy
139 options.behaviors().setReduceTimerPrecisionCallerType(
140 JS::RTPCallerTypeToken{0});
141 JS::PersistentRootedObject global(cx);
142 global = JS_NewGlobalObject(cx, &GlobalClass, nullptr,
143 JS::FireOnNewGlobalHook, options);
144 ASSERT_TRUE(global != nullptr);
146 JS::Realm* oldRealm = JS::EnterRealm(cx, global);
148 using ElementT = JS::Heap<JSObject*>;
150 TestArrayType<nsTArray<ElementT>>(cx);
151 TestArrayType<FallibleTArray<ElementT>>(cx);
152 TestArrayType<AutoTArray<ElementT, 1>>(cx);
154 JS::LeaveRealm(cx, oldRealm);
157 TEST(GCPostBarriers, nsTArray)
159 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
160 ASSERT_TRUE(ccjscx != nullptr);
161 JSContext* cx = ccjscx->Context();
162 ASSERT_TRUE(cx != nullptr);
164 CreateGlobalAndRunTest(cx);