Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / jsapi-tests / testGCGrayMarking.cpp
blob1ae1683a63c228e85cc1e523ae2af991b9fbdbec
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/. */
8 #include <algorithm>
10 #include "gc/WeakMap.h"
11 #include "gc/Zone.h"
12 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById
13 #include "js/Proxy.h"
14 #include "js/WeakMap.h"
15 #include "jsapi-tests/tests.h"
17 using namespace js;
18 using namespace js::gc;
20 static constexpr CellColor AllCellColors[] = {CellColor::White, CellColor::Gray,
21 CellColor::Black};
23 static constexpr CellColor MarkedCellColors[] = {CellColor::Gray,
24 CellColor::Black};
26 namespace js {
28 struct GCManagedObjectWeakMap : public ObjectWeakMap {
29 using ObjectWeakMap::ObjectWeakMap;
32 } // namespace js
34 namespace JS {
36 template <>
37 struct MapTypeToRootKind<js::GCManagedObjectWeakMap*> {
38 static const JS::RootKind kind = JS::RootKind::Traceable;
41 template <>
42 struct GCPolicy<js::GCManagedObjectWeakMap*>
43 : public NonGCPointerPolicy<js::GCManagedObjectWeakMap*> {};
45 } // namespace JS
47 class AutoNoAnalysisForTest {
48 public:
49 AutoNoAnalysisForTest() {}
50 } JS_HAZ_GC_SUPPRESSED;
52 BEGIN_TEST(testGCGrayMarking) {
53 AutoNoAnalysisForTest disableAnalysis;
54 AutoDisableCompactingGC disableCompactingGC(cx);
55 AutoLeaveZeal nozeal(cx);
57 CHECK(InitGlobals());
58 JSAutoRealm ar(cx, global1);
60 InitGrayRootTracer();
62 // Enable incremental GC.
63 AutoGCParameter param1(cx, JSGC_INCREMENTAL_GC_ENABLED, true);
64 AutoGCParameter param2(cx, JSGC_PER_ZONE_GC_ENABLED, true);
66 bool ok = TestMarking() && TestJSWeakMaps() && TestInternalWeakMaps() &&
67 TestCCWs() && TestGrayUnmarking();
69 global1 = nullptr;
70 global2 = nullptr;
71 RemoveGrayRootTracer();
73 return ok;
76 bool TestMarking() {
77 JSObject* sameTarget = AllocPlainObject();
78 CHECK(sameTarget);
80 JSObject* sameSource = AllocSameCompartmentSourceObject(sameTarget);
81 CHECK(sameSource);
83 JSObject* crossTarget = AllocPlainObject();
84 CHECK(crossTarget);
86 JSObject* crossSource = GetCrossCompartmentWrapper(crossTarget);
87 CHECK(crossSource);
89 // Test GC with black roots marks objects black.
91 JS::RootedObject blackRoot1(cx, sameSource);
92 JS::RootedObject blackRoot2(cx, crossSource);
94 JS_GC(cx);
96 CHECK(IsMarkedBlack(sameSource));
97 CHECK(IsMarkedBlack(crossSource));
98 CHECK(IsMarkedBlack(sameTarget));
99 CHECK(IsMarkedBlack(crossTarget));
101 // Test GC with black and gray roots marks objects black.
103 grayRoots.grayRoot1 = sameSource;
104 grayRoots.grayRoot2 = crossSource;
106 JS_GC(cx);
108 CHECK(IsMarkedBlack(sameSource));
109 CHECK(IsMarkedBlack(crossSource));
110 CHECK(IsMarkedBlack(sameTarget));
111 CHECK(IsMarkedBlack(crossTarget));
113 CHECK(!JS::ObjectIsMarkedGray(sameSource));
115 // Test GC with gray roots marks object gray.
117 blackRoot1 = nullptr;
118 blackRoot2 = nullptr;
120 JS_GC(cx);
122 CHECK(IsMarkedGray(sameSource));
123 CHECK(IsMarkedGray(crossSource));
124 CHECK(IsMarkedGray(sameTarget));
125 CHECK(IsMarkedGray(crossTarget));
127 CHECK(JS::ObjectIsMarkedGray(sameSource));
129 // Test ExposeToActiveJS marks gray objects black.
131 JS::ExposeObjectToActiveJS(sameSource);
132 JS::ExposeObjectToActiveJS(crossSource);
133 CHECK(IsMarkedBlack(sameSource));
134 CHECK(IsMarkedBlack(crossSource));
135 CHECK(IsMarkedBlack(sameTarget));
136 CHECK(IsMarkedBlack(crossTarget));
138 // Test a zone GC with black roots marks gray object in other zone black.
140 JS_GC(cx);
142 CHECK(IsMarkedGray(crossSource));
143 CHECK(IsMarkedGray(crossTarget));
145 blackRoot1 = crossSource;
146 CHECK(ZoneGC(crossSource->zone()));
148 CHECK(IsMarkedBlack(crossSource));
149 CHECK(IsMarkedBlack(crossTarget));
151 blackRoot1 = nullptr;
152 blackRoot2 = nullptr;
153 grayRoots.grayRoot1 = nullptr;
154 grayRoots.grayRoot2 = nullptr;
156 return true;
159 static constexpr CellColor DontMark = CellColor::White;
161 enum MarkKeyOrDelegate : bool { MarkKey = true, MarkDelegate = false };
163 bool TestJSWeakMaps() {
164 for (auto keyOrDelegateColor : MarkedCellColors) {
165 for (auto mapColor : MarkedCellColors) {
166 for (auto markKeyOrDelegate : {MarkKey, MarkDelegate}) {
167 CellColor expected = std::min(keyOrDelegateColor, mapColor);
168 CHECK(TestJSWeakMap(markKeyOrDelegate, keyOrDelegateColor, mapColor,
169 expected));
170 #ifdef JS_GC_ZEAL
171 CHECK(TestJSWeakMapWithGrayUnmarking(
172 markKeyOrDelegate, keyOrDelegateColor, mapColor, expected));
173 #endif
178 return true;
181 bool TestInternalWeakMaps() {
182 for (auto keyMarkColor : AllCellColors) {
183 for (auto delegateMarkColor : AllCellColors) {
184 if (keyMarkColor == CellColor::White &&
185 delegateMarkColor == CellColor::White) {
186 continue;
189 // The map is black. The delegate marks its key via wrapper preservation.
190 // The key maps its delegate and the value. Thus, all three end up the
191 // maximum of the key and delegate colors.
192 CellColor expected = std::max(keyMarkColor, delegateMarkColor);
193 CHECK(TestInternalWeakMap(keyMarkColor, delegateMarkColor, expected));
195 #ifdef JS_GC_ZEAL
196 CHECK(TestInternalWeakMapWithGrayUnmarking(keyMarkColor,
197 delegateMarkColor, expected));
198 #endif
202 return true;
205 bool TestJSWeakMap(MarkKeyOrDelegate markKey, CellColor weakMapMarkColor,
206 CellColor keyOrDelegateMarkColor,
207 CellColor expectedValueColor) {
208 using std::swap;
210 // Test marking a JS WeakMap object.
212 // This marks the map and one of the key or delegate. The key/delegate and the
213 // value can end up different colors depending on the color of the map.
215 JSObject* weakMap;
216 JSObject* key;
217 JSObject* value;
219 // If both map and key are marked the same color, test both possible
220 // orderings.
221 unsigned markOrderings = weakMapMarkColor == keyOrDelegateMarkColor ? 2 : 1;
223 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) {
224 CHECK(CreateJSWeakMapObjects(&weakMap, &key, &value));
226 JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
227 JSObject* keyOrDelegate = markKey ? key : delegate;
229 RootedObject blackRoot1(cx);
230 RootedObject blackRoot2(cx);
232 RootObject(weakMap, weakMapMarkColor, blackRoot1, grayRoots.grayRoot1);
233 RootObject(keyOrDelegate, keyOrDelegateMarkColor, blackRoot2,
234 grayRoots.grayRoot2);
236 if (markOrder != 0) {
237 swap(blackRoot1.get(), blackRoot2.get());
238 swap(grayRoots.grayRoot1, grayRoots.grayRoot2);
241 JS_GC(cx);
243 ClearGrayRoots();
245 CHECK(weakMap->color() == weakMapMarkColor);
246 CHECK(keyOrDelegate->color() == keyOrDelegateMarkColor);
247 CHECK(value->color() == expectedValueColor);
250 return true;
253 #ifdef JS_GC_ZEAL
255 bool TestJSWeakMapWithGrayUnmarking(MarkKeyOrDelegate markKey,
256 CellColor weakMapMarkColor,
257 CellColor keyOrDelegateMarkColor,
258 CellColor expectedValueColor) {
259 // This is like the previous test, but things are marked black by gray
260 // unmarking during incremental GC.
262 JSObject* weakMap;
263 JSObject* key;
264 JSObject* value;
266 // If both map and key are marked the same color, test both possible
267 // orderings.
268 unsigned markOrderings = weakMapMarkColor == keyOrDelegateMarkColor ? 2 : 1;
270 JS_SetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking), 0);
272 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) {
273 CHECK(CreateJSWeakMapObjects(&weakMap, &key, &value));
275 JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
276 JSObject* keyOrDelegate = markKey ? key : delegate;
278 grayRoots.grayRoot1 = keyOrDelegate;
279 grayRoots.grayRoot2 = weakMap;
281 // Start an incremental GC and run until gray roots have been pushed onto
282 // the mark stack.
283 JS::PrepareForFullGC(cx);
284 js::SliceBudget budget(TimeBudget(1000000));
285 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::DEBUG_GC,
286 budget);
287 MOZ_ASSERT(cx->runtime()->gc.state() == gc::State::Sweep);
288 MOZ_ASSERT(cx->zone()->gcState() == Zone::MarkBlackAndGray);
290 // Unmark gray things as specified.
291 if (markOrder != 0) {
292 MaybeExposeObject(weakMap, weakMapMarkColor);
293 MaybeExposeObject(keyOrDelegate, keyOrDelegateMarkColor);
294 } else {
295 MaybeExposeObject(keyOrDelegate, keyOrDelegateMarkColor);
296 MaybeExposeObject(weakMap, weakMapMarkColor);
299 JS::FinishIncrementalGC(cx, JS::GCReason::API);
301 ClearGrayRoots();
303 CHECK(weakMap->color() == weakMapMarkColor);
304 CHECK(keyOrDelegate->color() == keyOrDelegateMarkColor);
305 CHECK(value->color() == expectedValueColor);
308 JS_UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking));
310 return true;
313 static void MaybeExposeObject(JSObject* object, CellColor color) {
314 if (color == CellColor::Black) {
315 JS::ExposeObjectToActiveJS(object);
319 #endif // JS_GC_ZEAL
321 bool CreateJSWeakMapObjects(JSObject** weakMapOut, JSObject** keyOut,
322 JSObject** valueOut) {
323 RootedObject key(cx, AllocWeakmapKeyObject());
324 CHECK(key);
326 RootedObject value(cx, AllocPlainObject());
327 CHECK(value);
329 RootedObject weakMap(cx, JS::NewWeakMapObject(cx));
330 CHECK(weakMap);
332 JS::RootedValue keyValue(cx, ObjectValue(*key));
333 JS::RootedValue valueValue(cx, ObjectValue(*value));
334 CHECK(SetWeakMapEntry(cx, weakMap, keyValue, valueValue));
336 *weakMapOut = weakMap;
337 *keyOut = key;
338 *valueOut = value;
339 return true;
342 bool TestInternalWeakMap(CellColor keyMarkColor, CellColor delegateMarkColor,
343 CellColor expectedColor) {
344 using std::swap;
346 // Test marking for internal weakmaps (without an owning JSObject).
348 // All of the key, delegate and value are expected to end up the same color.
350 UniquePtr<GCManagedObjectWeakMap> weakMap;
351 JSObject* key;
352 JSObject* value;
354 // If both key and delegate are marked the same color, test both possible
355 // orderings.
356 unsigned markOrderings = keyMarkColor == delegateMarkColor ? 2 : 1;
358 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) {
359 CHECK(CreateInternalWeakMapObjects(&weakMap, &key, &value));
361 JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
363 RootedObject blackRoot1(cx);
364 RootedObject blackRoot2(cx);
366 Rooted<GCManagedObjectWeakMap*> rootMap(cx, weakMap.get());
367 RootObject(key, keyMarkColor, blackRoot1, grayRoots.grayRoot1);
368 RootObject(delegate, delegateMarkColor, blackRoot2, grayRoots.grayRoot2);
370 if (markOrder != 0) {
371 swap(blackRoot1.get(), blackRoot2.get());
372 swap(grayRoots.grayRoot1, grayRoots.grayRoot2);
375 JS_GC(cx);
377 ClearGrayRoots();
379 CHECK(key->color() == expectedColor);
380 CHECK(delegate->color() == expectedColor);
381 CHECK(value->color() == expectedColor);
384 return true;
387 #ifdef JS_GC_ZEAL
389 bool TestInternalWeakMapWithGrayUnmarking(CellColor keyMarkColor,
390 CellColor delegateMarkColor,
391 CellColor expectedColor) {
392 UniquePtr<GCManagedObjectWeakMap> weakMap;
393 JSObject* key;
394 JSObject* value;
396 // If both key and delegate are marked the same color, test both possible
397 // orderings.
398 unsigned markOrderings = keyMarkColor == delegateMarkColor ? 2 : 1;
400 JS_SetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking), 0);
402 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) {
403 CHECK(CreateInternalWeakMapObjects(&weakMap, &key, &value));
405 JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
407 Rooted<GCManagedObjectWeakMap*> rootMap(cx, weakMap.get());
408 grayRoots.grayRoot1 = key;
409 grayRoots.grayRoot2 = delegate;
411 // Start an incremental GC and run until gray roots have been pushed onto
412 // the mark stack.
413 JS::PrepareForFullGC(cx);
414 js::SliceBudget budget(TimeBudget(1000000));
415 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::DEBUG_GC,
416 budget);
417 MOZ_ASSERT(cx->runtime()->gc.state() == gc::State::Sweep);
418 MOZ_ASSERT(cx->zone()->gcState() == Zone::MarkBlackAndGray);
420 // Unmark gray things as specified.
421 if (markOrder != 0) {
422 MaybeExposeObject(key, keyMarkColor);
423 MaybeExposeObject(delegate, delegateMarkColor);
424 } else {
425 MaybeExposeObject(key, keyMarkColor);
426 MaybeExposeObject(delegate, delegateMarkColor);
429 JS::FinishIncrementalGC(cx, JS::GCReason::API);
431 ClearGrayRoots();
433 CHECK(key->color() == expectedColor);
434 CHECK(delegate->color() == expectedColor);
435 CHECK(value->color() == expectedColor);
438 JS_UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking));
440 return true;
443 #endif // JS_GC_ZEAL
445 bool CreateInternalWeakMapObjects(UniquePtr<GCManagedObjectWeakMap>* weakMapOut,
446 JSObject** keyOut, JSObject** valueOut) {
447 RootedObject key(cx, AllocWeakmapKeyObject());
448 CHECK(key);
450 RootedObject value(cx, AllocPlainObject());
451 CHECK(value);
453 auto weakMap = cx->make_unique<GCManagedObjectWeakMap>(cx);
454 CHECK(weakMap);
456 CHECK(weakMap->add(cx, key, value));
458 *weakMapOut = std::move(weakMap);
459 *keyOut = key;
460 *valueOut = value;
461 return true;
464 void RootObject(JSObject* object, CellColor color, RootedObject& blackRoot,
465 JS::Heap<JSObject*>& grayRoot) {
466 if (color == CellColor::Black) {
467 blackRoot = object;
468 } else if (color == CellColor::Gray) {
469 grayRoot = object;
470 } else {
471 MOZ_RELEASE_ASSERT(color == CellColor::White);
475 bool TestCCWs() {
476 JSObject* target = AllocPlainObject();
477 CHECK(target);
479 // Test getting a new wrapper doesn't return a gray wrapper.
481 RootedObject blackRoot(cx, target);
482 JSObject* wrapper = GetCrossCompartmentWrapper(target);
483 CHECK(wrapper);
484 CHECK(!IsMarkedGray(wrapper));
486 // Test getting an existing wrapper doesn't return a gray wrapper.
488 grayRoots.grayRoot1 = wrapper;
489 grayRoots.grayRoot2 = nullptr;
490 JS_GC(cx);
491 CHECK(IsMarkedGray(wrapper));
492 CHECK(IsMarkedBlack(target));
494 CHECK(GetCrossCompartmentWrapper(target) == wrapper);
495 CHECK(!IsMarkedGray(wrapper));
497 // Test getting an existing wrapper doesn't return a gray wrapper
498 // during incremental GC.
500 JS_GC(cx);
501 CHECK(IsMarkedGray(wrapper));
502 CHECK(IsMarkedBlack(target));
504 JSRuntime* rt = cx->runtime();
505 JS::PrepareForFullGC(cx);
506 js::SliceBudget budget(js::WorkBudget(1));
507 rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
508 while (rt->gc.state() == gc::State::Prepare) {
509 rt->gc.debugGCSlice(budget);
511 CHECK(JS::IsIncrementalGCInProgress(cx));
513 CHECK(!IsMarkedBlack(wrapper));
514 CHECK(wrapper->zone()->isGCMarkingBlackOnly());
516 CHECK(GetCrossCompartmentWrapper(target) == wrapper);
517 CHECK(IsMarkedBlack(wrapper));
519 JS::FinishIncrementalGC(cx, JS::GCReason::API);
521 // Test behaviour of gray CCWs marked black by a barrier during incremental
522 // GC.
524 // Initial state: source and target are gray.
525 blackRoot = nullptr;
526 grayRoots.grayRoot1 = wrapper;
527 grayRoots.grayRoot2 = nullptr;
528 JS_GC(cx);
529 CHECK(IsMarkedGray(wrapper));
530 CHECK(IsMarkedGray(target));
532 // Incremental zone GC started: the source is now unmarked.
533 JS::PrepareZoneForGC(cx, wrapper->zone());
534 budget = js::SliceBudget(js::WorkBudget(1));
535 rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
536 while (rt->gc.state() == gc::State::Prepare) {
537 rt->gc.debugGCSlice(budget);
539 CHECK(JS::IsIncrementalGCInProgress(cx));
540 CHECK(wrapper->zone()->isGCMarkingBlackOnly());
541 CHECK(!target->zone()->wasGCStarted());
542 CHECK(!IsMarkedBlack(wrapper));
543 CHECK(!IsMarkedGray(wrapper));
544 CHECK(IsMarkedGray(target));
546 // Betweeen GC slices: source marked black by barrier, target is
547 // still gray. Target will be marked gray
548 // eventually. ObjectIsMarkedGray() is conservative and reports
549 // that target is not marked gray; AssertObjectIsNotGray() will
550 // assert.
551 grayRoots.grayRoot1.get();
552 CHECK(IsMarkedBlack(wrapper));
553 CHECK(IsMarkedGray(target));
554 CHECK(!JS::ObjectIsMarkedGray(target));
556 // Final state: source and target are black.
557 JS::FinishIncrementalGC(cx, JS::GCReason::API);
558 CHECK(IsMarkedBlack(wrapper));
559 CHECK(IsMarkedBlack(target));
561 grayRoots.grayRoot1 = nullptr;
562 grayRoots.grayRoot2 = nullptr;
564 return true;
567 bool TestGrayUnmarking() {
568 const size_t length = 2000;
570 JSObject* chain = AllocObjectChain(length);
571 CHECK(chain);
573 RootedObject blackRoot(cx, chain);
574 JS_GC(cx);
575 size_t count;
576 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count)));
577 CHECK(count == length);
579 blackRoot = nullptr;
580 grayRoots.grayRoot1 = chain;
581 JS_GC(cx);
582 CHECK(cx->runtime()->gc.areGrayBitsValid());
583 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Gray, &count)));
584 CHECK(count == length);
586 JS::ExposeObjectToActiveJS(chain);
587 CHECK(cx->runtime()->gc.areGrayBitsValid());
588 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count)));
589 CHECK(count == length);
591 grayRoots.grayRoot1 = nullptr;
593 return true;
596 struct ColorCheckFunctor {
597 MarkColor color;
598 size_t& count;
600 ColorCheckFunctor(MarkColor colorArg, size_t* countArg)
601 : color(colorArg), count(*countArg) {
602 count = 0;
605 bool operator()(JSObject* obj) {
606 if (!CheckCellColor(obj, color)) {
607 return false;
610 NativeObject& nobj = obj->as<NativeObject>();
611 if (!CheckCellColor(nobj.shape(), color)) {
612 return false;
615 NativeShape* shape = nobj.shape();
616 if (!CheckCellColor(shape, color)) {
617 return false;
620 // Shapes and symbols are never marked gray.
621 ShapePropertyIter<NoGC> iter(shape);
622 jsid id = iter->key();
623 if (id.isGCThing() &&
624 !CheckCellColor(id.toGCCellPtr().asCell(), MarkColor::Black)) {
625 return false;
628 count++;
629 return true;
633 JS::PersistentRootedObject global1;
634 JS::PersistentRootedObject global2;
636 struct GrayRoots {
637 JS::Heap<JSObject*> grayRoot1;
638 JS::Heap<JSObject*> grayRoot2;
641 GrayRoots grayRoots;
643 bool InitGlobals() {
644 global1.init(cx, global);
645 if (!createGlobal()) {
646 return false;
648 global2.init(cx, global);
649 return global2 != nullptr;
652 void ClearGrayRoots() {
653 grayRoots.grayRoot1 = nullptr;
654 grayRoots.grayRoot2 = nullptr;
657 void InitGrayRootTracer() {
658 ClearGrayRoots();
659 JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, &grayRoots);
662 void RemoveGrayRootTracer() {
663 ClearGrayRoots();
664 JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
667 static bool TraceGrayRoots(JSTracer* trc, SliceBudget& budget, void* data) {
668 auto grayRoots = static_cast<GrayRoots*>(data);
669 TraceEdge(trc, &grayRoots->grayRoot1, "gray root 1");
670 TraceEdge(trc, &grayRoots->grayRoot2, "gray root 2");
671 return true;
674 JSObject* AllocPlainObject() {
675 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
676 EvictNursery();
678 MOZ_ASSERT(obj->compartment() == global1->compartment());
679 return obj;
682 JSObject* AllocSameCompartmentSourceObject(JSObject* target) {
683 JS::RootedObject source(cx, JS_NewPlainObject(cx));
684 if (!source) {
685 return nullptr;
688 JS::RootedObject obj(cx, target);
689 if (!JS_DefineProperty(cx, source, "ptr", obj, 0)) {
690 return nullptr;
693 EvictNursery();
695 MOZ_ASSERT(source->compartment() == global1->compartment());
696 return source;
699 JSObject* GetCrossCompartmentWrapper(JSObject* target) {
700 MOZ_ASSERT(target->compartment() == global1->compartment());
701 JS::RootedObject obj(cx, target);
702 JSAutoRealm ar(cx, global2);
703 if (!JS_WrapObject(cx, &obj)) {
704 return nullptr;
707 EvictNursery();
709 MOZ_ASSERT(obj->compartment() == global2->compartment());
710 return obj;
713 JSObject* AllocWeakmapKeyObject() {
714 JS::RootedObject delegate(cx, JS_NewPlainObject(cx));
715 if (!delegate) {
716 return nullptr;
719 JS::RootedObject key(cx,
720 js::Wrapper::New(cx, delegate, &js::Wrapper::singleton));
722 EvictNursery();
723 return key;
726 JSObject* AllocObjectChain(size_t length) {
727 // Allocate a chain of linked JSObjects.
729 // Use a unique property name so the shape is not shared with any other
730 // objects.
731 RootedString nextPropName(cx, JS_NewStringCopyZ(cx, "unique14142135"));
732 RootedId nextId(cx);
733 if (!JS_StringToId(cx, nextPropName, &nextId)) {
734 return nullptr;
737 RootedObject head(cx);
738 for (size_t i = 0; i < length; i++) {
739 RootedValue next(cx, ObjectOrNullValue(head));
740 head = AllocPlainObject();
741 if (!head) {
742 return nullptr;
744 if (!JS_DefinePropertyById(cx, head, nextId, next, 0)) {
745 return nullptr;
749 return head;
752 template <typename F>
753 bool IterateObjectChain(JSObject* chain, F f) {
754 RootedObject obj(cx, chain);
755 while (obj) {
756 if (!f(obj)) {
757 return false;
760 // Access the 'next' property via the object's slots to avoid triggering
761 // gray marking assertions when calling JS_GetPropertyById.
762 NativeObject& nobj = obj->as<NativeObject>();
763 MOZ_ASSERT(nobj.slotSpan() == 1);
764 obj = nobj.getSlot(0).toObjectOrNull();
767 return true;
770 static bool IsMarkedBlack(Cell* cell) {
771 TenuredCell* tc = &cell->asTenured();
772 return tc->isMarkedBlack();
775 static bool IsMarkedGray(Cell* cell) {
776 TenuredCell* tc = &cell->asTenured();
777 bool isGray = tc->isMarkedGray();
778 MOZ_ASSERT_IF(isGray, tc->isMarkedAny());
779 return isGray;
782 static bool CheckCellColor(Cell* cell, MarkColor color) {
783 MOZ_ASSERT(color == MarkColor::Black || color == MarkColor::Gray);
784 if (color == MarkColor::Black && !IsMarkedBlack(cell)) {
785 printf("Found non-black cell: %p\n", cell);
786 return false;
787 } else if (color == MarkColor::Gray && !IsMarkedGray(cell)) {
788 printf("Found non-gray cell: %p\n", cell);
789 return false;
792 return true;
795 void EvictNursery() { cx->runtime()->gc.evictNursery(); }
797 bool ZoneGC(JS::Zone* zone) {
798 JS::PrepareZoneForGC(cx, zone);
799 cx->runtime()->gc.gc(JS::GCOptions::Normal, JS::GCReason::API);
800 CHECK(!cx->runtime()->gc.isFullGc());
801 return true;
804 END_TEST(testGCGrayMarking)