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/. */
8 * Tests that generational garbage collection post-barriers are correctly
9 * implemented for nsTArrays that contain JavaScript Values.
12 #include "mozilla/UniquePtr.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
) {
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.
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
) {
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
);
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
;
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
);