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:
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/. */
10 #include "gc/WeakMap.h"
12 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById
14 #include "js/WeakMap.h"
15 #include "jsapi-tests/tests.h"
18 using namespace js::gc
;
20 static constexpr CellColor AllCellColors
[] = {CellColor::White
, CellColor::Gray
,
23 static constexpr CellColor MarkedCellColors
[] = {CellColor::Gray
,
28 struct GCManagedObjectWeakMap
: public ObjectWeakMap
{
29 using ObjectWeakMap::ObjectWeakMap
;
37 struct MapTypeToRootKind
<js::GCManagedObjectWeakMap
*> {
38 static const JS::RootKind kind
= JS::RootKind::Traceable
;
42 struct GCPolicy
<js::GCManagedObjectWeakMap
*>
43 : public NonGCPointerPolicy
<js::GCManagedObjectWeakMap
*> {};
47 class AutoNoAnalysisForTest
{
49 AutoNoAnalysisForTest() {}
50 } JS_HAZ_GC_SUPPRESSED
;
52 BEGIN_TEST(testGCGrayMarking
) {
53 AutoNoAnalysisForTest disableAnalysis
;
54 AutoDisableCompactingGC
disableCompactingGC(cx
);
55 AutoLeaveZeal
nozeal(cx
);
58 JSAutoRealm
ar(cx
, global1
);
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();
71 RemoveGrayRootTracer();
77 JSObject
* sameTarget
= AllocPlainObject();
80 JSObject
* sameSource
= AllocSameCompartmentSourceObject(sameTarget
);
83 JSObject
* crossTarget
= AllocPlainObject();
86 JSObject
* crossSource
= GetCrossCompartmentWrapper(crossTarget
);
89 // Test GC with black roots marks objects black.
91 JS::RootedObject
blackRoot1(cx
, sameSource
);
92 JS::RootedObject
blackRoot2(cx
, crossSource
);
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
;
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;
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.
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;
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
,
171 CHECK(TestJSWeakMapWithGrayUnmarking(
172 markKeyOrDelegate
, keyOrDelegateColor
, mapColor
, expected
));
181 bool TestInternalWeakMaps() {
182 for (auto keyMarkColor
: AllCellColors
) {
183 for (auto delegateMarkColor
: AllCellColors
) {
184 if (keyMarkColor
== CellColor::White
&&
185 delegateMarkColor
== CellColor::White
) {
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
));
196 CHECK(TestInternalWeakMapWithGrayUnmarking(keyMarkColor
,
197 delegateMarkColor
, expected
));
205 bool TestJSWeakMap(MarkKeyOrDelegate markKey
, CellColor weakMapMarkColor
,
206 CellColor keyOrDelegateMarkColor
,
207 CellColor expectedValueColor
) {
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.
219 // If both map and key are marked the same color, test both possible
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
);
245 CHECK(weakMap
->color() == weakMapMarkColor
);
246 CHECK(keyOrDelegate
->color() == keyOrDelegateMarkColor
);
247 CHECK(value
->color() == expectedValueColor
);
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.
266 // If both map and key are marked the same color, test both possible
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
283 JS::PrepareForFullGC(cx
);
284 js::SliceBudget
budget(TimeBudget(1000000));
285 JS::StartIncrementalGC(cx
, JS::GCOptions::Normal
, JS::GCReason::DEBUG_GC
,
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
);
295 MaybeExposeObject(keyOrDelegate
, keyOrDelegateMarkColor
);
296 MaybeExposeObject(weakMap
, weakMapMarkColor
);
299 JS::FinishIncrementalGC(cx
, JS::GCReason::API
);
303 CHECK(weakMap
->color() == weakMapMarkColor
);
304 CHECK(keyOrDelegate
->color() == keyOrDelegateMarkColor
);
305 CHECK(value
->color() == expectedValueColor
);
308 JS_UnsetGCZeal(cx
, uint8_t(ZealMode::YieldWhileGrayMarking
));
313 static void MaybeExposeObject(JSObject
* object
, CellColor color
) {
314 if (color
== CellColor::Black
) {
315 JS::ExposeObjectToActiveJS(object
);
321 bool CreateJSWeakMapObjects(JSObject
** weakMapOut
, JSObject
** keyOut
,
322 JSObject
** valueOut
) {
323 RootedObject
key(cx
, AllocWeakmapKeyObject());
326 RootedObject
value(cx
, AllocPlainObject());
329 RootedObject
weakMap(cx
, JS::NewWeakMapObject(cx
));
332 JS::RootedValue
keyValue(cx
, ObjectValue(*key
));
333 JS::RootedValue
valueValue(cx
, ObjectValue(*value
));
334 CHECK(SetWeakMapEntry(cx
, weakMap
, keyValue
, valueValue
));
336 *weakMapOut
= weakMap
;
342 bool TestInternalWeakMap(CellColor keyMarkColor
, CellColor delegateMarkColor
,
343 CellColor expectedColor
) {
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
;
354 // If both key and delegate are marked the same color, test both possible
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
);
379 CHECK(key
->color() == expectedColor
);
380 CHECK(delegate
->color() == expectedColor
);
381 CHECK(value
->color() == expectedColor
);
389 bool TestInternalWeakMapWithGrayUnmarking(CellColor keyMarkColor
,
390 CellColor delegateMarkColor
,
391 CellColor expectedColor
) {
392 UniquePtr
<GCManagedObjectWeakMap
> weakMap
;
396 // If both key and delegate are marked the same color, test both possible
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
413 JS::PrepareForFullGC(cx
);
414 js::SliceBudget
budget(TimeBudget(1000000));
415 JS::StartIncrementalGC(cx
, JS::GCOptions::Normal
, JS::GCReason::DEBUG_GC
,
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
);
425 MaybeExposeObject(key
, keyMarkColor
);
426 MaybeExposeObject(delegate
, delegateMarkColor
);
429 JS::FinishIncrementalGC(cx
, JS::GCReason::API
);
433 CHECK(key
->color() == expectedColor
);
434 CHECK(delegate
->color() == expectedColor
);
435 CHECK(value
->color() == expectedColor
);
438 JS_UnsetGCZeal(cx
, uint8_t(ZealMode::YieldWhileGrayMarking
));
445 bool CreateInternalWeakMapObjects(UniquePtr
<GCManagedObjectWeakMap
>* weakMapOut
,
446 JSObject
** keyOut
, JSObject
** valueOut
) {
447 RootedObject
key(cx
, AllocWeakmapKeyObject());
450 RootedObject
value(cx
, AllocPlainObject());
453 auto weakMap
= cx
->make_unique
<GCManagedObjectWeakMap
>(cx
);
456 CHECK(weakMap
->add(cx
, key
, value
));
458 *weakMapOut
= std::move(weakMap
);
464 void RootObject(JSObject
* object
, CellColor color
, RootedObject
& blackRoot
,
465 JS::Heap
<JSObject
*>& grayRoot
) {
466 if (color
== CellColor::Black
) {
468 } else if (color
== CellColor::Gray
) {
471 MOZ_RELEASE_ASSERT(color
== CellColor::White
);
476 JSObject
* target
= AllocPlainObject();
479 // Test getting a new wrapper doesn't return a gray wrapper.
481 RootedObject
blackRoot(cx
, target
);
482 JSObject
* wrapper
= GetCrossCompartmentWrapper(target
);
484 CHECK(!IsMarkedGray(wrapper
));
486 // Test getting an existing wrapper doesn't return a gray wrapper.
488 grayRoots
.grayRoot1
= wrapper
;
489 grayRoots
.grayRoot2
= nullptr;
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.
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
524 // Initial state: source and target are gray.
526 grayRoots
.grayRoot1
= wrapper
;
527 grayRoots
.grayRoot2
= nullptr;
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
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;
567 bool TestGrayUnmarking() {
568 const size_t length
= 2000;
570 JSObject
* chain
= AllocObjectChain(length
);
573 RootedObject
blackRoot(cx
, chain
);
576 CHECK(IterateObjectChain(chain
, ColorCheckFunctor(MarkColor::Black
, &count
)));
577 CHECK(count
== length
);
580 grayRoots
.grayRoot1
= chain
;
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;
596 struct ColorCheckFunctor
{
600 ColorCheckFunctor(MarkColor colorArg
, size_t* countArg
)
601 : color(colorArg
), count(*countArg
) {
605 bool operator()(JSObject
* obj
) {
606 if (!CheckCellColor(obj
, color
)) {
610 NativeObject
& nobj
= obj
->as
<NativeObject
>();
611 if (!CheckCellColor(nobj
.shape(), color
)) {
615 NativeShape
* shape
= nobj
.shape();
616 if (!CheckCellColor(shape
, color
)) {
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
)) {
633 JS::PersistentRootedObject global1
;
634 JS::PersistentRootedObject global2
;
637 JS::Heap
<JSObject
*> grayRoot1
;
638 JS::Heap
<JSObject
*> grayRoot2
;
644 global1
.init(cx
, global
);
645 if (!createGlobal()) {
648 global2
.init(cx
, global
);
649 return global2
!= nullptr;
652 void ClearGrayRoots() {
653 grayRoots
.grayRoot1
= nullptr;
654 grayRoots
.grayRoot2
= nullptr;
657 void InitGrayRootTracer() {
659 JS_SetGrayGCRootsTracer(cx
, TraceGrayRoots
, &grayRoots
);
662 void RemoveGrayRootTracer() {
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");
674 JSObject
* AllocPlainObject() {
675 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
678 MOZ_ASSERT(obj
->compartment() == global1
->compartment());
682 JSObject
* AllocSameCompartmentSourceObject(JSObject
* target
) {
683 JS::RootedObject
source(cx
, JS_NewPlainObject(cx
));
688 JS::RootedObject
obj(cx
, target
);
689 if (!JS_DefineProperty(cx
, source
, "ptr", obj
, 0)) {
695 MOZ_ASSERT(source
->compartment() == global1
->compartment());
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
)) {
709 MOZ_ASSERT(obj
->compartment() == global2
->compartment());
713 JSObject
* AllocWeakmapKeyObject() {
714 JS::RootedObject
delegate(cx
, JS_NewPlainObject(cx
));
719 JS::RootedObject
key(cx
,
720 js::Wrapper::New(cx
, delegate
, &js::Wrapper::singleton
));
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
731 RootedString
nextPropName(cx
, JS_NewStringCopyZ(cx
, "unique14142135"));
733 if (!JS_StringToId(cx
, nextPropName
, &nextId
)) {
737 RootedObject
head(cx
);
738 for (size_t i
= 0; i
< length
; i
++) {
739 RootedValue
next(cx
, ObjectOrNullValue(head
));
740 head
= AllocPlainObject();
744 if (!JS_DefinePropertyById(cx
, head
, nextId
, next
, 0)) {
752 template <typename F
>
753 bool IterateObjectChain(JSObject
* chain
, F f
) {
754 RootedObject
obj(cx
, chain
);
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();
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());
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
);
787 } else if (color
== MarkColor::Gray
&& !IsMarkedGray(cell
)) {
788 printf("Found non-gray cell: %p\n", cell
);
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());
804 END_TEST(testGCGrayMarking
)