diff options
Diffstat (limited to 'js/src/vm/Runtime.cpp')
-rw-r--r-- | js/src/vm/Runtime.cpp | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp new file mode 100644 index 000000000..646d48299 --- /dev/null +++ b/js/src/vm/Runtime.cpp @@ -0,0 +1,968 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "mozilla/ArrayUtils.h" +#include "mozilla/Atomics.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ThreadLocal.h" +#include "mozilla/Unused.h" + +#if defined(XP_DARWIN) +#include <mach/mach.h> +#elif defined(XP_UNIX) +#include <sys/resource.h> +#elif defined(XP_WIN) +#include <processthreadsapi.h> +#include <windows.h> +#endif // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN) + +#include <locale.h> +#include <string.h> + +#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES +# include <sys/mman.h> +#endif + +#include "jsatom.h" +#include "jsdtoa.h" +#include "jsgc.h" +#include "jsmath.h" +#include "jsnativestack.h" +#include "jsobj.h" +#include "jsscript.h" +#include "jswatchpoint.h" +#include "jswin.h" +#include "jswrapper.h" + +#include "builtin/Promise.h" +#include "gc/GCInternals.h" +#include "jit/arm/Simulator-arm.h" +#include "jit/arm64/vixl/Simulator-vixl.h" +#include "jit/IonBuilder.h" +#include "jit/JitCompartment.h" +#include "jit/mips32/Simulator-mips32.h" +#include "jit/mips64/Simulator-mips64.h" +#include "jit/PcScriptCache.h" +#include "js/Date.h" +#include "js/MemoryMetrics.h" +#include "js/SliceBudget.h" +#include "vm/Debugger.h" +#include "wasm/WasmSignalHandlers.h" + +#include "jscntxtinlines.h" +#include "jsgcinlines.h" + +using namespace js; +using namespace js::gc; + +using mozilla::Atomic; +using mozilla::DebugOnly; +using mozilla::NegativeInfinity; +using mozilla::PodZero; +using mozilla::PodArrayZero; +using mozilla::PositiveInfinity; +using JS::GenericNaN; +using JS::DoubleNaNValue; + +/* static */ MOZ_THREAD_LOCAL(PerThreadData*) js::TlsPerThreadData; +/* static */ Atomic<size_t> JSRuntime::liveRuntimesCount; + +namespace js { + bool gCanUseExtraThreads = true; +} // namespace js + +void +js::DisableExtraThreads() +{ + gCanUseExtraThreads = false; +} + +const JSSecurityCallbacks js::NullSecurityCallbacks = { }; + +PerThreadData::PerThreadData(JSRuntime* runtime) + : runtime_(runtime) +#ifdef JS_TRACE_LOGGING + , traceLogger(nullptr) +#endif + , autoFlushICache_(nullptr) + , dtoaState(nullptr) + , suppressGC(0) +#ifdef DEBUG + , ionCompiling(false) + , ionCompilingSafeForMinorGC(false) + , performingGC(false) + , gcSweeping(false) +#endif +{} + +PerThreadData::~PerThreadData() +{ + if (dtoaState) + DestroyDtoaState(dtoaState); +} + +bool +PerThreadData::init() +{ + dtoaState = NewDtoaState(); + if (!dtoaState) + return false; + + return true; +} + +static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = { + TransparentObjectWrapper, + nullptr +}; + +static size_t +ReturnZeroSize(const void* p) +{ + return 0; +} + +JSRuntime::JSRuntime(JSRuntime* parentRuntime) + : mainThread(this), + jitTop(nullptr), + jitActivation(nullptr), + jitStackLimit_(0xbad), + jitStackLimitNoInterrupt_(0xbad), +#ifdef DEBUG + ionBailAfter_(0), +#endif + activation_(nullptr), + profilingActivation_(nullptr), + profilerSampleBufferGen_(0), + profilerSampleBufferLapCount_(1), + wasmActivationStack_(nullptr), + entryMonitor(nullptr), + noExecuteDebuggerTop(nullptr), + parentRuntime(parentRuntime), +#ifdef DEBUG + updateChildRuntimeCount(parentRuntime), +#endif + interrupt_(false), + telemetryCallback(nullptr), + handlingSegFault(false), + handlingJitInterrupt_(false), + interruptCallbackDisabled(false), + getIncumbentGlobalCallback(nullptr), + enqueuePromiseJobCallback(nullptr), + enqueuePromiseJobCallbackData(nullptr), + promiseRejectionTrackerCallback(nullptr), + promiseRejectionTrackerCallbackData(nullptr), + startAsyncTaskCallback(nullptr), + finishAsyncTaskCallback(nullptr), + promiseTasksToDestroy(mutexid::PromiseTaskPtrVector), + exclusiveAccessLock(mutexid::RuntimeExclusiveAccess), +#ifdef DEBUG + mainThreadHasExclusiveAccess(false), +#endif + numExclusiveThreads(0), + numCompartments(0), + localeCallbacks(nullptr), + defaultLocale(nullptr), + defaultVersion_(JSVERSION_DEFAULT), + ownerThread_(js::ThisThread::GetId()), + ownerThreadNative_(0), + tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + jitRuntime_(nullptr), + selfHostingGlobal_(nullptr), + nativeStackBase(GetNativeStackBase()), + destroyCompartmentCallback(nullptr), + sizeOfIncludingThisCompartmentCallback(nullptr), + destroyZoneCallback(nullptr), + sweepZoneCallback(nullptr), + compartmentNameCallback(nullptr), + activityCallback(nullptr), + activityCallbackArg(nullptr), + requestDepth(0), +#ifdef DEBUG + checkRequestDepth(0), +#endif + gc(thisFromCtor()), + gcInitialized(false), +#ifdef JS_SIMULATOR + simulator_(nullptr), +#endif + scriptAndCountsVector(nullptr), + lcovOutput(), + NaNValue(DoubleNaNValue()), + negativeInfinityValue(DoubleValue(NegativeInfinity<double>())), + positiveInfinityValue(DoubleValue(PositiveInfinity<double>())), + emptyString(nullptr), + spsProfiler(thisFromCtor()), + profilingScripts(false), + suppressProfilerSampling(false), + hadOutOfMemory(false), +#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) + runningOOMTest(false), +#endif + allowRelazificationForTesting(false), + defaultFreeOp_(nullptr), + debuggerMutations(0), + securityCallbacks(&NullSecurityCallbacks), + DOMcallbacks(nullptr), + destroyPrincipals(nullptr), + readPrincipals(nullptr), + warningReporter(nullptr), + buildIdOp(nullptr), + propertyRemovals(0), +#if !EXPOSE_INTL_API + thousandsSeparator(0), + decimalSeparator(0), + numGrouping(0), +#endif + keepAtoms_(0), + trustedPrincipals_(nullptr), + beingDestroyed_(false), + atoms_(nullptr), + atomsCompartment_(nullptr), + staticStrings(nullptr), + commonNames(nullptr), + permanentAtoms(nullptr), + wellKnownSymbols(nullptr), + wrapObjectCallbacks(&DefaultWrapObjectCallbacks), + preserveWrapperCallback(nullptr), + jitSupportsFloatingPoint(false), + jitSupportsUnalignedAccesses(false), + jitSupportsSimd(false), + ionPcScriptCache(nullptr), + scriptEnvironmentPreparer(nullptr), + ctypesActivityCallback(nullptr), + windowProxyClass_(nullptr), + offthreadIonCompilationEnabled_(true), + parallelParsingEnabled_(true), + autoWritableJitCodeActive_(false), +#ifdef DEBUG + enteredPolicy(nullptr), +#endif + largeAllocationFailureCallback(nullptr), + oomCallback(nullptr), + debuggerMallocSizeOf(ReturnZeroSize), + lastAnimationTime(0), + performanceMonitoring(thisFromCtor()), + ionLazyLinkListSize_(0), + stackFormat_(parentRuntime ? js::StackFormat::Default + : js::StackFormat::SpiderMonkey) +{ + setGCStoreBufferPtr(&gc.storeBuffer); + + liveRuntimesCount++; + + /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ + JS_INIT_CLIST(&onNewGlobalObjectWatchers); + + PodArrayZero(nativeStackQuota); + PodZero(&asmJSCacheOps); + lcovOutput.init(); +} + +bool +JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) +{ + MOZ_ASSERT(ownerThread_ == js::ThisThread::GetId()); + + // Get a platform-native handle for the owner thread, used by + // js::InterruptRunningJitCode to halt the runtime's main thread. +#ifdef XP_WIN + size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | + THREAD_QUERY_INFORMATION; + HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId()); + if (!self) + return false; + static_assert(sizeof(HANDLE) <= sizeof(ownerThreadNative_), "need bigger field"); + ownerThreadNative_ = (size_t)self; +#else + static_assert(sizeof(pthread_t) <= sizeof(ownerThreadNative_), "need bigger field"); + ownerThreadNative_ = (size_t)pthread_self(); +#endif + + if (!mainThread.init()) + return false; + + if (!regexpStack.init()) + return false; + + if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) + return false; + + js::TlsPerThreadData.set(&mainThread); + + defaultFreeOp_ = js_new<js::FreeOp>(this); + if (!defaultFreeOp_) + return false; + + if (!gc.init(maxbytes, maxNurseryBytes)) + return false; + + ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this)); + if (!atomsZone || !atomsZone->init(true)) + return false; + + JS::CompartmentOptions options; + ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options)); + if (!atomsCompartment || !atomsCompartment->init(nullptr)) + return false; + + if (!gc.zones.append(atomsZone.get())) + return false; + if (!atomsZone->compartments.append(atomsCompartment.get())) + return false; + + atomsCompartment->setIsSystem(true); + atomsCompartment->setIsAtomsCompartment(); + + atomsZone.forget(); + this->atomsCompartment_ = atomsCompartment.forget(); + + if (!symbolRegistry_.init()) + return false; + + if (!scriptDataTable_.init()) + return false; + + /* The garbage collector depends on everything before this point being initialized. */ + gcInitialized = true; + + if (!InitRuntimeNumberState(this)) + return false; + + JS::ResetTimeZone(); + +#ifdef JS_SIMULATOR + simulator_ = js::jit::Simulator::Create(contextFromMainThread()); + if (!simulator_) + return false; +#endif + + jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint(); + jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses(); + jitSupportsSimd = js::jit::JitSupportsSimd(); + + if (!wasm::EnsureSignalHandlers(this)) + return false; + + if (!spsProfiler.init()) + return false; + + if (!fx.initInstance()) + return false; + + if (!parentRuntime) { + sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create(); + if (!sharedImmutableStrings_) + return false; + } + + return true; +} + +void +JSRuntime::destroyRuntime() +{ + MOZ_ASSERT(!isHeapBusy()); + MOZ_ASSERT(childRuntimeCount == 0); + + fx.destroyInstance(); + + sharedIntlData.destroyInstance(); + + if (gcInitialized) { + /* + * Finish any in-progress GCs first. This ensures the parseWaitingOnGC + * list is empty in CancelOffThreadParses. + */ + JSContext* cx = contextFromMainThread(); + if (JS::IsIncrementalGCInProgress(cx)) + FinishGC(cx); + + /* Free source hook early, as its destructor may want to delete roots. */ + sourceHook = nullptr; + + /* + * Cancel any pending, in progress or completed Ion compilations and + * parse tasks. Waiting for wasm and compression tasks is done + * synchronously (on the main thread or during parse tasks), so no + * explicit canceling is needed for these. + */ + CancelOffThreadIonCompile(this); + CancelOffThreadParses(this); + + /* Remove persistent GC roots. */ + gc.finishRoots(); + + /* + * Flag us as being destroyed. This allows the GC to free things like + * interned atoms and Ion trampolines. + */ + beingDestroyed_ = true; + + /* Allow the GC to release scripts that were being profiled. */ + profilingScripts = false; + + /* Set the profiler sampler buffer generation to invalid. */ + profilerSampleBufferGen_ = UINT32_MAX; + + JS::PrepareForFullGC(contextFromMainThread()); + gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME); + } + + MOZ_ASSERT(ionLazyLinkListSize_ == 0); + MOZ_ASSERT(ionLazyLinkList_.isEmpty()); + + MOZ_ASSERT(!numExclusiveThreads); + AutoLockForExclusiveAccess lock(this); + + /* + * Even though all objects in the compartment are dead, we may have keep + * some filenames around because of gcKeepAtoms. + */ + FreeScriptData(this, lock); + +#if !EXPOSE_INTL_API + FinishRuntimeNumberState(this); +#endif + + gc.finish(); + atomsCompartment_ = nullptr; + + js_delete(defaultFreeOp_); + + js_free(defaultLocale); + js_delete(jitRuntime_); + + js_delete(ionPcScriptCache); + + gc.storeBuffer.disable(); + gc.nursery.disable(); + +#ifdef JS_SIMULATOR + js::jit::Simulator::Destroy(simulator_); +#endif + + DebugOnly<size_t> oldCount = liveRuntimesCount--; + MOZ_ASSERT(oldCount > 0); + +#ifdef JS_TRACE_LOGGING + DestroyTraceLoggerMainThread(this); +#endif + + js::TlsPerThreadData.set(nullptr); + +#ifdef XP_WIN + if (ownerThreadNative_) + CloseHandle((HANDLE)ownerThreadNative_); +#endif +} + +void +JSRuntime::addTelemetry(int id, uint32_t sample, const char* key) +{ + if (telemetryCallback) + (*telemetryCallback)(id, sample, key); +} + +void +JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) +{ + rt->telemetryCallback = callback; +} + +void +JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes) +{ + // Several tables in the runtime enumerated below can be used off thread. + AutoLockForExclusiveAccess lock(this); + + // For now, measure the size of the derived class (JSContext). + // TODO (bug 1281529): make memory reporting reflect the new + // JSContext/JSRuntime world better. + JSContext* cx = unsafeContextFromAnyThread(); + rtSizes->object += mallocSizeOf(cx); + + rtSizes->atomsTable += atoms(lock).sizeOfIncludingThis(mallocSizeOf); + + if (!parentRuntime) { + rtSizes->atomsTable += mallocSizeOf(staticStrings); + rtSizes->atomsTable += mallocSizeOf(commonNames); + rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf); + } + + rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf); + + rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); + + rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf); + + if (MathCache* cache = cx->caches.maybeGetMathCache()) + rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf); + + if (sharedImmutableStrings_) { + rtSizes->sharedImmutableStringsCache += + sharedImmutableStrings_->sizeOfExcludingThis(mallocSizeOf); + } + + rtSizes->sharedIntlData += sharedIntlData.sizeOfExcludingThis(mallocSizeOf); + + rtSizes->uncompressedSourceCache += + cx->caches.uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf); + + + rtSizes->scriptData += scriptDataTable(lock).sizeOfExcludingThis(mallocSizeOf); + for (ScriptDataTable::Range r = scriptDataTable(lock).all(); !r.empty(); r.popFront()) + rtSizes->scriptData += mallocSizeOf(r.front()); + + if (jitRuntime_) { + jitRuntime_->execAlloc().addSizeOfCode(&rtSizes->code); + jitRuntime_->backedgeExecAlloc().addSizeOfCode(&rtSizes->code); + } + + rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf); + rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted(); + rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf); + gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); +} + +static bool +InvokeInterruptCallback(JSContext* cx) +{ + MOZ_ASSERT(cx->runtime()->requestDepth >= 1); + + cx->runtime()->gc.gcIfRequested(); + + // A worker thread may have requested an interrupt after finishing an Ion + // compilation. + jit::AttachFinishedCompilations(cx); + + // Important: Additional callbacks can occur inside the callback handler + // if it re-enters the JS engine. The embedding must ensure that the + // callback is disconnected before attempting such re-entry. + if (cx->runtime()->interruptCallbackDisabled) + return true; + + bool stop = false; + for (JSInterruptCallback cb : cx->runtime()->interruptCallbacks) { + if (!cb(cx)) + stop = true; + } + + if (!stop) { + // Debugger treats invoking the interrupt callback as a "step", so + // invoke the onStep handler. + if (cx->compartment()->isDebuggee()) { + ScriptFrameIter iter(cx); + if (!iter.done() && + cx->compartment() == iter.compartment() && + iter.script()->stepModeEnabled()) + { + RootedValue rval(cx); + switch (Debugger::onSingleStep(cx, &rval)) { + case JSTRAP_ERROR: + return false; + case JSTRAP_CONTINUE: + return true; + case JSTRAP_RETURN: + // See note in Debugger::propagateForcedReturn. + Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval); + return false; + case JSTRAP_THROW: + cx->setPendingException(rval); + return false; + default:; + } + } + } + + return true; + } + + // No need to set aside any pending exception here: ComputeStackString + // already does that. + JSString* stack = ComputeStackString(cx); + JSFlatString* flat = stack ? stack->ensureFlat(cx) : nullptr; + + const char16_t* chars; + AutoStableStringChars stableChars(cx); + if (flat && stableChars.initTwoByte(cx, flat)) + chars = stableChars.twoByteRange().begin().get(); + else + chars = u"(stack not available)"; + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr, + JSMSG_TERMINATED, chars); + + return false; +} + +void +JSRuntime::requestInterrupt(InterruptMode mode) +{ + interrupt_ = true; + jitStackLimit_ = UINTPTR_MAX; + + if (mode == JSRuntime::RequestInterruptUrgent) { + // If this interrupt is urgent (slow script dialog and garbage + // collection among others), take additional steps to + // interrupt corner cases where the above fields are not + // regularly polled. Wake both ilooping JIT code and + // Atomics.wait(). + fx.lock(); + if (fx.isWaiting()) + fx.wake(FutexRuntime::WakeForJSInterrupt); + fx.unlock(); + InterruptRunningJitCode(this); + } +} + +bool +JSRuntime::handleInterrupt(JSContext* cx) +{ + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); + if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) { + interrupt_ = false; + cx->resetJitStackLimit(); + return InvokeInterruptCallback(cx); + } + return true; +} + +bool +JSRuntime::setDefaultLocale(const char* locale) +{ + if (!locale) + return false; + resetDefaultLocale(); + defaultLocale = JS_strdup(contextFromMainThread(), locale); + return defaultLocale != nullptr; +} + +void +JSRuntime::resetDefaultLocale() +{ + js_free(defaultLocale); + defaultLocale = nullptr; +} + +const char* +JSRuntime::getDefaultLocale() +{ + if (defaultLocale) + return defaultLocale; + + const char* locale; +#ifdef HAVE_SETLOCALE + locale = setlocale(LC_ALL, nullptr); +#else + locale = getenv("LANG"); +#endif + // convert to a well-formed BCP 47 language tag + if (!locale || !strcmp(locale, "C")) + locale = "und"; + + char* lang = JS_strdup(contextFromMainThread(), locale); + if (!lang) + return nullptr; + + char* p; + if ((p = strchr(lang, '.'))) + *p = '\0'; + while ((p = strchr(lang, '_'))) + *p = '-'; + + defaultLocale = lang; + return defaultLocale; +} + +void +JSRuntime::traceSharedIntlData(JSTracer* trc) +{ + sharedIntlData.trace(trc); +} + +void +JSRuntime::triggerActivityCallback(bool active) +{ + if (!activityCallback) + return; + + /* + * The activity callback must not trigger a GC: it would create a cirular + * dependency between entering a request and Rooted's requirement of being + * in a request. In practice this callback already cannot trigger GC. The + * suppression serves to inform the exact rooting hazard analysis of this + * property and ensures that it remains true in the future. + */ + AutoSuppressGC suppress(contextFromMainThread()); + + activityCallback(activityCallbackArg, active); +} + +FreeOp::FreeOp(JSRuntime* maybeRuntime) + : JSFreeOp(maybeRuntime) +{ + MOZ_ASSERT_IF(maybeRuntime, CurrentThreadCanAccessRuntime(maybeRuntime)); +} + +FreeOp::~FreeOp() +{ + for (size_t i = 0; i < freeLaterList.length(); i++) + free_(freeLaterList[i]); + + if (!jitPoisonRanges.empty()) + jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges); +} + +bool +FreeOp::isDefaultFreeOp() const +{ + return runtime_ && runtime_->defaultFreeOp() == this; +} + +JSObject* +JSRuntime::getIncumbentGlobal(JSContext* cx) +{ + MOZ_ASSERT(cx->runtime()->getIncumbentGlobalCallback, + "Must set a callback using JS_SetGetIncumbentGlobalCallback before using Promises"); + + return cx->runtime()->getIncumbentGlobalCallback(cx); +} + +bool +JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject promise, + HandleObject incumbentGlobal) +{ + MOZ_ASSERT(cx->runtime()->enqueuePromiseJobCallback, + "Must set a callback using JS_SetEnqeueuPromiseJobCallback before using Promises"); + MOZ_ASSERT_IF(incumbentGlobal, !IsWrapper(incumbentGlobal) && !IsWindowProxy(incumbentGlobal)); + + void* data = cx->runtime()->enqueuePromiseJobCallbackData; + RootedObject allocationSite(cx); + if (promise) { + RootedObject unwrappedPromise(cx, promise); + // While the job object is guaranteed to be unwrapped, the promise + // might be wrapped. See the comments in + // intrinsic_EnqueuePromiseReactionJob for details. + if (IsWrapper(promise)) + unwrappedPromise = UncheckedUnwrap(promise); + if (unwrappedPromise->is<PromiseObject>()) + allocationSite = JS::GetPromiseAllocationSite(unwrappedPromise); + } + return cx->runtime()->enqueuePromiseJobCallback(cx, job, allocationSite, incumbentGlobal, data); +} + +void +JSRuntime::addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise) +{ + MOZ_ASSERT(promise->is<PromiseObject>()); + if (!cx->runtime()->promiseRejectionTrackerCallback) + return; + + void* data = cx->runtime()->promiseRejectionTrackerCallbackData; + cx->runtime()->promiseRejectionTrackerCallback(cx, promise, + PromiseRejectionHandlingState::Unhandled, data); +} + +void +JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise) +{ + MOZ_ASSERT(promise->is<PromiseObject>()); + if (!cx->runtime()->promiseRejectionTrackerCallback) + return; + + void* data = cx->runtime()->promiseRejectionTrackerCallbackData; + cx->runtime()->promiseRejectionTrackerCallback(cx, promise, + PromiseRejectionHandlingState::Handled, data); +} + +mozilla::non_crypto::XorShift128PlusRNG& +JSRuntime::randomKeyGenerator() +{ + MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); + if (randomKeyGenerator_.isNothing()) { + mozilla::Array<uint64_t, 2> seed; + GenerateXorShift128PlusSeed(seed); + randomKeyGenerator_.emplace(seed[0], seed[1]); + } + return randomKeyGenerator_.ref(); +} + +mozilla::HashCodeScrambler +JSRuntime::randomHashCodeScrambler() +{ + auto& rng = randomKeyGenerator(); + return mozilla::HashCodeScrambler(rng.next(), rng.next()); +} + +mozilla::non_crypto::XorShift128PlusRNG +JSRuntime::forkRandomKeyGenerator() +{ + auto& rng = randomKeyGenerator(); + return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next()); +} + +void +JSRuntime::updateMallocCounter(size_t nbytes) +{ + updateMallocCounter(nullptr, nbytes); +} + +void +JSRuntime::updateMallocCounter(JS::Zone* zone, size_t nbytes) +{ + gc.updateMallocCounter(zone, nbytes); +} + +JS_FRIEND_API(void*) +JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr, + JSContext* maybecx) +{ + MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); + + if (isHeapBusy()) + return nullptr; + + if (!oom::IsSimulatedOOMAllocation()) { + /* + * Retry when we are done with the background sweeping and have stopped + * all the allocations and released the empty GC chunks. + */ + gc.onOutOfMallocMemory(); + void* p; + switch (allocFunc) { + case AllocFunction::Malloc: + p = js_malloc(nbytes); + break; + case AllocFunction::Calloc: + p = js_calloc(nbytes); + break; + case AllocFunction::Realloc: + p = js_realloc(reallocPtr, nbytes); + break; + default: + MOZ_CRASH(); + } + if (p) + return p; + } + + if (maybecx) + ReportOutOfMemory(maybecx); + return nullptr; +} + +void* +JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, size_t bytes, void* reallocPtr) +{ + if (largeAllocationFailureCallback && bytes >= LARGE_ALLOCATION) + largeAllocationFailureCallback(largeAllocationFailureCallbackData); + return onOutOfMemory(allocFunc, bytes, reallocPtr); +} + +bool +JSRuntime::activeGCInAtomsZone() +{ + Zone* zone = atomsCompartment_->zone(); + return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) || + zone->wasGCStarted(); +} + +void +JSRuntime::setUsedByExclusiveThread(Zone* zone) +{ + MOZ_ASSERT(!zone->usedByExclusiveThread); + zone->usedByExclusiveThread = true; + numExclusiveThreads++; +} + +void +JSRuntime::clearUsedByExclusiveThread(Zone* zone) +{ + MOZ_ASSERT(zone->usedByExclusiveThread); + zone->usedByExclusiveThread = false; + numExclusiveThreads--; + if (gc.fullGCForAtomsRequested() && !keepAtoms()) + gc.triggerFullGCForAtoms(); +} + +bool +js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) +{ + return rt->ownerThread_ == js::ThisThread::GetId(); +} + +bool +js::CurrentThreadCanAccessZone(Zone* zone) +{ + if (CurrentThreadCanAccessRuntime(zone->runtime_)) + return true; + + // Only zones in use by an exclusive thread can be used off the main thread. + // We don't keep track of which thread owns such zones though, so this check + // is imperfect. + return zone->usedByExclusiveThread; +} + +#ifdef DEBUG +bool +js::CurrentThreadIsPerformingGC() +{ + return TlsPerThreadData.get()->performingGC; +} +#endif + +JS_FRIEND_API(void) +JS::UpdateJSContextProfilerSampleBufferGen(JSContext* cx, uint32_t generation, + uint32_t lapCount) +{ + cx->setProfilerSampleBufferGen(generation); + cx->updateProfilerSampleBufferLapCount(lapCount); +} + +JS_FRIEND_API(bool) +JS::IsProfilingEnabledForContext(JSContext* cx) +{ + MOZ_ASSERT(cx); + return cx->spsProfiler.enabled(); +} + +JSRuntime::IonBuilderList& +JSRuntime::ionLazyLinkList() +{ + MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(), + "Should only be mutated by the main thread."); + return ionLazyLinkList_; +} + +void +JSRuntime::ionLazyLinkListRemove(jit::IonBuilder* builder) +{ + MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(), + "Should only be mutated by the main thread."); + MOZ_ASSERT(ionLazyLinkListSize_ > 0); + + builder->removeFrom(ionLazyLinkList()); + ionLazyLinkListSize_--; + + MOZ_ASSERT(ionLazyLinkList().isEmpty() == (ionLazyLinkListSize_ == 0)); +} + +void +JSRuntime::ionLazyLinkListAdd(jit::IonBuilder* builder) +{ + MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(), + "Should only be mutated by the main thread."); + ionLazyLinkList().insertFront(builder); + ionLazyLinkListSize_++; +} + +JSContext* +PerThreadData::contextFromMainThread() +{ + return runtime_->contextFromMainThread(); +} |