Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/gc/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  Zone.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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/. */


#include "gc/Zone.h"
#include "js/shadow/Zone.h"  // JS::shadow::Zone

#include "mozilla/Sprintf.h"
#include "mozilla/TimeStamp.h"

#include <type_traits>

#include "gc/FinalizationObservers.h"
#include "gc/GCContext.h"
#include "gc/PublicIterators.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/Invalidation.h"
#include "jit/JitScript.h"
#include "jit/JitZone.h"
#include "vm/Runtime.h"
#include "vm/Time.h"

#include "debugger/DebugAPI-inl.h"
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "gc/Nursery-inl.h"
#include "gc/StableCellHasher-inl.h"
#include "gc/WeakMap-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/Realm-inl.h"

using namespace js;
using namespace js::gc;

Zone* const Zone::NotOnList = reinterpret_cast<Zone*>(1);

ZoneAllocator::ZoneAllocator(JSRuntime* rt, Kind kind)
    : JS::shadow::Zone(rt, rt->gc.marker().tracer(), kind),
      jitHeapThreshold(size_t(jit::MaxCodeBytesPerProcess * 0.8)) {}

ZoneAllocator::~ZoneAllocator() {
#ifdef DEBUG
  mallocTracker.checkEmptyOnDestroy();
  MOZ_ASSERT(gcHeapSize.bytes() == 0);
  MOZ_ASSERT(mallocHeapSize.bytes() == 0);
  MOZ_ASSERT(jitHeapSize.bytes() == 0);
#endif
}

void ZoneAllocator::fixupAfterMovingGC() {
#ifdef DEBUG
  mallocTracker.fixupAfterMovingGC();
#endif
}

void js::ZoneAllocator::updateSchedulingStateOnGCStart() {
  gcHeapSize.updateOnGCStart();
  mallocHeapSize.updateOnGCStart();
  jitHeapSize.updateOnGCStart();
  perZoneGCTime = mozilla::TimeDuration::Zero();
}

void js::ZoneAllocator::updateGCStartThresholds(GCRuntime& gc) {
  bool isAtomsZone = JS::Zone::from(this)->isAtomsZone();
  gcHeapThreshold.updateStartThreshold(
      gcHeapSize.retainedBytes(), smoothedAllocationRate.ref(),
      smoothedCollectionRate.ref(), gc.tunables, gc.schedulingState,
      isAtomsZone);

  mallocHeapThreshold.updateStartThreshold(mallocHeapSize.retainedBytes(),
                                           gc.tunables, gc.schedulingState);
}

void js::ZoneAllocator::setGCSliceThresholds(GCRuntime& gc,
                                             bool waitingOnBGTask) {
  gcHeapThreshold.setSliceThreshold(this, gcHeapSize, gc.tunables,
                                    waitingOnBGTask);
  mallocHeapThreshold.setSliceThreshold(this, mallocHeapSize, gc.tunables,
                                        waitingOnBGTask);
  jitHeapThreshold.setSliceThreshold(this, jitHeapSize, gc.tunables,
                                     waitingOnBGTask);
}

void js::ZoneAllocator::clearGCSliceThresholds() {
  gcHeapThreshold.clearSliceThreshold();
  mallocHeapThreshold.clearSliceThreshold();
  jitHeapThreshold.clearSliceThreshold();
}

bool ZoneAllocator::addSharedMemory(void* mem, size_t nbytes, MemoryUse use) {
  // nbytes can be zero here for SharedArrayBuffers.

  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));

  auto ptr = sharedMemoryUseCounts.lookupForAdd(mem);
  MOZ_ASSERT_IF(ptr, ptr->value().use == use);

  if (!ptr && !sharedMemoryUseCounts.add(ptr, mem, gc::SharedMemoryUse(use))) {
    return false;
  }

  ptr->value().count++;

  // Allocations can grow, so add any increase over the previous size and record
  // the new size.
  if (nbytes > ptr->value().nbytes) {
    mallocHeapSize.addBytes(nbytes - ptr->value().nbytes);
    ptr->value().nbytes = nbytes;
  }

  maybeTriggerGCOnMalloc();

  return true;
}

void ZoneAllocator::removeSharedMemory(void* mem, size_t nbytes,
                                       MemoryUse use) {
  // nbytes can be zero here for SharedArrayBuffers.

  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
  MOZ_ASSERT(CurrentThreadIsGCFinalizing());

  auto ptr = sharedMemoryUseCounts.lookup(mem);

  MOZ_ASSERT(ptr);
  MOZ_ASSERT(ptr->value().count != 0);
  MOZ_ASSERT(ptr->value().use == use);
  MOZ_ASSERT(ptr->value().nbytes >= nbytes);

  ptr->value().count--;
  if (ptr->value().count == 0) {
    mallocHeapSize.removeBytes(ptr->value().nbytes, true);
    sharedMemoryUseCounts.remove(ptr);
  }
}

template <TrackingKind kind>
void js::TrackedAllocPolicy<kind>::decMemory(size_t nbytes) {
  bool updateRetainedSize = false;
  if constexpr (kind == TrackingKind::Cell) {
    // Only subtract freed cell memory from retained size for cell associations
    // during sweeping.
    JS::GCContext* gcx = TlsGCContext.get();
    updateRetainedSize = gcx->isFinalizing();
  }

  zone_->decNonGCMemory(this, nbytes, MemoryUse::TrackedAllocPolicy,
                        updateRetainedSize);
}

namespace js {
template class TrackedAllocPolicy<TrackingKind::Zone>;
template class TrackedAllocPolicy<TrackingKind::Cell>;
}  // namespace js

JS::Zone::Zone(JSRuntime* rt, Kind kind)
    : ZoneAllocator(rt, kind),
      arenas(this),
      bufferAllocator(this),
      data(nullptr),
      suppressAllocationMetadataBuilder(false),
      allocNurseryObjects_(true),
      allocNurseryStrings_(true),
      allocNurseryBigInts_(true),
      pretenuring(this),
      crossZoneStringWrappers_(this),
      shapeZone_(this),
      gcScheduled_(false),
      gcScheduledSaved_(false),
      gcPreserveCode_(false),
      keepPropMapTables_(false),
      wasCollected_(false),
      listNext_(NotOnList),
      keptObjects(this) {
  /* Ensure that there are no vtables to mess us up here. */
  MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
             static_cast<JS::shadow::Zone*>(this));
  MOZ_ASSERT_IF(isAtomsZone(), rt->gc.zones().empty());

  updateGCStartThresholds(rt->gc);
  rt->gc.nursery().setAllocFlagsForZone(this);
}

Zone::~Zone() {
  MOZ_ASSERT_IF(regExps_.ref(), regExps().empty());

  MOZ_ASSERT(numRealmsWithAllocMetadataBuilder_ == 0);

  DebugAPI::deleteDebugScriptMap(debugScriptMap);
  js_delete(finalizationObservers_.ref().release());

  MOZ_ASSERT(gcWeakMapList().isEmpty());
  MOZ_ASSERT(objectsWithWeakPointers.ref().empty());

  JSRuntime* rt = runtimeFromAnyThread();
  if (this == rt->gc.systemZone) {
    MOZ_ASSERT(isSystemZone());
    rt->gc.systemZone = nullptr;
  }

  js_delete(jitZone_.ref());
}

bool Zone::init() {
  regExps_.ref() = make_unique<RegExpZone>(this);
  return !!regExps_.ref();
}

void Zone::setNeedsIncrementalBarrier(bool needs) {
  needsIncrementalBarrier_ = needs;
}

void Zone::changeGCState(GCState prev, GCState next) {
  MOZ_ASSERT(RuntimeHeapIsBusy());
  MOZ_ASSERT(gcState() == prev);

  // This can be called when barriers have been temporarily disabled by
  // AutoDisableBarriers. In that case, don't update needsIncrementalBarrier_
  // and barriers will be re-enabled by ~AutoDisableBarriers() if necessary.
  bool barriersDisabled = isGCMarking() && !needsIncrementalBarrier();

  gcState_ = next;

  // Update the barriers state when we transition between marking and
  // non-marking states, unless barriers have been disabled.
  if (!barriersDisabled) {
    needsIncrementalBarrier_ = isGCMarking();
  }
}

template <class Pred>
static void EraseIf(js::gc::EphemeronEdgeVector& entries, Pred pred) {
  auto* begin = entries.begin();
  autoconst end = entries.end();

  auto* newEnd = begin;
  for (auto* p = begin; p != end; p++) {
    if (!pred(*p)) {
      *newEnd++ = *p;
    }
  }

  size_t removed = end - newEnd;
  entries.shrinkBy(removed);
}

static void SweepEphemeronEdgesWhileMinorSweeping(
    js::gc::EphemeronEdgeVector& entries) {
  EraseIf(entries, [](js::gc::EphemeronEdge& edge) -> bool {
    return IsAboutToBeFinalizedDuringMinorSweep(&edge.target);
  });
}

void Zone::sweepAfterMinorGC(JSTracer* trc) {
  sweepEphemeronTablesAfterMinorGC();
  crossZoneStringWrappers().sweepAfterMinorGC(trc);

  for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
    comp->sweepAfterMinorGC(trc);
  }
}

void Zone::sweepEphemeronTablesAfterMinorGC() {
  for (auto r = gcNurseryEphemeronEdges().all(); !r.empty(); r.popFront()) {
    // Sweep gcNurseryEphemeronEdges to move live (forwarded) keys to
    // gcEphemeronEdges, scanning through all the entries for such keys to
    // update them.
    //
    // Forwarded and dead keys may also appear in their delegates' entries,
    // so sweep those too (see below.)

    // The tricky case is when the key has a delegate that was already
    // tenured. Then it will be in its compartment's gcEphemeronEdges, but we
    // still need to update the key (which will be in the entries
    // associated with it.)
    gc::Cell* key = r.front().key();
    MOZ_ASSERT(!key->isTenured());
    if (!Nursery::getForwardedPointer(&key)) {
      // Dead nursery cell => discard.
      continue;
    }

    // Key been moved. The value is an array of <color,cell> pairs; update all
    // cells in that array.
    EphemeronEdgeVector& entries = r.front().value();
    SweepEphemeronEdgesWhileMinorSweeping(entries);

    // Live (moved) nursery cell. Append entries to gcEphemeronEdges.
    EphemeronEdgeTable& tenuredEdges = gcEphemeronEdges();
    AutoEnterOOMUnsafeRegion oomUnsafe;
    auto entry = tenuredEdges.lookupForAdd(key);
    if (!entry) {
      if (!tenuredEdges.add(entry, key, EphemeronEdgeVector())) {
        oomUnsafe.crash("Failed to tenure weak keys entry");
      }
    }
    if (!entry->value().appendAll(entries)) {
      oomUnsafe.crash("Failed to tenure weak keys entry");
    }

    // If the key has a delegate, then it will map to a WeakKeyEntryVector
    // containing the key that needs to be updated.

    JSObject* delegate = gc::detail::GetDelegate(key->as<JSObject>());
    if (!delegate) {
      continue;
    }
    MOZ_ASSERT(delegate->isTenured());

    // If delegate was formerly nursery-allocated, we will sweep its entries
    // when we visit its gcNurseryEphemeronEdges (if we haven't already). Note
    // that we don't know the nursery address of the delegate, since the
    // location it was stored in has already been updated.
    //
    // Otherwise, it will be in gcEphemeronEdges and we sweep it here.
    auto p = delegate->zone()->gcEphemeronEdges().lookup(delegate);
    if (p) {
      SweepEphemeronEdgesWhileMinorSweeping(p->value());
    }
  }

  gcNurseryEphemeronEdges().clearAndCompact();
}

void Zone::traceWeakCCWEdges(JSTracer* trc) {
  crossZoneStringWrappers().traceWeak(trc);
  for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
    comp->traceCrossCompartmentObjectWrapperEdges(trc);
  }
}

/* static */
void Zone::fixupAllCrossCompartmentWrappersAfterMovingGC(JSTracer* trc) {
  MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());

  for (ZonesIter zone(trc->runtime(), WithAtoms); !zone.done(); zone.next()) {
    // Trace the wrapper map to update keys (wrapped values) in other
    // compartments that may have been moved.
    zone->crossZoneStringWrappers().traceWeak(trc);

    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
      comp->fixupCrossCompartmentObjectWrappersAfterMovingGC(trc);
    }
  }
}

void Zone::dropStringWrappersOnGC() {
  MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
  crossZoneStringWrappers().clear();
}

#ifdef JSGC_HASH_TABLE_CHECKS

void Zone::checkAllCrossCompartmentWrappersAfterMovingGC() {
  checkStringWrappersAfterMovingGC();
  for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
    comp->checkObjectWrappersAfterMovingGC();
  }
}

void Zone::checkStringWrappersAfterMovingGC() {
  CheckTableAfterMovingGC(crossZoneStringWrappers(), [this](const auto& entry) {
    JSString* key = entry.key().get();
    CheckGCThingAfterMovingGC(key);  // Keys may be in a different zone.
    CheckGCThingAfterMovingGC(entry.value().unbarrieredGet(), this);
    return key;
  });
}
#endif

void Zone::maybeDiscardJitCode(JS::GCContext* gcx) {
  if (!isPreservingCode()) {
    forceDiscardJitCode(gcx);
  }
}

void Zone::forceDiscardJitCode(JS::GCContext* gcx,
                               const JitDiscardOptions& options) {
  if (!jitZone()) {
    return;
  }

  if (options.discardJitScripts) {
    lastDiscardedCodeTime_ = mozilla::TimeStamp::Now();
  }

  // Copy Baseline IC stubs that are active on the stack to a new LifoAlloc.
  // After freeing stub memory, these chunks are then transferred to the
  // zone-wide allocator.
  jit::ICStubSpace newStubSpace;

#ifdef DEBUG
  // Assert no ICScripts are marked as active.
  jitZone()->forEachJitScript([](jit::JitScript* jitScript) {
    MOZ_ASSERT(!jitScript->hasActiveICScript());
  });
#endif

  // Mark ICScripts on the stack as active and copy active Baseline stubs.
  jit::MarkActiveICScriptsAndCopyStubs(this, newStubSpace);

  // Invalidate all Ion code in this zone.
  jit::InvalidateAll(gcx, this);

  jitZone()->forEachJitScript<jit::IncludeDyingScripts>(
      [&](jit::JitScript* jitScript) {
        JSScript* script = jitScript->owningScript();
        jit::FinishInvalidation(gcx, script);

        // Discard baseline script if it's not marked as active.
        if (jitScript->hasBaselineScript() &&
            !jitScript->icScript()->active()) {
          jit::FinishDiscardBaselineScript(gcx, script);
        }

#ifdef JS_CACHEIR_SPEW
        maybeUpdateWarmUpCount(script);
#endif

        // Warm-up counter for scripts are reset on GC. After discarding code we
        // need to let it warm back up to get information such as which
        // opcodes are setting array holes or accessing getter properties.
        script->resetWarmUpCounterForGC();

        // Try to release the script's JitScript. This should happen after
        // releasing JIT code because we can't do this when the script still has
        // JIT code.
        if (options.discardJitScripts) {
          script->maybeReleaseJitScript(gcx);
          jitScript = script->maybeJitScript();
          if (!jitScript) {
            // If we successfully discarded the JIT script, try to discard the
            // ScriptCounts too.
            if (!script->realm()->collectCoverageForDebug() &&
                !gcx->runtime()->profilingScripts) {
              script->destroyScriptCounts();
            }
            return;  // Continue script loop.
          }
        }

        // If we did not release the JitScript, we need to purge IC stubs
        // because the ICStubSpace will be purged below. Also purge all
        // trial-inlined ICScripts that are not active on the stack.
        jitScript->purgeInactiveICScripts();
        jitScript->purgeStubs(script, newStubSpace);

        if (options.resetNurseryAllocSites ||
            options.resetPretenuredAllocSites) {
          jitScript->resetAllocSites(options.resetNurseryAllocSites,
                                     options.resetPretenuredAllocSites);
        }

        // Reset the active flag of each ICScript.
        jitScript->resetAllActiveFlags();
      });

  // Also clear references to jit code from RegExpShared cells at this point.
  // This avoid holding onto ExecutablePools.
  for (auto regExp = cellIterUnsafe<RegExpShared>(); !regExp.done();
       regExp.next()) {
    regExp->discardJitCode();
  }

  /*
   * When scripts contain pointers to nursery things, the store buffer
   * can contain entries that point into the optimized stub space. Since
   * this method can be called outside the context of a GC, this situation
   * could result in us trying to mark invalid store buffer entries.
   *
   * Defer freeing any allocated blocks until after the next minor GC.
   */

  jitZone()->stubSpace()->freeAllAfterMinorGC(this);
  jitZone()->stubSpace()->transferFrom(newStubSpace);
  jitZone()->purgeIonCacheIRStubInfo();

  // Generate a profile marker
  if (gcx->runtime()->geckoProfiler().enabled()) {
    char discardingJitScript = options.discardJitScripts ? 'Y' : 'N';
    char discardingBaseline = 'Y';
    char discardingIon = 'Y';

    char discardingRegExp = 'Y';
    char discardingNurserySites = options.resetNurseryAllocSites ? 'Y' : 'N';
    char discardingPretenuredSites =
        options.resetPretenuredAllocSites ? 'Y' : 'N';

    char buf[100];
    SprintfLiteral(buf,
                   "JitScript:%c Baseline:%c Ion:%c "
                   "RegExp:%c NurserySites:%c PretenuredSites:%c",
                   discardingJitScript, discardingBaseline, discardingIon,
                   discardingRegExp, discardingNurserySites,
                   discardingPretenuredSites);
    gcx->runtime()->geckoProfiler().markEvent("DiscardJit", buf);
  }
}

void JS::Zone::resetAllocSitesAndInvalidate(bool resetNurserySites,
                                            bool resetPretenuredSites) {
  MOZ_ASSERT(resetNurserySites || resetPretenuredSites);

  if (!jitZone()) {
    return;
  }

  JSContext* cx = runtime_->mainContextFromOwnThread();
  jitZone()->forEachJitScript<jit::IncludeDyingScripts>(
      [&](jit::JitScript* jitScript) {
        if (jitScript->resetAllocSites(resetNurserySites,
                                       resetPretenuredSites)) {
          JSScript* script = jitScript->owningScript();
          CancelOffThreadIonCompile(script);
          if (script->hasIonScript()) {
            jit::Invalidate(cx, script,
                            /* resetUses = */ true,
                            /* cancelOffThread = */ true);
          }
        }
      });
}

void JS::Zone::traceWeakJitScripts(JSTracer* trc) {
  if (jitZone()) {
    jitZone()->forEachJitScript(
        [&](jit::JitScript* jitScript) { jitScript->traceWeak(trc); });
  }
}

void JS::Zone::beforeClearDelegateInternal(JSObject* wrapper,
                                           JSObject* delegate) {
  // 'delegate' is no longer the delegate of 'wrapper'.
  MOZ_ASSERT(js::gc::detail::GetDelegate(wrapper) == delegate);
  MOZ_ASSERT(needsIncrementalBarrier());
  MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(this));

  // If |wrapper| might be a key in a weak map, trigger a barrier to account for
  // the removal of the automatically added edge from delegate to wrapper.
  if (HasUniqueId(wrapper)) {
    PreWriteBarrier(wrapper);
  }
}

#ifdef JSGC_HASH_TABLE_CHECKS
void JS::Zone::checkUniqueIdTableAfterMovingGC() {
  CheckTableAfterMovingGC(uniqueIds(), [this](const auto& entry) {
    js::gc::CheckGCThingAfterMovingGC(entry.key(), this);
    return entry.key();
  });
}
#endif

js::jit::JitZone* Zone::createJitZone(JSContext* cx) {
  MOZ_ASSERT(!jitZone_);
#ifndef ENABLE_PORTABLE_BASELINE_INTERP
  MOZ_ASSERT(cx->runtime()->hasJitRuntime());
#endif

  auto jitZone = cx->make_unique<jit::JitZone>(cx, allocNurseryStrings());
  if (!jitZone) {
    return nullptr;
  }

  jitZone_ = jitZone.release();
  return jitZone_;
}

bool Zone::hasMarkedRealms() {
  for (RealmsInZoneIter realm(this); !realm.done(); realm.next()) {
    if (realm->marked()) {
      return true;
    }
  }
  return false;
}

void Zone::notifyObservingDebuggers() {
  AutoAssertNoGC nogc;
  MOZ_ASSERT(JS::RuntimeHeapIsCollecting(),
             "This method should be called during GC.");

  JSRuntime* rt = runtimeFromMainThread();

  for (RealmsInZoneIter realms(this); !realms.done(); realms.next()) {
    GlobalObject* global = realms->unsafeUnbarrieredMaybeGlobal();
    if (!global) {
      continue;
    }

    DebugAPI::notifyParticipatesInGC(global, rt->gc.majorGCCount());
  }
}

bool Zone::isOnList() const { return listNext_ != NotOnList; }

Zone* Zone::nextZone() const {
  MOZ_ASSERT(isOnList());
  return listNext_;
}

void Zone::fixupAfterMovingGC() {
  ZoneAllocator::fixupAfterMovingGC();
  shapeZone().fixupPropMapShapeTableAfterMovingGC();
}

void Zone::purgeAtomCache() {
  atomCache_.ref().reset();

  // Also purge the dtoa caches so that subsequent lookups populate atom
  // cache too.
  for (RealmsInZoneIter r(this); !r.done(); r.next()) {
    r->dtoaCache.purge();
  }
}

void Zone::addSizeOfIncludingThis(
    mozilla::MallocSizeOf mallocSizeOf, size_t* zoneObject, JS::CodeSizes* code,
    size_t* regexpZone, size_t* jitZone, size_t* cacheIRStubs,
    size_t* uniqueIdMap, size_t* initialPropMapTable, size_t* shapeTables,
    size_t* atomsMarkBitmaps, size_t* compartmentObjects,
    size_t* crossCompartmentWrappersTables, size_t* compartmentsPrivateData,
    size_t* scriptCountsMapArg) {
  *zoneObject += mallocSizeOf(this);
  *regexpZone += regExps().sizeOfIncludingThis(mallocSizeOf);
  if (jitZone_) {
    jitZone_->addSizeOfIncludingThis(mallocSizeOf, code, jitZone, cacheIRStubs);
  }
  *uniqueIdMap += uniqueIds().shallowSizeOfExcludingThis(mallocSizeOf);
  shapeZone().addSizeOfExcludingThis(mallocSizeOf, initialPropMapTable,
                                     shapeTables);
  *atomsMarkBitmaps += markedAtoms().sizeOfExcludingThis(mallocSizeOf);
  *crossCompartmentWrappersTables +=
      crossZoneStringWrappers().sizeOfExcludingThis(mallocSizeOf);

  for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
    comp->addSizeOfIncludingThis(mallocSizeOf, compartmentObjects,
                                 crossCompartmentWrappersTables,
                                 compartmentsPrivateData);
  }

  if (scriptCountsMap) {
    *scriptCountsMapArg +=
        scriptCountsMap->shallowSizeOfIncludingThis(mallocSizeOf);
    for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
      *scriptCountsMapArg +=
          r.front().value()->sizeOfIncludingThis(mallocSizeOf);
    }
  }
}

void* ZoneAllocator::onOutOfMemory(js::AllocFunction allocFunc,
                                   arena_id_t arena, size_t nbytes,
                                   void* reallocPtr) {
  if (!js::CurrentThreadCanAccessRuntime(runtime_)) {
    return nullptr;
  }
  // The analysis sees that JSRuntime::onOutOfMemory could report an error,
  // which with a JSErrorInterceptor could GC. But we're passing a null cx (to
  // a default parameter) so the error will not be reported.
  JS::AutoSuppressGCAnalysis suppress;
  return runtimeFromMainThread()->onOutOfMemory(allocFunc, arena, nbytes,
                                                reallocPtr);
}

void ZoneAllocator::reportAllocationOverflow() const {
  js::ReportAllocationOverflow(static_cast<JSContext*>(nullptr));
}

ZoneList::ZoneList() : head(nullptr), tail(nullptr) {}

ZoneList::ZoneList(Zone* zone) : head(zone), tail(zone) {
  MOZ_RELEASE_ASSERT(!zone->isOnList());
  zone->listNext_ = nullptr;
}

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

void ZoneList::check() const {
#ifdef DEBUG
  MOZ_ASSERT((head == nullptr) == (tail == nullptr));
  if (!head) {
    return;
  }

  Zone* zone = head;
  for (;;) {
    MOZ_ASSERT(zone && zone->isOnList());
    if (zone == tail) break;
    zone = zone->listNext_;
  }
  MOZ_ASSERT(!zone->listNext_);
#endif
}

bool ZoneList::isEmpty() const { return head == nullptr; }

Zone* ZoneList::front() const {
  MOZ_ASSERT(!isEmpty());
  MOZ_ASSERT(head->isOnList());
  return head;
}

void ZoneList::prepend(Zone* zone) { prependList(ZoneList(zone)); }

void ZoneList::append(Zone* zone) { appendList(ZoneList(zone)); }

void ZoneList::prependList(ZoneList&& other) {
  check();
  other.check();

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

  MOZ_ASSERT(tail != other.tail);

  if (!isEmpty()) {
    other.tail->listNext_ = head;
  } else {
    tail = other.tail;
  }
  head = other.head;

  other.head = nullptr;
  other.tail = nullptr;
}

void ZoneList::appendList(ZoneList&& other) {
  check();
  other.check();

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

  MOZ_ASSERT(tail != other.tail);

  if (!isEmpty()) {
    tail->listNext_ = other.head;
  } else {
    head = other.head;
  }
  tail = other.tail;

  other.head = nullptr;
  other.tail = nullptr;
}

Zone* ZoneList::removeFront() {
  MOZ_ASSERT(!isEmpty());
  check();

  Zone* front = head;
  head = head->listNext_;
  if (!head) {
    tail = nullptr;
  }

  front->listNext_ = Zone::NotOnList;

  return front;
}

void ZoneList::clear() {
  while (!isEmpty()) {
    removeFront();
  }
}

JS_PUBLIC_API void JS::shadow::RegisterWeakCache(
    JS::Zone* zone, detail::WeakCacheBase* cachep) {
  zone->registerWeakCache(cachep);
}

void Zone::traceRootsInMajorGC(JSTracer* trc) {
  if (trc->isMarkingTracer() && !isGCMarking()) {
    return;
  }

  // Trace zone script-table roots. See comment below for justification re:
  // calling this only during major (non-nursery) collections.
  traceScriptTableRoots(trc);

  if (FinalizationObservers* observers = finalizationObservers()) {
    observers->traceRoots(trc);
  }
}

void Zone::traceScriptTableRoots(JSTracer* trc) {
  static_assert(std::is_convertible_v<BaseScript*, gc::TenuredCell*>,
                "BaseScript must not be nursery-allocated for script-table "
                "tracing to work");

  // Performance optimization: the script-table keys are JSScripts, which
  // cannot be in the nursery, so we can skip this tracing if we are only in a
  // minor collection. We static-assert this fact above.
  MOZ_ASSERT(!JS::RuntimeHeapIsMinorCollecting());

  // N.B.: the script-table keys are weak *except* in an exceptional case: when
  // then --dump-bytecode command line option or the PCCount JSFriend API is
  // used, then the scripts for all counts must remain alive. We only trace
  // when the `trc->runtime()->profilingScripts` flag is set. This flag is
  // cleared in JSRuntime::destroyRuntime() during shutdown to ensure that
  // scripts are collected before the runtime goes away completely.
  if (scriptCountsMap && trc->runtime()->profilingScripts) {
    for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty();
         r.popFront()) {
      BaseScript* script = r.front().key();
      MOZ_ASSERT(script->hasScriptCounts());
      TraceRoot(trc, &script, "profilingScripts");
    }
  }

  // Trace the debugger's DebugScript weak map.
  if (debugScriptMap) {
    DebugAPI::traceDebugScriptMap(trc, debugScriptMap);
  }
}

void Zone::fixupScriptMapsAfterMovingGC(JSTracer* trc) {
  // Map entries are removed by BaseScript::finalize, but we need to update the
  // script pointers here in case they are moved by the GC.

  if (scriptCountsMap) {
    scriptCountsMap->traceWeak(trc);
  }

  if (scriptLCovMap) {
    scriptLCovMap->traceWeak(trc);
  }

#ifdef MOZ_VTUNE
  if (scriptVTuneIdMap) {
    scriptVTuneIdMap->traceWeak(trc);
  }
#endif

#ifdef JS_CACHEIR_SPEW
  if (scriptFinalWarmUpCountMap) {
    scriptFinalWarmUpCountMap->traceWeak(trc);
  }
#endif
}

#ifdef JSGC_HASH_TABLE_CHECKS
void Zone::checkScriptMapsAfterMovingGC() {
  // |debugScriptMap| is checked automatically because it is s a WeakMap.

  if (scriptCountsMap) {
    CheckTableAfterMovingGC(*scriptCountsMap, [this](const auto& entry) {
      BaseScript* script = entry.key();
      CheckGCThingAfterMovingGC(script, this);
      return script;
    });
  }

  if (scriptLCovMap) {
    CheckTableAfterMovingGC(*scriptLCovMap, [this](const auto& entry) {
      BaseScript* script = entry.key();
      CheckGCThingAfterMovingGC(script, this);
      return script;
    });
  }

#  ifdef MOZ_VTUNE
  if (scriptVTuneIdMap) {
    CheckTableAfterMovingGC(*scriptVTuneIdMap, [this](const auto& entry) {
      BaseScript* script = entry.key();
      CheckGCThingAfterMovingGC(script, this);
      return script;
    });
  }
#  endif  // MOZ_VTUNE

#  ifdef JS_CACHEIR_SPEW
  if (scriptFinalWarmUpCountMap) {
    CheckTableAfterMovingGC(*scriptFinalWarmUpCountMap,
                            [this](const auto& entry) {
                              BaseScript* script = entry.key();
                              CheckGCThingAfterMovingGC(script, this);
                              return script;
                            });
  }
#  endif  // JS_CACHEIR_SPEW
}
#endif

void Zone::clearScriptCounts(Realm* realm) {
  if (!scriptCountsMap) {
    return;
  }

  // Clear all hasScriptCounts_ flags of BaseScript, in order to release all
  // ScriptCounts entries of the given realm.
  for (auto i = scriptCountsMap->modIter(); !i.done(); i.next()) {
    const HeapPtr<BaseScript*>& script = i.get().key();
    if (IsAboutToBeFinalized(script)) {
      // Dead scripts may be present during incremental GC until script
      // finalizers have been run.
      continue;
    }

    if (script->realm() != realm) {
      continue;
    }
    // We can't destroy the ScriptCounts yet if the script has Baseline code,
    // because Baseline code bakes in pointers to the counters. The ScriptCounts
    // will be destroyed in Zone::discardJitCode when discarding the JitScript.
    if (script->hasBaselineScript()) {
      continue;
    }
    script->clearHasScriptCounts();
    i.remove();
  }
}

void Zone::clearScriptLCov(Realm* realm) {
  if (!scriptLCovMap) {
    return;
  }

  for (auto i = scriptLCovMap->modIter(); !i.done(); i.next()) {
    const HeapPtr<BaseScript*>& script = i.get().key();
    if (IsAboutToBeFinalized(script)) {
      // Dead scripts may be present during incremental GC until script
      // finalizers have been run.
      continue;
    }

    if (script->realm() == realm) {
      i.remove();
    }
  }
}

void Zone::clearRootsForShutdownGC() {
  // Finalization callbacks are not called if we're shutting down.
  if (finalizationObservers()) {
    finalizationObservers()->clearRecords();
  }

  clearKeptObjects();
}

void Zone::finishRoots() {
  for (RealmsInZoneIter r(this); !r.done(); r.next()) {
    r->finishRoots();
  }
}

void Zone::traceKeptObjects(JSTracer* trc) { keptObjects.ref().trace(trc); }

bool Zone::addToKeptObjects(HandleObject target) {
  return keptObjects.ref().put(target);
}

void Zone::clearKeptObjects() { keptObjects.ref().clear(); }

bool Zone::ensureFinalizationObservers() {
  if (finalizationObservers_.ref()) {
    return true;
  }

  finalizationObservers_ = js::MakeUnique<FinalizationObservers>(this);
  return bool(finalizationObservers_.ref());
}

bool Zone::registerObjectWithWeakPointers(JSObject* obj) {
  MOZ_ASSERT(obj->getClass()->hasTrace());
  MOZ_ASSERT(!IsInsideNursery(obj));
  return objectsWithWeakPointers.ref().append(obj);
}

Messung V0.5
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.