|
25 | 25 | #include "hermes/VM/GCPointer-inline.h"
|
26 | 26 | #include "hermes/VM/HeapSnapshot.h"
|
27 | 27 | #include "hermes/VM/HermesValue-inline.h"
|
| 28 | +#include "hermes/VM/JSWeakMapImpl.h" |
28 | 29 | #include "hermes/VM/Serializer.h"
|
29 | 30 | #include "hermes/VM/SlotAcceptorDefault-inline.h"
|
30 | 31 | #include "hermes/VM/StringPrimitive.h"
|
@@ -630,9 +631,102 @@ void GenGC::completeMarking() {
|
630 | 631 | if (markState_.markStackOverflow_)
|
631 | 632 | break;
|
632 | 633 | }
|
| 634 | + // The marking loop above will have accumulated WeakMaps; |
| 635 | + // find things reachable from values of reachable keys. |
| 636 | + // (Note that this can also set markStackOverflow_). |
| 637 | + completeWeakMapMarking(); |
633 | 638 | } while (markState_.markStackOverflow_);
|
634 | 639 | }
|
635 | 640 |
|
| 641 | +/// For all reachable keys in \p weakMap, mark from the corresponding value |
| 642 | +/// using \p markState and the given \p acceptor, reaching a |
| 643 | +/// transitive closure (or setting \p markState.markStackOverflow_). |
| 644 | +/// Returns whether any previously-unmarked values were marked. |
| 645 | +static bool markFromReachableWeakMapKeys( |
| 646 | + GC *gc, |
| 647 | + JSWeakMap *weakMap, |
| 648 | + CompleteMarkState *markState, |
| 649 | + CompleteMarkState::FullMSCMarkTransitiveAcceptor &acceptor) { |
| 650 | + bool newlyMarkedValue = false; |
| 651 | + for (auto iter = weakMap->keys_begin(), end = weakMap->keys_end(); |
| 652 | + iter != end; |
| 653 | + iter++) { |
| 654 | + GCCell *cell = iter->getObject(gc); |
| 655 | + if (!cell || !AlignedHeapSegment::getCellMarkBit(cell)) { |
| 656 | + continue; |
| 657 | + } |
| 658 | + HermesValue val = weakMap->getValueDirect(gc, *iter); |
| 659 | + if (val.isPointer()) { |
| 660 | + GCCell *valCell = reinterpret_cast<GCCell *>(val.getPointer()); |
| 661 | + if (!AlignedHeapSegment::getCellMarkBit(valCell)) { |
| 662 | + AlignedHeapSegment::setCellMarkBit(valCell); |
| 663 | + markState->pushCell(valCell); |
| 664 | + markState->drainMarkStack(gc, acceptor); |
| 665 | + newlyMarkedValue = true; |
| 666 | + } |
| 667 | + } |
| 668 | + } |
| 669 | + return newlyMarkedValue; |
| 670 | +} |
| 671 | + |
| 672 | +/// For all non-null keys in \p weakMap that are unreachable, clear |
| 673 | +/// the key (clear the pointer in the WeakRefSlot) and value (set it |
| 674 | +/// to undefined). |
| 675 | +static void clearEntriesWithUnreachableKeys(GenGC *gc, JSWeakMap *weakMap) { |
| 676 | + for (auto iter = weakMap->keys_begin(), end = weakMap->keys_end(); |
| 677 | + iter != end; |
| 678 | + iter++) { |
| 679 | + JSObject *keyObj = iter->getObject(gc); |
| 680 | + if (keyObj && !AlignedHeapSegment::getCellMarkBit(keyObj)) { |
| 681 | + weakMap->clearEntryDirect(gc, *iter); |
| 682 | + } |
| 683 | + } |
| 684 | +} |
| 685 | + |
| 686 | +void GenGC::completeWeakMapMarking() { |
| 687 | + CompleteMarkState::FullMSCMarkTransitiveAcceptor acceptor(*this, &markState_); |
| 688 | + |
| 689 | + // Set the currentParPointer to a maximal value, so all pointers scanned |
| 690 | + // will be pushed on the mark stack. |
| 691 | + markState_.currentParPointer = |
| 692 | + reinterpret_cast<GCCell *>(static_cast<intptr_t>(-1)); |
| 693 | + // Must declare this outside the loop, but the initial value doesn't matter: |
| 694 | + // we make it false at the start of each loop iteration. |
| 695 | + bool newReachableValueFound = true; |
| 696 | + do { |
| 697 | + newReachableValueFound = false; |
| 698 | + // Note that new reachable weak maps may be discovered during the loop, so |
| 699 | + // markState_.reachableWeakMaps_.size() may increase during the loop. |
| 700 | + for (unsigned i = 0; i < markState_.reachableWeakMaps_.size(); i++) { |
| 701 | + JSWeakMap *weakMap = markState_.reachableWeakMaps_[i]; |
| 702 | + if (markFromReachableWeakMapKeys(this, weakMap, &markState_, acceptor)) { |
| 703 | + newReachableValueFound = true; |
| 704 | + } |
| 705 | + } |
| 706 | + } while (newReachableValueFound); |
| 707 | + for (auto *weakMap : markState_.reachableWeakMaps_) { |
| 708 | + clearEntriesWithUnreachableKeys(this, weakMap); |
| 709 | + // The argument for why this works is delicate. We need to call |
| 710 | + // markCell, because it marks fields of the WeakMap that are not |
| 711 | + // part of the table -- in particular, the pointer to the |
| 712 | + // valueStorage_ array. There would be a problem, however, if |
| 713 | + // this markCell/drain pair could cause any keys of any WeakMaps |
| 714 | + // to become newly reachable. If so, we'd need to scan their |
| 715 | + // corresponding values. This cannot happen, however. The |
| 716 | + // marking loop above ensured that all values in the valueStorage_ |
| 717 | + // corresponding to reachable keys have been marked. The call |
| 718 | + // just above ensures that values corresponding to unreachable |
| 719 | + // keys have been cleared. So we'll mark *only* the valueStorage_ |
| 720 | + // array; the drainMarkStack call should not do any work. Perhaps |
| 721 | + // we should assert this. |
| 722 | + GCBase::markCell(weakMap, this, acceptor); |
| 723 | + markState_.drainMarkStack(this, acceptor); |
| 724 | + } |
| 725 | + |
| 726 | + markState_.currentParPointer = nullptr; |
| 727 | + markState_.reachableWeakMaps_.clear(); |
| 728 | +} |
| 729 | + |
636 | 730 | void GenGC::finalizeUnreachableObjects() {
|
637 | 731 | youngGen_.finalizeUnreachableObjects();
|
638 | 732 | oldGen_.finalizeUnreachableObjects();
|
|
0 commit comments