testGCGrayMarking.cpp (22612B)
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 <algorithm> 9 10 #include "gc/GCInternals.h" 11 #include "gc/WeakMap.h" 12 #include "gc/Zone.h" 13 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById 14 #include "js/Proxy.h" 15 #include "js/WeakMap.h" 16 #include "jsapi-tests/tests.h" 17 18 using namespace js; 19 using namespace js::gc; 20 21 static constexpr CellColor AllCellColors[] = {CellColor::White, CellColor::Gray, 22 CellColor::Black}; 23 24 static constexpr CellColor MarkedCellColors[] = {CellColor::Gray, 25 CellColor::Black}; 26 27 namespace js { 28 29 struct GCManagedObjectWeakMap 30 : public WeakMap<JSObject*, JSObject*, ZoneAllocPolicy> { 31 using Base = WeakMap<JSObject*, JSObject*, ZoneAllocPolicy>; 32 using Base::Base; 33 }; 34 35 } // namespace js 36 37 namespace JS { 38 39 template <> 40 struct MapTypeToRootKind<js::GCManagedObjectWeakMap*> { 41 static const JS::RootKind kind = JS::RootKind::Traceable; 42 }; 43 44 template <> 45 struct GCPolicy<js::GCManagedObjectWeakMap*> 46 : public NonGCPointerPolicy<js::GCManagedObjectWeakMap*> {}; 47 48 } // namespace JS 49 50 class AutoNoAnalysisForTest { 51 public: 52 AutoNoAnalysisForTest() {} 53 } JS_HAZ_GC_SUPPRESSED; 54 55 BEGIN_TEST(testGCGrayMarking) { 56 AutoNoAnalysisForTest disableAnalysis; 57 AutoDisableCompactingGC disableCompactingGC(cx); 58 AutoLeaveZeal nozeal(cx); 59 60 CHECK(InitGlobals()); 61 JSAutoRealm ar(cx, global1); 62 63 InitGrayRootTracer(); 64 65 // Enable incremental GC. 66 AutoGCParameter param1(cx, JSGC_INCREMENTAL_GC_ENABLED, true); 67 AutoGCParameter param2(cx, JSGC_PER_ZONE_GC_ENABLED, true); 68 69 bool ok = TestMarking() && TestJSWeakMaps() && TestInternalWeakMaps() && 70 TestCCWs() && TestGrayUnmarking(); 71 72 global1 = nullptr; 73 global2 = nullptr; 74 RemoveGrayRootTracer(); 75 76 return ok; 77 } 78 79 bool TestMarking() { 80 JSObject* sameTarget = AllocPlainObject(); 81 CHECK(sameTarget); 82 83 JSObject* sameSource = AllocSameCompartmentSourceObject(sameTarget); 84 CHECK(sameSource); 85 86 JSObject* crossTarget = AllocPlainObject(); 87 CHECK(crossTarget); 88 89 JSObject* crossSource = GetCrossCompartmentWrapper(crossTarget); 90 CHECK(crossSource); 91 92 // Test GC with black roots marks objects black. 93 94 JS::RootedObject blackRoot1(cx, sameSource); 95 JS::RootedObject blackRoot2(cx, crossSource); 96 97 JS_GC(cx); 98 99 CHECK(IsMarkedBlack(sameSource)); 100 CHECK(IsMarkedBlack(crossSource)); 101 CHECK(IsMarkedBlack(sameTarget)); 102 CHECK(IsMarkedBlack(crossTarget)); 103 104 // Test GC with black and gray roots marks objects black. 105 106 grayRoots.grayRoot1 = sameSource; 107 grayRoots.grayRoot2 = crossSource; 108 109 JS_GC(cx); 110 111 CHECK(IsMarkedBlack(sameSource)); 112 CHECK(IsMarkedBlack(crossSource)); 113 CHECK(IsMarkedBlack(sameTarget)); 114 CHECK(IsMarkedBlack(crossTarget)); 115 116 CHECK(!JS::ObjectIsMarkedGray(sameSource)); 117 118 // Test GC with gray roots marks object gray. 119 120 blackRoot1 = nullptr; 121 blackRoot2 = nullptr; 122 123 JS_GC(cx); 124 125 CHECK(IsMarkedGray(sameSource)); 126 CHECK(IsMarkedGray(crossSource)); 127 CHECK(IsMarkedGray(sameTarget)); 128 CHECK(IsMarkedGray(crossTarget)); 129 130 CHECK(JS::ObjectIsMarkedGray(sameSource)); 131 132 // Test ExposeToActiveJS marks gray objects black. 133 134 JS::ExposeObjectToActiveJS(sameSource); 135 JS::ExposeObjectToActiveJS(crossSource); 136 CHECK(IsMarkedBlack(sameSource)); 137 CHECK(IsMarkedBlack(crossSource)); 138 CHECK(IsMarkedBlack(sameTarget)); 139 CHECK(IsMarkedBlack(crossTarget)); 140 141 // Test a zone GC with black roots marks gray object in other zone black. 142 143 JS_GC(cx); 144 145 CHECK(IsMarkedGray(crossSource)); 146 CHECK(IsMarkedGray(crossTarget)); 147 148 blackRoot1 = crossSource; 149 CHECK(ZoneGC(crossSource->zone())); 150 151 CHECK(IsMarkedBlack(crossSource)); 152 CHECK(IsMarkedBlack(crossTarget)); 153 154 blackRoot1 = nullptr; 155 blackRoot2 = nullptr; 156 grayRoots.grayRoot1 = nullptr; 157 grayRoots.grayRoot2 = nullptr; 158 159 return true; 160 } 161 162 static constexpr CellColor DontMark = CellColor::White; 163 164 enum MarkKeyOrDelegate : bool { MarkKey = true, MarkDelegate = false }; 165 166 bool TestJSWeakMaps() { 167 for (auto keyOrDelegateColor : MarkedCellColors) { 168 for (auto mapColor : MarkedCellColors) { 169 for (auto markKeyOrDelegate : {MarkKey, MarkDelegate}) { 170 CellColor expected = std::min(keyOrDelegateColor, mapColor); 171 CHECK(TestJSWeakMap(markKeyOrDelegate, keyOrDelegateColor, mapColor, 172 expected)); 173 #ifdef JS_GC_ZEAL 174 CHECK(TestJSWeakMapWithGrayUnmarking( 175 markKeyOrDelegate, keyOrDelegateColor, mapColor, expected)); 176 #endif 177 } 178 } 179 } 180 181 return true; 182 } 183 184 bool TestInternalWeakMaps() { 185 for (auto keyMarkColor : AllCellColors) { 186 for (auto delegateMarkColor : AllCellColors) { 187 if (keyMarkColor == CellColor::White && 188 delegateMarkColor == CellColor::White) { 189 continue; 190 } 191 192 // The map is black. The delegate marks its key via wrapper preservation. 193 // The key maps its delegate and the value. Thus, all three end up the 194 // maximum of the key and delegate colors. 195 CellColor expected = std::max(keyMarkColor, delegateMarkColor); 196 CHECK(TestInternalWeakMap(keyMarkColor, delegateMarkColor, expected)); 197 198 #ifdef JS_GC_ZEAL 199 CHECK(TestInternalWeakMapWithGrayUnmarking(keyMarkColor, 200 delegateMarkColor, expected)); 201 #endif 202 } 203 } 204 205 return true; 206 } 207 208 bool TestJSWeakMap(MarkKeyOrDelegate markKey, CellColor weakMapMarkColor, 209 CellColor keyOrDelegateMarkColor, 210 CellColor expectedValueColor) { 211 using std::swap; 212 213 // Test marking a JS WeakMap object. 214 // 215 // This marks the map and one of the key or delegate. The key/delegate and the 216 // value can end up different colors depending on the color of the map. 217 218 JSObject* weakMap; 219 JSObject* key; 220 JSObject* value; 221 222 // If both map and key are marked the same color, test both possible 223 // orderings. 224 unsigned markOrderings = weakMapMarkColor == keyOrDelegateMarkColor ? 2 : 1; 225 226 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) { 227 CHECK(CreateJSWeakMapObjects(&weakMap, &key, &value)); 228 229 JSObject* delegate = UncheckedUnwrapWithoutExpose(key); 230 JSObject* keyOrDelegate = markKey ? key : delegate; 231 232 RootedObject blackRoot1(cx); 233 RootedObject blackRoot2(cx); 234 235 RootObject(weakMap, weakMapMarkColor, blackRoot1, grayRoots.grayRoot1); 236 RootObject(keyOrDelegate, keyOrDelegateMarkColor, blackRoot2, 237 grayRoots.grayRoot2); 238 239 if (markOrder != 0) { 240 swap(blackRoot1.get(), blackRoot2.get()); 241 swap(grayRoots.grayRoot1, grayRoots.grayRoot2); 242 } 243 244 JS_GC(cx); 245 246 ClearGrayRoots(); 247 248 CHECK(weakMap->color() == weakMapMarkColor); 249 CHECK(keyOrDelegate->color() == keyOrDelegateMarkColor); 250 CHECK(value->color() == expectedValueColor); 251 } 252 253 return true; 254 } 255 256 #ifdef JS_GC_ZEAL 257 258 bool TestJSWeakMapWithGrayUnmarking(MarkKeyOrDelegate markKey, 259 CellColor weakMapMarkColor, 260 CellColor keyOrDelegateMarkColor, 261 CellColor expectedValueColor) { 262 // This is like the previous test, but things are marked black by gray 263 // unmarking during incremental GC. 264 265 JSObject* weakMap; 266 JSObject* key; 267 JSObject* value; 268 269 // If both map and key are marked the same color, test both possible 270 // orderings. 271 unsigned markOrderings = weakMapMarkColor == keyOrDelegateMarkColor ? 2 : 1; 272 273 JS::SetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking), 0); 274 275 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) { 276 CHECK(CreateJSWeakMapObjects(&weakMap, &key, &value)); 277 278 JSObject* delegate = UncheckedUnwrapWithoutExpose(key); 279 JSObject* keyOrDelegate = markKey ? key : delegate; 280 281 grayRoots.grayRoot1 = keyOrDelegate; 282 grayRoots.grayRoot2 = weakMap; 283 284 // Start an incremental GC and run until gray roots have been pushed onto 285 // the mark stack. 286 JS::PrepareForFullGC(cx); 287 JS::SliceBudget budget(JS::TimeBudget(1000000)); 288 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::DEBUG_GC, 289 budget); 290 MOZ_ASSERT(cx->runtime()->gc.state() == gc::State::Sweep); 291 MOZ_ASSERT(cx->zone()->gcState() == Zone::MarkBlackAndGray); 292 293 // Unmark gray things as specified. 294 if (markOrder != 0) { 295 MaybeExposeObject(weakMap, weakMapMarkColor); 296 MaybeExposeObject(keyOrDelegate, keyOrDelegateMarkColor); 297 } else { 298 MaybeExposeObject(keyOrDelegate, keyOrDelegateMarkColor); 299 MaybeExposeObject(weakMap, weakMapMarkColor); 300 } 301 302 JS::FinishIncrementalGC(cx, JS::GCReason::API); 303 304 ClearGrayRoots(); 305 306 CHECK(weakMap->color() == weakMapMarkColor); 307 CHECK(keyOrDelegate->color() == keyOrDelegateMarkColor); 308 CHECK(value->color() == expectedValueColor); 309 } 310 311 JS::UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking)); 312 313 return true; 314 } 315 316 static void MaybeExposeObject(JSObject* object, CellColor color) { 317 if (color == CellColor::Black) { 318 JS::ExposeObjectToActiveJS(object); 319 } 320 } 321 322 #endif // JS_GC_ZEAL 323 324 bool CreateJSWeakMapObjects(JSObject** weakMapOut, JSObject** keyOut, 325 JSObject** valueOut) { 326 RootedObject key(cx, AllocWeakmapKeyObject()); 327 CHECK(key); 328 329 RootedObject value(cx, AllocPlainObject()); 330 CHECK(value); 331 332 RootedObject weakMap(cx, JS::NewWeakMapObject(cx)); 333 CHECK(weakMap); 334 335 JS::RootedValue keyValue(cx, ObjectValue(*key)); 336 JS::RootedValue valueValue(cx, ObjectValue(*value)); 337 CHECK(SetWeakMapEntry(cx, weakMap, keyValue, valueValue)); 338 339 *weakMapOut = weakMap; 340 *keyOut = key; 341 *valueOut = value; 342 return true; 343 } 344 345 bool TestInternalWeakMap(CellColor keyMarkColor, CellColor delegateMarkColor, 346 CellColor expectedColor) { 347 using std::swap; 348 349 // Test marking for internal weakmaps (without an owning JSObject). 350 // 351 // All of the key, delegate and value are expected to end up the same color. 352 353 UniquePtr<GCManagedObjectWeakMap> weakMap; 354 JSObject* key; 355 JSObject* value; 356 357 // If both key and delegate are marked the same color, test both possible 358 // orderings. 359 unsigned markOrderings = keyMarkColor == delegateMarkColor ? 2 : 1; 360 361 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) { 362 CHECK(CreateInternalWeakMapObjects(&weakMap, &key, &value)); 363 364 JSObject* delegate = UncheckedUnwrapWithoutExpose(key); 365 366 RootedObject blackRoot1(cx); 367 RootedObject blackRoot2(cx); 368 369 Rooted<GCManagedObjectWeakMap*> rootMap(cx, weakMap.get()); 370 RootObject(key, keyMarkColor, blackRoot1, grayRoots.grayRoot1); 371 RootObject(delegate, delegateMarkColor, blackRoot2, grayRoots.grayRoot2); 372 373 if (markOrder != 0) { 374 swap(blackRoot1.get(), blackRoot2.get()); 375 swap(grayRoots.grayRoot1, grayRoots.grayRoot2); 376 } 377 378 JS_GC(cx); 379 380 ClearGrayRoots(); 381 382 CHECK(key->color() == expectedColor); 383 CHECK(delegate->color() == expectedColor); 384 CHECK(value->color() == expectedColor); 385 386 AutoSetThreadIsFinalizing setFinalizing; 387 js_delete(weakMap.release()); 388 } 389 390 return true; 391 } 392 393 #ifdef JS_GC_ZEAL 394 395 bool TestInternalWeakMapWithGrayUnmarking(CellColor keyMarkColor, 396 CellColor delegateMarkColor, 397 CellColor expectedColor) { 398 UniquePtr<GCManagedObjectWeakMap> weakMap; 399 JSObject* key; 400 JSObject* value; 401 402 // If both key and delegate are marked the same color, test both possible 403 // orderings. 404 unsigned markOrderings = keyMarkColor == delegateMarkColor ? 2 : 1; 405 406 JS::SetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking), 0); 407 408 for (unsigned markOrder = 0; markOrder < markOrderings; markOrder++) { 409 CHECK(CreateInternalWeakMapObjects(&weakMap, &key, &value)); 410 411 JSObject* delegate = UncheckedUnwrapWithoutExpose(key); 412 413 Rooted<GCManagedObjectWeakMap*> rootMap(cx, weakMap.get()); 414 grayRoots.grayRoot1 = key; 415 grayRoots.grayRoot2 = delegate; 416 417 // Start an incremental GC and run until gray roots have been pushed onto 418 // the mark stack. 419 JS::PrepareForFullGC(cx); 420 JS::SliceBudget budget(JS::TimeBudget(1000000)); 421 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::DEBUG_GC, 422 budget); 423 MOZ_ASSERT(cx->runtime()->gc.state() == gc::State::Sweep); 424 MOZ_ASSERT(cx->zone()->gcState() == Zone::MarkBlackAndGray); 425 426 // Unmark gray things as specified. 427 if (markOrder != 0) { 428 MaybeExposeObject(key, keyMarkColor); 429 MaybeExposeObject(delegate, delegateMarkColor); 430 } else { 431 MaybeExposeObject(key, keyMarkColor); 432 MaybeExposeObject(delegate, delegateMarkColor); 433 } 434 435 JS::FinishIncrementalGC(cx, JS::GCReason::API); 436 437 ClearGrayRoots(); 438 439 CHECK(key->color() == expectedColor); 440 CHECK(delegate->color() == expectedColor); 441 CHECK(value->color() == expectedColor); 442 443 AutoSetThreadIsFinalizing setFinalizing; 444 js_delete(weakMap.release()); 445 } 446 447 JS::UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking)); 448 449 return true; 450 } 451 452 #endif // JS_GC_ZEAL 453 454 bool CreateInternalWeakMapObjects(UniquePtr<GCManagedObjectWeakMap>* weakMapOut, 455 JSObject** keyOut, JSObject** valueOut) { 456 RootedObject key(cx, AllocWeakmapKeyObject()); 457 CHECK(key); 458 459 RootedObject value(cx, AllocPlainObject()); 460 CHECK(value); 461 462 auto weakMap = cx->make_unique<GCManagedObjectWeakMap>(cx); 463 CHECK(weakMap); 464 465 CHECK(weakMap->put(key, value)); 466 467 *weakMapOut = std::move(weakMap); 468 *keyOut = key; 469 *valueOut = value; 470 return true; 471 } 472 473 void RootObject(JSObject* object, CellColor color, RootedObject& blackRoot, 474 JS::Heap<JSObject*>& grayRoot) { 475 if (color == CellColor::Black) { 476 blackRoot = object; 477 } else if (color == CellColor::Gray) { 478 grayRoot = object; 479 } else { 480 MOZ_RELEASE_ASSERT(color == CellColor::White); 481 } 482 } 483 484 bool TestCCWs() { 485 JSObject* target = AllocPlainObject(); 486 CHECK(target); 487 488 // Test getting a new wrapper doesn't return a gray wrapper. 489 490 RootedObject blackRoot(cx, target); 491 JSObject* wrapper = GetCrossCompartmentWrapper(target); 492 CHECK(wrapper); 493 CHECK(!IsMarkedGray(wrapper)); 494 495 // Test getting an existing wrapper doesn't return a gray wrapper. 496 497 grayRoots.grayRoot1 = wrapper; 498 grayRoots.grayRoot2 = nullptr; 499 JS_GC(cx); 500 CHECK(IsMarkedGray(wrapper)); 501 CHECK(IsMarkedBlack(target)); 502 503 CHECK(GetCrossCompartmentWrapper(target) == wrapper); 504 CHECK(!IsMarkedGray(wrapper)); 505 506 // Test getting an existing wrapper doesn't return a gray wrapper 507 // during incremental GC. 508 509 JS_GC(cx); 510 CHECK(IsMarkedGray(wrapper)); 511 CHECK(IsMarkedBlack(target)); 512 513 JSRuntime* rt = cx->runtime(); 514 JS::PrepareForFullGC(cx); 515 JS::SliceBudget budget(JS::WorkBudget(1)); 516 rt->gc.startDebugGC(JS::GCOptions::Normal, budget); 517 while (rt->gc.state() == gc::State::Prepare) { 518 rt->gc.debugGCSlice(budget); 519 } 520 CHECK(JS::IsIncrementalGCInProgress(cx)); 521 522 CHECK(!IsMarkedBlack(wrapper)); 523 CHECK(wrapper->zone()->isGCMarkingBlackOnly()); 524 525 CHECK(GetCrossCompartmentWrapper(target) == wrapper); 526 CHECK(IsMarkedBlack(wrapper)); 527 528 JS::FinishIncrementalGC(cx, JS::GCReason::API); 529 530 // Test behaviour of gray CCWs marked black by a barrier during incremental 531 // GC. 532 533 // Initial state: source and target are gray. 534 blackRoot = nullptr; 535 grayRoots.grayRoot1 = wrapper; 536 grayRoots.grayRoot2 = nullptr; 537 JS_GC(cx); 538 CHECK(IsMarkedGray(wrapper)); 539 CHECK(IsMarkedGray(target)); 540 541 // Incremental zone GC started: the source is now unmarked. 542 JS::PrepareZoneForGC(cx, wrapper->zone()); 543 budget = JS::SliceBudget(JS::WorkBudget(1)); 544 rt->gc.startDebugGC(JS::GCOptions::Normal, budget); 545 while (rt->gc.state() == gc::State::Prepare) { 546 rt->gc.debugGCSlice(budget); 547 } 548 CHECK(JS::IsIncrementalGCInProgress(cx)); 549 CHECK(wrapper->zone()->isGCMarkingBlackOnly()); 550 CHECK(!target->zone()->wasGCStarted()); 551 CHECK(!IsMarkedBlack(wrapper)); 552 CHECK(!IsMarkedGray(wrapper)); 553 CHECK(IsMarkedGray(target)); 554 555 // Betweeen GC slices: source marked black by barrier, target is 556 // still gray. Target will be marked gray 557 // eventually. ObjectIsMarkedGray() is conservative and reports 558 // that target is not marked gray; AssertObjectIsNotGray() will 559 // assert. 560 grayRoots.grayRoot1.get(); 561 CHECK(IsMarkedBlack(wrapper)); 562 CHECK(IsMarkedGray(target)); 563 CHECK(!JS::ObjectIsMarkedGray(target)); 564 565 // Final state: source and target are black. 566 JS::FinishIncrementalGC(cx, JS::GCReason::API); 567 CHECK(IsMarkedBlack(wrapper)); 568 CHECK(IsMarkedBlack(target)); 569 570 grayRoots.grayRoot1 = nullptr; 571 grayRoots.grayRoot2 = nullptr; 572 573 return true; 574 } 575 576 bool TestGrayUnmarking() { 577 const size_t length = 2000; 578 579 JSObject* chain = AllocObjectChain(length); 580 CHECK(chain); 581 582 RootedObject blackRoot(cx, chain); 583 JS_GC(cx); 584 size_t count; 585 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count))); 586 CHECK(count == length); 587 588 blackRoot = nullptr; 589 grayRoots.grayRoot1 = chain; 590 JS_GC(cx); 591 CHECK(cx->runtime()->gc.areGrayBitsValid()); 592 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Gray, &count))); 593 CHECK(count == length); 594 595 JS::ExposeObjectToActiveJS(chain); 596 CHECK(cx->runtime()->gc.areGrayBitsValid()); 597 CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count))); 598 CHECK(count == length); 599 600 grayRoots.grayRoot1 = nullptr; 601 602 return true; 603 } 604 605 struct ColorCheckFunctor { 606 MarkColor color; 607 size_t& count; 608 609 ColorCheckFunctor(MarkColor colorArg, size_t* countArg) 610 : color(colorArg), count(*countArg) { 611 count = 0; 612 } 613 614 bool operator()(JSObject* obj) { 615 if (!CheckCellColor(obj, color)) { 616 return false; 617 } 618 619 NativeObject& nobj = obj->as<NativeObject>(); 620 if (!CheckCellColor(nobj.shape(), color)) { 621 return false; 622 } 623 624 NativeShape* shape = nobj.shape(); 625 if (!CheckCellColor(shape, color)) { 626 return false; 627 } 628 629 // Shapes and symbols are never marked gray. 630 ShapePropertyIter<NoGC> iter(shape); 631 jsid id = iter->key(); 632 if (id.isGCThing() && 633 !CheckCellColor(id.toGCCellPtr().asCell(), MarkColor::Black)) { 634 return false; 635 } 636 637 count++; 638 return true; 639 } 640 }; 641 642 JS::PersistentRootedObject global1; 643 JS::PersistentRootedObject global2; 644 645 struct GrayRoots { 646 JS::Heap<JSObject*> grayRoot1; 647 JS::Heap<JSObject*> grayRoot2; 648 }; 649 650 GrayRoots grayRoots; 651 652 bool InitGlobals() { 653 global1.init(cx, global); 654 if (!createGlobal()) { 655 return false; 656 } 657 global2.init(cx, global); 658 return global2 != nullptr; 659 } 660 661 void ClearGrayRoots() { 662 grayRoots.grayRoot1 = nullptr; 663 grayRoots.grayRoot2 = nullptr; 664 } 665 666 void InitGrayRootTracer() { 667 ClearGrayRoots(); 668 JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, &grayRoots); 669 } 670 671 void RemoveGrayRootTracer() { 672 ClearGrayRoots(); 673 JS_SetGrayGCRootsTracer(cx, nullptr, nullptr); 674 } 675 676 static bool TraceGrayRoots(JSTracer* trc, JS::SliceBudget& budget, void* data) { 677 auto grayRoots = static_cast<GrayRoots*>(data); 678 TraceEdge(trc, &grayRoots->grayRoot1, "gray root 1"); 679 TraceEdge(trc, &grayRoots->grayRoot2, "gray root 2"); 680 return true; 681 } 682 683 JSObject* AllocPlainObject() { 684 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 685 EvictNursery(); 686 687 MOZ_ASSERT(obj->compartment() == global1->compartment()); 688 return obj; 689 } 690 691 JSObject* AllocSameCompartmentSourceObject(JSObject* target) { 692 JS::RootedObject source(cx, JS_NewPlainObject(cx)); 693 if (!source) { 694 return nullptr; 695 } 696 697 JS::RootedObject obj(cx, target); 698 if (!JS_DefineProperty(cx, source, "ptr", obj, 0)) { 699 return nullptr; 700 } 701 702 EvictNursery(); 703 704 MOZ_ASSERT(source->compartment() == global1->compartment()); 705 return source; 706 } 707 708 JSObject* GetCrossCompartmentWrapper(JSObject* target) { 709 MOZ_ASSERT(target->compartment() == global1->compartment()); 710 JS::RootedObject obj(cx, target); 711 JSAutoRealm ar(cx, global2); 712 if (!JS_WrapObject(cx, &obj)) { 713 return nullptr; 714 } 715 716 EvictNursery(); 717 718 MOZ_ASSERT(obj->compartment() == global2->compartment()); 719 return obj; 720 } 721 722 JSObject* AllocWeakmapKeyObject() { 723 JS::RootedObject delegate(cx, JS_NewPlainObject(cx)); 724 if (!delegate) { 725 return nullptr; 726 } 727 728 JS::RootedObject key(cx, 729 js::Wrapper::New(cx, delegate, &js::Wrapper::singleton)); 730 731 EvictNursery(); 732 return key; 733 } 734 735 JSObject* AllocObjectChain(size_t length) { 736 // Allocate a chain of linked JSObjects. 737 738 // Use a unique property name so the shape is not shared with any other 739 // objects. 740 RootedString nextPropName(cx, JS_NewStringCopyZ(cx, "unique14142135")); 741 RootedId nextId(cx); 742 if (!JS_StringToId(cx, nextPropName, &nextId)) { 743 return nullptr; 744 } 745 746 RootedObject head(cx); 747 for (size_t i = 0; i < length; i++) { 748 RootedValue next(cx, ObjectOrNullValue(head)); 749 head = AllocPlainObject(); 750 if (!head) { 751 return nullptr; 752 } 753 if (!JS_DefinePropertyById(cx, head, nextId, next, 0)) { 754 return nullptr; 755 } 756 } 757 758 return head; 759 } 760 761 template <typename F> 762 bool IterateObjectChain(JSObject* chain, F f) { 763 RootedObject obj(cx, chain); 764 while (obj) { 765 if (!f(obj)) { 766 return false; 767 } 768 769 // Access the 'next' property via the object's slots to avoid triggering 770 // gray marking assertions when calling JS_GetPropertyById. 771 NativeObject& nobj = obj->as<NativeObject>(); 772 MOZ_ASSERT(nobj.slotSpan() == 1); 773 obj = nobj.getSlot(0).toObjectOrNull(); 774 } 775 776 return true; 777 } 778 779 static bool IsMarkedBlack(Cell* cell) { 780 TenuredCell* tc = &cell->asTenured(); 781 return tc->isMarkedBlack(); 782 } 783 784 static bool IsMarkedGray(Cell* cell) { 785 TenuredCell* tc = &cell->asTenured(); 786 bool isGray = tc->isMarkedGray(); 787 MOZ_ASSERT_IF(isGray, tc->isMarkedAny()); 788 return isGray; 789 } 790 791 static bool CheckCellColor(Cell* cell, MarkColor color) { 792 MOZ_ASSERT(color == MarkColor::Black || color == MarkColor::Gray); 793 if (color == MarkColor::Black && !IsMarkedBlack(cell)) { 794 printf("Found non-black cell: %p\n", cell); 795 return false; 796 } else if (color == MarkColor::Gray && !IsMarkedGray(cell)) { 797 printf("Found non-gray cell: %p\n", cell); 798 return false; 799 } 800 801 return true; 802 } 803 804 void EvictNursery() { cx->runtime()->gc.evictNursery(); } 805 806 bool ZoneGC(JS::Zone* zone) { 807 JS::PrepareZoneForGC(cx, zone); 808 cx->runtime()->gc.gc(JS::GCOptions::Normal, JS::GCReason::API); 809 CHECK(!cx->runtime()->gc.isFullGc()); 810 return true; 811 } 812 813 END_TEST(testGCGrayMarking)