Source code

Revision control

Other Tools

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
*/
4
/* This Source Code Form is subject to the terms of the Mozilla Public
5
* License, v. 2.0. If a copy of the MPL was not distributed with this
6
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "mozilla/TypeTraits.h"
9
#include "mozilla/UniquePtr.h"
10
11
#include "js/ArrayBuffer.h" // JS::NewArrayBuffer
12
#include "js/RootingAPI.h"
13
#include "jsapi-tests/tests.h"
14
#include "vm/Runtime.h"
15
16
#include "vm/JSContext-inl.h"
17
18
// A heap-allocated structure containing one of our barriered pointer wrappers
19
// to test.
20
template <typename W>
21
struct TestStruct {
22
W wrapper;
23
};
24
25
// A specialized version for GCPtr that adds a zone() method.
26
template <typename T>
27
struct TestStruct<js::GCPtr<T>> {
28
js::GCPtr<T> wrapper;
29
30
void trace(JSTracer* trc) {
31
TraceNullableEdge(trc, &wrapper, "TestStruct::wrapper");
32
}
33
};
34
35
// Give the GCPtr version GCManagedDeletePolicy as required.
36
namespace JS {
37
template <typename T>
38
struct DeletePolicy<TestStruct<js::GCPtr<T>>>
39
: public js::GCManagedDeletePolicy<TestStruct<js::GCPtr<T>>> {};
40
} // namespace JS
41
42
template <typename T>
43
static T* CreateGCThing(JSContext* cx) {
44
MOZ_CRASH();
45
return nullptr;
46
}
47
48
template <>
49
JSObject* CreateGCThing(JSContext* cx) {
50
JS::RootedObject obj(cx, JS_NewPlainObject(cx));
51
if (!obj) {
52
return nullptr;
53
}
54
JS_DefineProperty(cx, obj, "x", 42, 0);
55
return obj;
56
}
57
58
template <>
59
JSFunction* CreateGCThing(JSContext* cx) {
60
/*
61
* We don't actually use the function as a function, so here we cheat and
62
* cast a JSObject.
63
*/
64
return static_cast<JSFunction*>(CreateGCThing<JSObject>(cx));
65
}
66
67
BEGIN_TEST(testGCHeapPostBarriers) {
68
#ifdef JS_GC_ZEAL
69
AutoLeaveZeal nozeal(cx);
70
#endif /* JS_GC_ZEAL */
71
72
/* Sanity check - objects start in the nursery and then become tenured. */
73
JS_GC(cx);
74
JS::RootedObject obj(cx, CreateGCThing<JSObject>(cx));
75
CHECK(js::gc::IsInsideNursery(obj.get()));
76
JS_GC(cx);
77
CHECK(!js::gc::IsInsideNursery(obj.get()));
78
JS::RootedObject tenuredObject(cx, obj);
79
80
/* Currently JSObject and JSFunction objects are nursery allocated. */
81
CHECK(TestHeapPostBarriersForType<JSObject>());
82
CHECK(TestHeapPostBarriersForType<JSFunction>());
83
84
return true;
85
}
86
87
bool CanAccessObject(JSObject* obj) {
88
JS::RootedObject rootedObj(cx, obj);
89
JS::RootedValue value(cx);
90
CHECK(JS_GetProperty(cx, rootedObj, "x", &value));
91
CHECK(value.isInt32());
92
CHECK(value.toInt32() == 42);
93
return true;
94
}
95
96
template <typename T>
97
bool TestHeapPostBarriersForType() {
98
CHECK((TestHeapPostBarriersForWrapper<T, JS::Heap<T*>>()));
99
CHECK((TestHeapPostBarriersForWrapper<T, js::GCPtr<T*>>()));
100
CHECK((TestHeapPostBarriersForWrapper<T, js::HeapPtr<T*>>()));
101
return true;
102
}
103
104
template <typename T, typename W>
105
bool TestHeapPostBarriersForWrapper() {
106
CHECK((TestHeapPostBarrierUpdate<T, W>()));
107
CHECK((TestHeapPostBarrierInitFailure<T, W>()));
108
return true;
109
}
110
111
template <typename T, typename W>
112
bool TestHeapPostBarrierUpdate() {
113
// Normal case - allocate a heap object, write a nursery pointer into it and
114
// check that it gets updated on minor GC.
115
116
T* initialObj = CreateGCThing<T>(cx);
117
CHECK(initialObj != nullptr);
118
CHECK(js::gc::IsInsideNursery(initialObj));
119
uintptr_t initialObjAsInt = uintptr_t(initialObj);
120
121
TestStruct<W>* ptr = nullptr;
122
123
{
124
auto testStruct = cx->make_unique<TestStruct<W>>();
125
CHECK(testStruct);
126
127
W& wrapper = testStruct->wrapper;
128
CHECK(wrapper.get() == nullptr);
129
wrapper = initialObj;
130
CHECK(wrapper == initialObj);
131
132
ptr = testStruct.release();
133
}
134
135
cx->minorGC(JS::GCReason::API);
136
137
W& wrapper = ptr->wrapper;
138
CHECK(uintptr_t(wrapper.get()) != initialObjAsInt);
139
CHECK(!js::gc::IsInsideNursery(wrapper.get()));
140
CHECK(CanAccessObject(wrapper.get()));
141
142
JS::DeletePolicy<TestStruct<W>>()(ptr);
143
144
cx->minorGC(JS::GCReason::API);
145
146
return true;
147
}
148
149
template <typename T, typename W>
150
bool TestHeapPostBarrierInitFailure() {
151
// Failure case - allocate a heap object, write a nursery pointer into it
152
// and fail to complete initialization.
153
154
T* initialObj = CreateGCThing<T>(cx);
155
CHECK(initialObj != nullptr);
156
CHECK(js::gc::IsInsideNursery(initialObj));
157
158
{
159
auto testStruct = cx->make_unique<TestStruct<W>>();
160
CHECK(testStruct);
161
162
W& wrapper = testStruct->wrapper;
163
CHECK(wrapper.get() == nullptr);
164
wrapper = initialObj;
165
CHECK(wrapper == initialObj);
166
167
// testStruct deleted here, as if we left this block due to an error.
168
}
169
170
cx->minorGC(JS::GCReason::API);
171
172
return true;
173
}
174
175
END_TEST(testGCHeapPostBarriers)
176
177
BEGIN_TEST(testUnbarrieredEquality) {
178
#ifdef JS_GC_ZEAL
179
AutoLeaveZeal nozeal(cx);
180
#endif /* JS_GC_ZEAL */
181
182
// Use ArrayBuffers because they have finalizers, which allows using them in
183
// TenuredHeap<> without awkward conversations about nursery allocatability.
184
JS::RootedObject robj(cx, JS::NewArrayBuffer(cx, 20));
185
JS::RootedObject robj2(cx, JS::NewArrayBuffer(cx, 30));
186
cx->runtime()->gc.evictNursery(); // Need tenured objects
187
188
// Need some bare pointers to compare against.
189
JSObject* obj = robj;
190
JSObject* obj2 = robj2;
191
const JSObject* constobj = robj;
192
const JSObject* constobj2 = robj2;
193
194
// Make them gray. We will make sure they stay gray. (For most reads, the
195
// barrier will unmark gray.)
196
using namespace js::gc;
197
TenuredCell* cell = &obj->asTenured();
198
TenuredCell* cell2 = &obj2->asTenured();
199
cell->markIfUnmarked(MarkColor::Gray);
200
cell2->markIfUnmarked(MarkColor::Gray);
201
MOZ_ASSERT(cell->isMarkedGray());
202
MOZ_ASSERT(cell2->isMarkedGray());
203
204
{
205
JS::Heap<JSObject*> heap(obj);
206
JS::Heap<JSObject*> heap2(obj2);
207
CHECK(TestWrapper(obj, obj2, heap, heap2));
208
CHECK(TestWrapper(constobj, constobj2, heap, heap2));
209
}
210
211
{
212
JS::TenuredHeap<JSObject*> heap(obj);
213
JS::TenuredHeap<JSObject*> heap2(obj2);
214
CHECK(TestWrapper(obj, obj2, heap, heap2));
215
CHECK(TestWrapper(constobj, constobj2, heap, heap2));
216
}
217
218
// Sanity check that the barriers normally mark things black.
219
{
220
JS::Heap<JSObject*> heap(obj);
221
JS::Heap<JSObject*> heap2(obj2);
222
heap.get();
223
heap2.get();
224
CHECK(cell->isMarkedBlack());
225
CHECK(cell2->isMarkedBlack());
226
}
227
228
return true;
229
}
230
231
template <typename ObjectT, typename WrapperT>
232
bool TestWrapper(ObjectT obj, ObjectT obj2, WrapperT& wrapper,
233
WrapperT& wrapper2) {
234
using namespace js::gc;
235
236
const TenuredCell& cell = obj->asTenured();
237
const TenuredCell& cell2 = obj2->asTenured();
238
239
int x = 0;
240
241
CHECK(cell.isMarkedGray());
242
CHECK(cell2.isMarkedGray());
243
x += obj == obj2;
244
CHECK(cell.isMarkedGray());
245
CHECK(cell2.isMarkedGray());
246
x += obj == wrapper2;
247
CHECK(cell.isMarkedGray());
248
CHECK(cell2.isMarkedGray());
249
x += wrapper == obj2;
250
CHECK(cell.isMarkedGray());
251
CHECK(cell2.isMarkedGray());
252
x += wrapper == wrapper2;
253
CHECK(cell.isMarkedGray());
254
CHECK(cell2.isMarkedGray());
255
256
CHECK(x == 0);
257
258
x += obj != obj2;
259
CHECK(cell.isMarkedGray());
260
CHECK(cell2.isMarkedGray());
261
x += obj != wrapper2;
262
CHECK(cell.isMarkedGray());
263
CHECK(cell2.isMarkedGray());
264
x += wrapper != obj2;
265
CHECK(cell.isMarkedGray());
266
CHECK(cell2.isMarkedGray());
267
x += wrapper != wrapper2;
268
CHECK(cell.isMarkedGray());
269
CHECK(cell2.isMarkedGray());
270
271
CHECK(x == 4);
272
273
return true;
274
}
275
276
END_TEST(testUnbarrieredEquality)