Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  FinalizationObservers.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
 * GC support for FinalizationRegistry and WeakRef objects.
 */


#include "gc/FinalizationObservers.h"

#include "builtin/FinalizationRegistryObject.h"
#include "builtin/WeakRefObject.h"
#include "gc/GCRuntime.h"
#include "gc/PublicIterators.h"
#include "gc/Zone.h"
#include "vm/JSContext.h"

#include "gc/WeakMap-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"

using namespace js;
using namespace js::gc;

static inline Zone* GetWeakTargetZone(const Value& value) {
  return value.toGCThing()->zone();
}

static inline void CheckTargetValue(const Value& target) {
  MOZ_ASSERT(CanBeHeldWeakly(target));
  MOZ_ASSERT_IF(target.isObject(),
                !IsCrossCompartmentWrapper(&target.toObject()));
}

/* static */
ObserverListPtr ObserverListPtr::fromValue(Value value) {
  MOZ_ASSERT(value.isDouble());  // Stored as PrivateValue.
  return ObserverListPtr(value);
}

ObserverListPtr::ObserverListPtr(ObserverListObject* element)
    : ObserverListPtr(element, ElementKind) {}

ObserverListPtr::ObserverListPtr(ObserverList* list)
    : ObserverListPtr(list, ListHeadKind) {}

ObserverListPtr::ObserverListPtr(void* ptr, Kind kind)
    : value(PrivateValue(uintptr_t(ptr) | kind)) {
  MOZ_ASSERT((uintptr_t(ptr) & KindMask) == 0);
}

ObserverListPtr::ObserverListPtr(Value value) : value(value) {}

template <typename F>
auto ObserverListPtr::map(F&& func) const {
  if (isElement()) {
    return func(asElement());
  }

  return func(asList());
}

bool ObserverListPtr::isElement() const { return kind() == ElementKind; }

ObserverListPtr::Kind ObserverListPtr::kind() const {
  uintptr_t bits = uintptr_t(value.toPrivate());
  return static_cast<Kind>(bits & KindMask);
}

void* ObserverListPtr::ptr() const {
  uintptr_t bits = uintptr_t(value.toPrivate());
  return reinterpret_cast<void*>(bits & ~KindMask);
}

ObserverListObject* ObserverListPtr::asElement() const {
  MOZ_ASSERT(isElement());
  return static_cast<ObserverListObject*>(ptr());
}

ObserverList* ObserverListPtr::asList() const {
  MOZ_ASSERT(!isElement());
  return static_cast<ObserverList*>(ptr());
}

ObserverListPtr ObserverListPtr::getNext() const {
  return map([](auto* element) { return element->getNext(); });
}

ObserverListPtr ObserverListPtr::getPrev() const {
  return map([](auto* element) { return element->getPrev(); });
}

void ObserverListPtr::setNext(ObserverListPtr next) {
  map([next](auto* element) { element->setNext(next); });
}

/* static */
void ObserverListPtr::setPrev(ObserverListPtr prev) {
  map([prev](auto* element) { element->setPrev(prev); });
}

// An iterator for ObserverList that allows removing the current element from
// the list.
class ObserverList::Iter {
  using Ptr = ObserverListPtr;
  const Ptr end;
  Ptr ptr;
  Ptr nextPtr;

 public:
  explicit Iter(ObserverList& list)
      : end(&list), ptr(end.getNext()), nextPtr(ptr.getNext()) {
    MOZ_ASSERT(list.isEmpty() == done());
  }

  bool done() const { return ptr == end; }

  ObserverListObject* get() const {
    MOZ_ASSERT(!done());
    return ptr.asElement();
  }

  void next() {
    MOZ_ASSERT(!done());
    ptr = nextPtr;
    nextPtr = ptr.getNext();
  }

  operator ObserverListObject*() const { return get(); }
  ObserverListObject* operator->() const { return get(); }
};

ObserverList::ObserverList() : next(this), prev(this) { MOZ_ASSERT(isEmpty()); }

ObserverList::~ObserverList() { MOZ_ASSERT(isEmpty()); }

ObserverList::ObserverList(ObserverList&& other) : ObserverList() {
  MOZ_ASSERT(&other != this);
  *this = std::move(other);
}
ObserverList& ObserverList::operator=(ObserverList&& other) {
  MOZ_ASSERT(&other != this);
  MOZ_ASSERT(isEmpty());

  AutoTouchingGrayThings atgt;

  if (other.isEmpty()) {
    return *this;
  }

  next = other.next;
  prev = other.prev;

  // Check other's list head is correctly linked to its neighbours.
  MOZ_ASSERT(next.getPrev().asList() == &other);
  MOZ_ASSERT(prev.getNext().asList() == &other);

  // Update those neighbours to point to this object.
  next.setPrev(this);
  prev.setNext(this);

  other.makeEmpty();

  return *this;
}

void ObserverList::makeEmpty() {
  next = this;
  prev = this;
  MOZ_ASSERT(isEmpty());
}

bool ObserverList::isEmpty() const {
  ObserverListPtr thisLink = const_cast<ObserverList*>(this);
  MOZ_ASSERT((getNext() == thisLink) == (getPrev() == thisLink));
  return getNext() == thisLink;
}

ObserverListObject* ObserverList::getFirst() const {
  MOZ_ASSERT(!isEmpty());
  return next.asElement();
}

ObserverList::Iter ObserverList::iter() { return Iter(*this); }

void ObserverList::insertFront(ObserverListObject* obj) {
  MOZ_ASSERT(!obj->isInList());

  // The other things in this list might be gray.
  AutoTouchingGrayThings atgt;

  Ptr oldNext = getNext();

  setNext(obj);
  obj->setNext(oldNext);

  oldNext.setPrev(obj);
  obj->setPrev(this);
}

static inline void LinkElements(ObserverListPtr a, ObserverListPtr b) {
  a.setNext(b);
  b.setPrev(a);
}

void ObserverList::append(ObserverList&& other) {
  // The things in these lists might be gray.
  AutoTouchingGrayThings atgt;

  if (other.isEmpty()) {
    return;
  }

  LinkElements(getPrev(), other.getNext());
  LinkElements(other.getPrev(), this);

  other.makeEmpty();
}

void ObserverList::setNext(Ptr link) { next = link; }

void ObserverList::setPrev(Ptr link) { prev = link; }

/* static */
const ClassExtension ObserverListObject::classExtension_ = {
    ObserverListObject::objectMoved,  // objectMovedOp
};

bool ObserverListObject::isInList() const {
  bool inList = !getReservedSlot(NextSlot).isUndefined();
  MOZ_ASSERT(inList == !getReservedSlot(PrevSlot).isUndefined());
  return inList;
}

/* static */
size_t ObserverListObject::objectMoved(JSObject* obj, JSObject* old) {
  auto* self = static_cast<ObserverListObject*>(obj);
  self->objectMovedFrom(static_cast<ObserverListObject*>(old));
  return 0;
}

void ObserverListObject::objectMovedFrom(ObserverListObject* old) {
  AutoTouchingGrayThings atgt;

  if (!isInList()) {
    return;
  }

#ifdef DEBUG
  Ptr oldPtr = old;
  MOZ_ASSERT(getNext() != oldPtr);
  MOZ_ASSERT(getPrev() != oldPtr);
  MOZ_ASSERT(getNext().getPrev() == oldPtr);
  MOZ_ASSERT(getPrev().getNext() == oldPtr);
#endif

  getNext().setPrev(this);
  getPrev().setNext(this);
}

void ObserverListObject::unlink() {
  AutoTouchingGrayThings atgt;

  if (!isInList()) {
    return;
  }

  Ptr next = getNext();
  Ptr prev = getPrev();

#ifdef DEBUG
  Ptr thisPtr = this;
  MOZ_ASSERT(prev.getNext() == thisPtr);
  MOZ_ASSERT(next.getPrev() == thisPtr);
#endif

  next.setPrev(prev);
  prev.setNext(next);

  setReservedSlot(NextSlot, UndefinedValue());
  setReservedSlot(PrevSlot, UndefinedValue());
  MOZ_ASSERT(!isInList());
}

ObserverListPtr ObserverListObject::getNext() const {
  Value value = getReservedSlot(NextSlot);
  return Ptr::fromValue(value);
}

ObserverListPtr ObserverListObject::getPrev() const {
  Value value = getReservedSlot(PrevSlot);
  return Ptr::fromValue(value);
}

void ObserverListObject::setNext(Ptr next) {
  setReservedSlot(NextSlot, next.asValue());
}

void ObserverListObject::setPrev(Ptr prev) {
  setReservedSlot(PrevSlot, prev.asValue());
}

FinalizationObservers::FinalizationObservers(Zone* zone)
    : registries(zone), recordMap(zone), weakRefMap(zone) {}

FinalizationObservers::~FinalizationObservers() {
  MOZ_ASSERT(registries.empty());
  MOZ_ASSERT(recordMap.empty());
}

bool GCRuntime::addFinalizationRegistry(
    JSContext* cx, Handle<FinalizationRegistryObject*> registry) {
  if (!cx->zone()->ensureFinalizationObservers() ||
      !cx->zone()->finalizationObservers()->addRegistry(registry)) {
    ReportOutOfMemory(cx);
    return false;
  }

  return true;
}

bool FinalizationObservers::addRegistry(
    Handle<FinalizationRegistryObject*> registry) {
  return registries.put(registry);
}

bool GCRuntime::registerWithFinalizationRegistry(
    JSContext* cx, HandleValue target,
    Handle<FinalizationRecordObject*> record) {
  CheckTargetValue(target);

  Zone* zone = GetWeakTargetZone(target);
  if (!zone->ensureFinalizationObservers() ||
      !zone->finalizationObservers()->addRecord(target, record)) {
    ReportOutOfMemory(cx);
    return false;
  }

  return true;
}

bool FinalizationObservers::addRecord(
    HandleValue target, Handle<FinalizationRecordObject*> record) {
  // Add a record to the record map and clean up on failure.
  //
  // The following must be updated and kept in sync:
  //  - the zone's recordMap (to observe the target)
  //  - the registry's global objects's recordSet (to trace the record)

  auto ptr = recordMap.lookupForAdd(target);
  if (!ptr && !recordMap.add(ptr, target, ObserverList())) {
    return false;
  }

  ptr->value().insertFront(record);

  record->setInRecordMap(true);
  return true;
}

void FinalizationObservers::clearRecords() {
  // Clear table entries related to FinalizationRecordObjects, which are not
  // processed after the start of shutdown.
  //
  // WeakRefs are still updated during shutdown to avoid the possibility of
  // stale or dangling pointers.
  for (auto iter = recordMap.iter(); !iter.done(); iter.next()) {
    ObserverList& records = iter.get().value();
    for (auto listIter = records.iter(); !listIter.done(); listIter.next()) {
      listIter->unlink();
    }
  }
  recordMap.clear();
}

void GCRuntime::traceWeakFinalizationObserverEdges(JSTracer* trc, Zone* zone) {
  MOZ_ASSERT(CurrentThreadCanAccessRuntime(trc->runtime()));
  FinalizationObservers* observers = zone->finalizationObservers();
  if (observers) {
    observers->traceWeakEdges(trc, zone);
  }
}

void FinalizationObservers::traceWeakEdges(JSTracer* trc, JS::Zone* zone) {
  // Removing dead pointers from vectors may reorder live pointers to gray
  // things in the vector. This is OK.
  AutoTouchingGrayThings atgt;

  traceWeakWeakRefEdges(trc);
  traceWeakFinalizationRegistryEdges(trc, zone);
}

void FinalizationObservers::traceWeakFinalizationRegistryEdges(JSTracer* trc,
                                                               JS::Zone* zone) {
  // Sweep finalization registry data and queue finalization records for cleanup
  // for any entries whose target is dying and remove them from the map.

  // Clear cached state and set it again below if required.
  zone->clearGCFinalizationRegistriesMayHaveSymbolRegistrations();

  GCRuntime* gc = &trc->runtime()->gc;

  for (auto iter = registries.modIter(); !iter.done(); iter.next()) {
    MOZ_ASSERT(MaybeForwarded(iter.get().get())->zone() == zone);

    auto result =
        TraceWeakEdge(trc, &iter.getMutable(), "FinalizationRegistry");
    if (result.isDead()) {
      auto* registry = result.initialTarget();
      registry->queue()->setHasRegistry(false);

      // Remove any queued records. These might be dead since the registry was
      // not marked.
      registry->queue()->clear();

      iter.remove();
    } else {
      FinalizationRegistryObject* registry = result.finalTarget();
      bool hasSymbolRegistrations = false;
      registry->traceWeak(trc, &hasSymbolRegistrations);
      if (hasSymbolRegistrations) {
        zone->setGCFinalizationRegistriesMayHaveSymbolRegistrations();
      }

      // Now we know the registry is alive we can queue any records for cleanup
      // if this didn't happen already. See
      // shouldQueueFinalizationRegistryForCleanup for details.
      FinalizationQueueObject* queue = registry->queue();
      if (queue->hasRecordsToCleanUp()) {
        MOZ_ASSERT(shouldQueueFinalizationRegistryForCleanup(queue));
        gc->queueFinalizationRegistryForCleanup(queue);
      }
    }
  }

  for (auto iter = recordMap.modIter(); !iter.done(); iter.next()) {
    ObserverList& records = iter.get().value();

    // Sweep finalization records, removing any dead ones.
    for (auto iter = records.iter(); !iter.done(); iter.next()) {
      auto* record = &iter->as<FinalizationRecordObject>();
      MOZ_ASSERT(record->isInRecordMap());
      auto result =
          TraceManuallyBarrieredWeakEdge(trc, &record, "FinalizationRecord");
      if (result.isDead()) {
        record = result.initialTarget();
        record->setInRecordMap(false);
        record->unlink();
      }
    }

    // Queue remaining finalization records if the target is dying.
    if (!TraceWeakEdge(trc, &iter.get().mutableKey(),
                       "FinalizationRecord target")) {
      for (auto iter = records.iter(); !iter.done(); iter.next()) {
        auto* record = &iter->as<FinalizationRecordObject>();
        record->setInRecordMap(false);
        record->unlink();

        // Move the record to the queue object. In theory this requires a read
        // barrier since the record pointer is weak. However we have may have
        // finished marking the record's zone at this point so this is not
        // possible. Instead note that the record will be marked if the registry
        // is alive. If not then we clear any queued records when we discover
        // the the registry is dead above.
        FinalizationQueueObject* queue = record->queue();
        queue->queueRecordToBeCleanedUp(record);

        if (shouldQueueFinalizationRegistryForCleanup(queue)) {
          gc->queueFinalizationRegistryForCleanup(queue);
        }
      }
      iter.remove();
    } else {
      CheckTargetValue(iter.get().key());
    }
  }
}

bool FinalizationObservers::shouldQueueFinalizationRegistryForCleanup(
    FinalizationQueueObject* queue) {
  // FinalizationRegistries and their targets may be in different zones and
  // therefore swept at different times during GC. If a target is observed to
  // die but the registry's zone has not yet been swept then we don't whether we
  // need to queue the registry for cleanup callbacks, as the registry itself
  // might be dead.
  //
  // In this case we defer queuing the registry and this happens when the
  // registry is swept.
  MOZ_ASSERT(queue->hasRegistry());
  Zone* zone = queue->zone();
  return !zone->wasGCStarted() || zone->gcState() >= Zone::Sweep;
}

void GCRuntime::queueFinalizationRegistryForCleanup(
    FinalizationQueueObject* queue) {
  // Prod the embedding to call us back later to run the finalization callbacks,
  // if necessary.

  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(queue));
  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(queue->doCleanupFunction()));
  if (queue->isQueuedForCleanup()) {
    return;
  }

  JSObject* incumbentGlobal = nullptr;

  if (JSObject* wrapped = queue->getIncumbentGlobalRepresentative()) {
    JSObject* unwrappedIncumbentGlobalRepresentative =
        UncheckedUnwrapWithoutExpose(wrapped);
    MOZ_ASSERT(unwrappedIncumbentGlobalRepresentative);
    // If the incumbentGlobal object becomes a dead wrapper here, the target
    // global has already gone, and the finalization callback won't do anything
    // to it anyway.
    if (JS_IsDeadWrapper(unwrappedIncumbentGlobalRepresentative)) {
      return;
    }
    incumbentGlobal = &unwrappedIncumbentGlobalRepresentative->nonCCWGlobal();
  }

  callHostCleanupFinalizationRegistryCallback(queue->doCleanupFunction(),
                                              incumbentGlobal);

  // The queue object may be gray, and that's OK.
  AutoTouchingGrayThings atgt;

  queue->setQueuedForCleanup(true);
}

// Register |target| such that when it dies |weakRef| will have its pointer to
// |target| cleared.
bool GCRuntime::registerWeakRef(JSContext* cx, HandleValue target,
                                Handle<WeakRefObject*> weakRef) {
  CheckTargetValue(target);

  Zone* zone = GetWeakTargetZone(target);
  if (!zone->ensureFinalizationObservers() ||
      !zone->finalizationObservers()->addWeakRefTarget(target, weakRef)) {
    ReportOutOfMemory(cx);
    return false;
  }

  return true;
}

bool FinalizationObservers::addWeakRefTarget(HandleValue target,
                                             Handle<WeakRefObject*> weakRef) {
  auto ptr = weakRefMap.lookupForAdd(target);
  if (!ptr && !weakRefMap.relookupOrAdd(ptr, target, ObserverList())) {
    return false;
  }

  ptr->value().insertFront(weakRef);
  return true;
}

void FinalizationObservers::removeWeakRefTarget(
    Handle<Value> target, Handle<WeakRefObject*> weakRef) {
  CheckTargetValue(target);
  MOZ_ASSERT(weakRef->target() == target);

  MOZ_ASSERT(weakRef->isInList());
  weakRef->clearTargetAndUnlink();

  auto ptr = weakRefMap.lookup(target);
  MOZ_ASSERT(ptr);
  ObserverList& list = ptr->value();
  if (list.isEmpty()) {
    weakRefMap.remove(ptr);
  }
}

void FinalizationObservers::traceWeakWeakRefEdges(JSTracer* trc) {
  for (auto iter = weakRefMap.modIter(); !iter.done(); iter.next()) {
    ObserverList& weakRefs = iter.get().value();
    auto result =
        TraceWeakEdge(trc, &iter.get().mutableKey(), "WeakRef target");

    if (result.isDead()) {
      // Clear the observer list if the target is dying.
      while (!weakRefs.isEmpty()) {
        auto* weakRef = &weakRefs.getFirst()->as<WeakRefObject>();
        weakRef->clearTargetAndUnlink();
      }
      iter.remove();
    } else {
      Value target = result.finalTarget();
      CheckTargetValue(target);

      // Update WeakRef targets if the target has been moved.
      if (target != result.initialTarget()) {
        traceWeakWeakRefList(trc, weakRefs, target);
      }
    }
  }
}

void FinalizationObservers::traceWeakWeakRefList(JSTracer* trc,
                                                 ObserverList& weakRefs,
                                                 Value target) {
  MOZ_ASSERT(!IsForwarded(target.toGCThing()));

  for (auto iter = weakRefs.iter(); !iter.done(); iter.next()) {
    auto* weakRef = &iter.get()->as<WeakRefObject>();
    MOZ_ASSERT(!IsForwarded(weakRef));
    if (weakRef->target() != target) {
      MOZ_ASSERT(MaybeForwarded(weakRef->target().toGCThing()) ==
                 target.toGCThing());
      weakRef->setTargetUnbarriered(target);
    }
  }
}

JS_PUBLIC_API void JS::MaybeClearWeakRefTargets(
    JSRuntime* runtime, JS::ShouldClearWeakRefTargetCallback callback,
    void* data) {
  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
  AssertHeapIsIdle();
  runtime->gc.maybeClearWeakRefTargets(callback, data);
}

void GCRuntime::maybeClearWeakRefTargets(
    JS::ShouldClearWeakRefTargetCallback callback, void* data) {
  for (AllZonesIter zone(this); !zone.done(); zone.next()) {
    FinalizationObservers* observers = zone->finalizationObservers();
    if (observers) {
      observers->maybeClearWeakRefTargets(callback, data);
    }
  }
}

void FinalizationObservers::maybeClearWeakRefTargets(
    JS::ShouldClearWeakRefTargetCallback callback, void* data) {
  for (auto iter = weakRefMap.modIter(); !iter.done(); iter.next()) {
    Value target = iter.get().key();
    if (callback(target.toGCCellPtr(), data)) {
      ObserverList& weakRefs = iter.get().value();
      while (!weakRefs.isEmpty()) {
        auto* weakRef = &weakRefs.getFirst()->as<WeakRefObject>();
        weakRef->clearTargetAndUnlink();
      }
      iter.remove();
    }
  }
}

bool FinalizationObservers::isTarget(const Value& target) {
  return weakRefMap.has(target) || recordMap.has(target);
}

/* static */
bool GCRuntime::isFinalizationObserverTarget(const Value& target) {
  Zone* zone = GetWeakTargetZone(target);
  FinalizationObservers* observers = zone->finalizationObservers();
  return observers && observers->isTarget(target);
}

bool GCRuntime::relocateFinalizationObserverTarget(const Value& oldTarget,
                                                   const Value& newTarget) {
  CheckTargetValue(oldTarget);
  CheckTargetValue(newTarget);
  MOZ_ASSERT(oldTarget != newTarget);

  Zone* oldZone = GetWeakTargetZone(oldTarget);
  FinalizationObservers* oldObservers = oldZone->finalizationObservers();
  if (!oldObservers) {
    return true;
  }

  ObserverList weakRefList = oldObservers->extractWeakRefObservers(oldTarget);
  ObserverList recordList = oldObservers->extractRecordObservers(oldTarget);
  if (weakRefList.isEmpty() && recordList.isEmpty()) {
    return true;
  }

  // Update the target stored in each WeakRefObject.
  for (auto iter = weakRefList.iter(); !iter.done(); iter.next()) {
    auto* weakRef = &iter.get()->as<WeakRefObject>();
    MOZ_ASSERT(weakRef->target() == oldTarget);
    weakRef->setTarget(newTarget);
    // The post barrier may add a store buffer entry but since these objects are
    // weakly held we don't know if they will survive GC. Therefore we need to
    // set a flag to ensure we clear out the nursery before doing any sweeping.
    if (weakRef->zone()->wasGCStarted() && weakRef->isTenured() &&
        !newTarget.toGCThing()->isTenured()) {
      storeBuffer().setMayHavePointersToDeadCells();
    }
  }

  Zone* newZone = GetWeakTargetZone(newTarget);
  if (!newZone->ensureFinalizationObservers()) {
    return false;
  }

  FinalizationObservers* newObservers = newZone->finalizationObservers();
  if (!weakRefList.isEmpty() &&
      !newObservers->addWeakRefObservers(newTarget, std::move(weakRefList))) {
    return false;
  }

  if (!recordList.isEmpty() &&
      !newObservers->addRecordObservers(newTarget, std::move(recordList))) {
    return false;
  }

  return true;
}

ObserverList FinalizationObservers::extractWeakRefObservers(
    const Value& target) {
  ObserverList list;
  if (auto ptr = weakRefMap.lookup(target)) {
    list = std::move(ptr->value());
    weakRefMap.remove(ptr);
  }

  return list;
}

bool FinalizationObservers::addWeakRefObservers(const Value& target,
                                                ObserverList&& list) {
  auto ptr = weakRefMap.lookupForAdd(target);
  if (!ptr && !weakRefMap.add(ptr, target, ObserverList())) {
    return false;
  }

  ptr->value().append(std::move(list));
  return true;
}

ObserverList FinalizationObservers::extractRecordObservers(
    const Value& target) {
  ObserverList list;
  if (auto ptr = recordMap.lookup(target)) {
    list = std::move(ptr->value());
    recordMap.remove(ptr);
  }

  return list;
}

bool FinalizationObservers::addRecordObservers(const Value& target,
                                               ObserverList&& list) {
  auto ptr = recordMap.lookupForAdd(target);
  if (!ptr && !recordMap.add(ptr, target, ObserverList())) {
    return false;
  }

  ptr->value().append(std::move(list));
  return true;
}

/* static */
void GCRuntime::clearWeakRefTargets(Compartment* source, const Value& target) {
  Zone* zone = target.toGCThing()->zone();
  FinalizationObservers* observers = zone->finalizationObservers();
  if (observers) {
    observers->clearWeakRefTargets(source, target);
  }
}

void FinalizationObservers::clearWeakRefTargets(Compartment* source,
                                                const Value& target) {
  if (auto ptr = weakRefMap.lookup(target)) {
    ObserverList& weakRefs = ptr->value();
    for (auto iter = weakRefs.iter(); !iter.done(); iter.next()) {
      auto* weakRef = &iter->as<WeakRefObject>();
      if (weakRef->compartment() == source) {
        weakRef->clearTargetAndUnlink();
      }
    }
    if (weakRefs.isEmpty()) {
      weakRefMap.remove(ptr);
    }
  }
}

/* static */
void GCRuntime::clearWeakRefTargets(const CompartmentFilter& sourceFilter,
                                    JS::Realm* targetFilter) {
  Zone* zone = targetFilter->zone();
  FinalizationObservers* observers = zone->finalizationObservers();
  if (observers) {
    observers->clearWeakRefTargets(sourceFilter, targetFilter);
  }
}

void FinalizationObservers::clearWeakRefTargets(
    const CompartmentFilter& sourceFilter, JS::Realm* targetFilter) {
  for (auto mapIter = weakRefMap.modIter(); !mapIter.done(); mapIter.next()) {
    Value target = mapIter.get().key();
    if (target.isObject() && target.toObject().nonCCWRealm() == targetFilter) {
      ObserverList& weakRefs = mapIter.get().value();
      for (auto iter = weakRefs.iter(); !iter.done(); iter.next()) {
        auto* weakRef = &iter->as<WeakRefObject>();
        if (sourceFilter.match(weakRef->compartment())) {
          weakRef->clearTargetAndUnlink();
        }
      }
      if (weakRefs.isEmpty()) {
        mapIter.remove();
      }
    }
  }
}

Messung V0.5 in Prozent
C=92 H=88 G=89

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002