/* -*- 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/. */ #ifndef vm_Runtime_h #define vm_Runtime_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/DoublyLinkedList.h" #include "mozilla/LinkedList.h" #include "mozilla/Maybe.h" #include "mozilla/MaybeOneOf.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ThreadLocal.h" #include "mozilla/TimeStamp.h" #include "mozilla/Vector.h" #include #include #include "jsapi.h" #include "builtin/AtomicsObject.h" #ifdef JS_HAS_INTL_API # include "builtin/intl/SharedIntlData.h" #endif #include "frontend/NameCollections.h" #include "gc/GCRuntime.h" #include "gc/Tracer.h" #include "js/AllocationRecording.h" #include "js/BuildId.h" // JS::BuildIdOp #include "js/Debug.h" #include "js/experimental/CTypes.h" // JS::CTypesActivityCallback #include "js/experimental/SourceHook.h" // js::SourceHook #include "js/friend/StackLimits.h" // js::ReportOverRecursed #include "js/friend/UsageStatistics.h" // JSAccumulateTelemetryDataCallback #include "js/GCVector.h" #include "js/HashTable.h" #include "js/Modules.h" // JS::Module{DynamicImport,Metadata,Resolve}Hook #ifdef DEBUG # include "js/Proxy.h" // For AutoEnterPolicy #endif #include "js/Stream.h" // JS::AbortSignalIsAborted #include "js/Symbol.h" #include "js/UniquePtr.h" #include "js/Utility.h" #include "js/Vector.h" #include "js/Warnings.h" // JS::WarningReporter #include "threading/Thread.h" #include "vm/Caches.h" #include "vm/CodeCoverage.h" #include "vm/CommonPropertyNames.h" #include "vm/GeckoProfiler.h" #include "vm/JSAtom.h" #include "vm/JSAtomState.h" #include "vm/JSScript.h" #include "vm/OffThreadPromiseRuntimeState.h" // js::OffThreadPromiseRuntimeState #include "vm/Scope.h" #include "vm/SharedImmutableStringsCache.h" #include "vm/SharedStencil.h" // js::SharedImmutableScriptDataTable #include "vm/Stack.h" #include "vm/SymbolType.h" #include "wasm/WasmTypes.h" struct JSClass; namespace js { class AutoAssertNoContentJS; class EnterDebuggeeNoExecute; #ifdef JS_TRACE_LOGGING class TraceLoggerThread; #endif } // namespace js struct DtoaState; struct JSLocaleCallbacks; #ifdef JS_SIMULATOR_ARM64 namespace vixl { class Simulator; } #endif namespace js { extern MOZ_COLD void ReportOutOfMemory(JSContext* cx); /* Different signature because the return type has MOZ_MUST_USE_TYPE. */ extern MOZ_COLD mozilla::GenericErrorResult ReportOutOfMemoryResult( JSContext* cx); extern MOZ_COLD void ReportAllocationOverflow(JSContext* maybecx); class Activation; class ActivationIterator; namespace jit { class JitRuntime; class JitActivation; struct PcScriptCache; class CompileRuntime; #ifdef JS_SIMULATOR_ARM64 typedef vixl::Simulator Simulator; #elif defined(JS_SIMULATOR) class Simulator; #endif } // namespace jit namespace frontend { class WellKnownParserAtoms; } // namespace frontend // [SMDOC] JS Engine Threading // // Threads interacting with a runtime are divided into two categories: // // - The main thread is capable of running JS. There's at most one main thread // per runtime. // // - Helper threads do not run JS, and are controlled or triggered by activity // on the main thread (or main threads, since all runtimes in a process share // helper threads). Helper threads may have exclusive access to zones created // for them, for parsing and similar tasks, but their activities do not cause // observable changes in script behaviors. Activity on helper threads may be // referred to as happening 'off thread' or on a background thread in some // parts of the VM. } /* namespace js */ namespace JS { struct RuntimeSizes; } // namespace JS namespace js { /* * Storage for well-known symbols. It's a separate struct from the Runtime so * that it can be shared across multiple runtimes. As in JSAtomState, each * field is a smart pointer that's immutable once initialized. * `rt->wellKnownSymbols->iterator` is convertible to Handle. * * Well-known symbols are never GC'd. The description() of each well-known * symbol is a permanent atom. */ struct WellKnownSymbols { #define DECLARE_SYMBOL(name) js::ImmutableSymbolPtr name; JS_FOR_EACH_WELL_KNOWN_SYMBOL(DECLARE_SYMBOL) #undef DECLARE_SYMBOL const ImmutableSymbolPtr& get(size_t u) const { MOZ_ASSERT(u < JS::WellKnownSymbolLimit); const ImmutableSymbolPtr* symbols = reinterpret_cast(this); return symbols[u]; } const ImmutableSymbolPtr& get(JS::SymbolCode code) const { return get(size_t(code)); } WellKnownSymbols() = default; WellKnownSymbols(const WellKnownSymbols&) = delete; WellKnownSymbols& operator=(const WellKnownSymbols&) = delete; }; // There are several coarse locks in the enum below. These may be either // per-runtime or per-process. When acquiring more than one of these locks, // the acquisition must be done in the order below to avoid deadlocks. enum RuntimeLock { HelperThreadStateLock, GCLock }; inline bool CanUseExtraThreads() { extern bool gCanUseExtraThreads; return gCanUseExtraThreads; } void DisableExtraThreads(); using ScriptAndCountsVector = GCVector; class AutoLockScriptData; // Self-hosted lazy functions do not maintain a BaseScript as we can clone from // the copy in the self-hosting zone. To allow these functions to be called by // the JITs, we need a minimal script object. There is one instance per runtime. struct SelfHostedLazyScript { SelfHostedLazyScript() = default; // Pointer to interpreter trampoline. This field is stored at same location as // in BaseScript::jitCodeRaw_. uint8_t* jitCodeRaw_ = nullptr; // Warm-up count of zero. This field is stored at the same offset as // BaseScript::warmUpData_. ScriptWarmUpData warmUpData_ = {}; static constexpr size_t offsetOfJitCodeRaw() { return offsetof(SelfHostedLazyScript, jitCodeRaw_); } static constexpr size_t offsetOfWarmUpData() { return offsetof(SelfHostedLazyScript, warmUpData_); } }; } // namespace js struct JSTelemetrySender; struct JSRuntime { private: friend class js::Activation; friend class js::ActivationIterator; friend class js::jit::JitActivation; friend class js::jit::CompileRuntime; /* Space for interpreter frames. */ js::MainThreadData interpreterStack_; public: js::InterpreterStack& interpreterStack() { return interpreterStack_.ref(); } /* * If non-null, another runtime guaranteed to outlive this one and whose * permanent data may be used by this one where possible. */ JSRuntime* const parentRuntime; #ifdef DEBUG /* The number of child runtimes that have this runtime as their parent. */ mozilla::Atomic childRuntimeCount; class AutoUpdateChildRuntimeCount { JSRuntime* parent_; public: explicit AutoUpdateChildRuntimeCount(JSRuntime* parent) : parent_(parent) { if (parent_) { parent_->childRuntimeCount++; } } ~AutoUpdateChildRuntimeCount() { if (parent_) { parent_->childRuntimeCount--; } } }; AutoUpdateChildRuntimeCount updateChildRuntimeCount; #endif private: #ifdef DEBUG js::WriteOnceData initialized_; #endif // The JSContext* for the runtime's main thread. Immutable after this is set // in JSRuntime::init. JSContext* mainContext_; public: JSContext* mainContextFromAnyThread() const { return mainContext_; } const void* addressOfMainContext() { return &mainContext_; } js::Fprinter parserWatcherFile; inline JSContext* mainContextFromOwnThread(); /* * The start of the range stored in the profiler sample buffer, as measured * after the most recent sample. * All JitcodeGlobalTable entries referenced from a given sample are * assigned the buffer position of the START of the sample. The buffer * entries that reference the JitcodeGlobalTable entries will only ever be * read from the buffer while the entire sample is still inside the buffer; * if some buffer entries at the start of the sample have left the buffer, * the entire sample will be considered inaccessible. * This means that, once profilerSampleBufferRangeStart_ advances beyond * the sample position that's stored on a JitcodeGlobalTable entry, the * buffer entries that reference this JitcodeGlobalTable entry will be * considered inaccessible, and those JitcodeGlobalTable entry can be * disposed of. */ mozilla::Atomic profilerSampleBufferRangeStart_; mozilla::Maybe profilerSampleBufferRangeStart() { if (beingDestroyed_ || !geckoProfiler().enabled()) { return mozilla::Nothing(); } uint64_t rangeStart = profilerSampleBufferRangeStart_; return mozilla::Some(rangeStart); } void setProfilerSampleBufferRangeStart(uint64_t rangeStart) { profilerSampleBufferRangeStart_ = rangeStart; } /* Call this to accumulate telemetry data. May be called from any thread; the * embedder is responsible for locking. */ JSAccumulateTelemetryDataCallback telemetryCallback; /* Call this to accumulate use counter data. */ js::MainThreadData useCounterCallback; js::MainThreadData getElementCallback; public: // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* // histogram. |key| provides an additional key to identify the histogram. // |sample| is the data to add to the histogram. void addTelemetry(int id, uint32_t sample, const char* key = nullptr); JSTelemetrySender getTelemetrySender() const; void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback); void setElementCallback(JSRuntime* rt, JSGetElementCallback callback); // Sets the use counter for a specific feature, measuring the presence or // absence of usage of a feature on a specific web page and document which // the passed JSObject belongs to. void setUseCounter(JSObject* obj, JSUseCounter counter); void setUseCounterCallback(JSRuntime* rt, JSSetUseCounterCallback callback); public: js::UnprotectedData offThreadPromiseState; js::UnprotectedData consumeStreamCallback; js::UnprotectedData reportStreamErrorCallback; js::GlobalObject* getIncumbentGlobal(JSContext* cx); bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise, js::Handle incumbentGlobal); void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); /* Had an out-of-memory error which did not populate an exception. */ mozilla::Atomic hadOutOfMemory; /* * Allow relazifying functions in compartments that are active. This is * only used by the relazifyFunctions() testing function. */ js::MainThreadData allowRelazificationForTesting; /* Zone destroy callback. */ js::MainThreadData destroyZoneCallback; /* Compartment destroy callback. */ js::MainThreadData destroyCompartmentCallback; /* Compartment memory reporting callback. */ js::MainThreadData sizeOfIncludingThisCompartmentCallback; /* Callback for creating ubi::Nodes representing DOM node objects. Set by * JS::ubi::SetConstructUbiNodeForDOMObjectCallback. Refer to * js/public/UbiNode.h. */ void (*constructUbiNodeForDOMObjectCallback)(void*, JSObject*) = nullptr; /* Realm destroy callback. */ js::MainThreadData destroyRealmCallback; /* Call this to get the name of a realm. */ js::MainThreadData realmNameCallback; js::MainThreadData> sourceHook; js::MainThreadData securityCallbacks; js::MainThreadData DOMcallbacks; js::MainThreadData destroyPrincipals; js::MainThreadData readPrincipals; /* Optional warning reporter. */ js::MainThreadData warningReporter; // Lazy self-hosted functions use a shared SelfHostedLazyScript instance // instead instead of a BaseScript. This contains the minimal pointers to // trampolines for the scripts to support direct jitCodeRaw calls. js::UnprotectedData selfHostedLazyScript; private: /* Gecko profiling metadata */ js::UnprotectedData geckoProfiler_; public: js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); } // Heap GC roots for PersistentRooted pointers. js::MainThreadData>>> heapRoots; void tracePersistentRoots(JSTracer* trc); void finishPersistentRoots(); void finishRoots(); private: js::UnprotectedData trustedPrincipals_; public: void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; } const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; } js::MainThreadData wrapObjectCallbacks; js::MainThreadData preserveWrapperCallback; js::MainThreadData hasReleasedWrapperCallback; js::MainThreadData scriptEnvironmentPreparer; js::MainThreadData ctypesActivityCallback; private: js::WriteOnceData windowProxyClass_; public: const JSClass* maybeWindowProxyClass() const { return windowProxyClass_; } void setWindowProxyClass(const JSClass* clasp) { windowProxyClass_ = clasp; } private: js::WriteOnceData abortSignalClass_; js::WriteOnceData abortSignalIsAborted_; public: void initPipeToHandling(const JSClass* abortSignalClass, JS::AbortSignalIsAborted isAborted) { MOZ_ASSERT(abortSignalClass != nullptr, "doesn't make sense for an embedder to provide a null class " "when specifying pipeTo handling"); MOZ_ASSERT(isAborted != nullptr, "must pass a valid function pointer"); abortSignalClass_ = abortSignalClass; abortSignalIsAborted_ = isAborted; } const JSClass* maybeAbortSignalClass() const { return abortSignalClass_; } bool abortSignalIsAborted(JSObject* obj) { MOZ_ASSERT(abortSignalIsAborted_ != nullptr, "must call initPipeToHandling first"); return abortSignalIsAborted_(obj); } private: // List of non-ephemeron weak containers to sweep during // beginSweepingSweepGroup. js::MainThreadData> weakCaches_; public: mozilla::LinkedList& weakCaches() { return weakCaches_.ref(); } void registerWeakCache(JS::detail::WeakCacheBase* cachep) { weakCaches().insertBack(cachep); } template struct GlobalObjectWatchersLinkAccess { static mozilla::DoublyLinkedListElement& Get(T* aThis) { return aThis->onNewGlobalObjectWatchersLink; } }; template struct GarbageCollectionWatchersLinkAccess { static mozilla::DoublyLinkedListElement& Get(T* aThis) { return aThis->onGarbageCollectionWatchersLink; } }; using OnNewGlobalWatchersList = mozilla::DoublyLinkedList>; using OnGarbageCollectionWatchersList = mozilla::DoublyLinkedList< js::Debugger, GarbageCollectionWatchersLinkAccess>; private: /* * List of all enabled Debuggers that have onNewGlobalObject handler * methods established. */ js::MainThreadData onNewGlobalObjectWatchers_; /* * List of all enabled Debuggers that have onGarbageCollection handler * methods established. */ js::MainThreadData onGarbageCollectionWatchers_; public: OnNewGlobalWatchersList& onNewGlobalObjectWatchers() { return onNewGlobalObjectWatchers_.ref(); } OnGarbageCollectionWatchersList& onGarbageCollectionWatchers() { return onGarbageCollectionWatchers_.ref(); } private: /* Linked list of all Debugger objects in the runtime. */ js::MainThreadData> debuggerList_; public: mozilla::LinkedList& debuggerList() { return debuggerList_.ref(); } private: /* * Lock used to protect the script data table, which can be used by * off-thread parsing. * * Locking this only occurs if there is actually a thread other than the * main thread which could access this. */ js::Mutex scriptDataLock; #ifdef DEBUG bool activeThreadHasScriptDataAccess; #endif // Number of off-thread ParseTasks that are using this runtime. This is only // updated on main-thread. If this is non-zero we must use `scriptDataLock` to // protect access to the bytecode table; mozilla::Atomic numParseTasks; // Number of zones which may be operated on by helper threads. mozilla::Atomic numActiveHelperThreadZones; friend class js::AutoLockScriptData; public: void setUsedByHelperThread(JS::Zone* zone); void clearUsedByHelperThread(JS::Zone* zone); bool hasParseTasks() const { return numParseTasks > 0; } bool hasHelperThreadZones() const { return numActiveHelperThreadZones > 0; } void addParseTaskRef() { numParseTasks++; } void decParseTaskRef() { numParseTasks--; } #ifdef DEBUG void assertCurrentThreadHasScriptDataAccess() const { if (!hasParseTasks()) { MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(this) && activeThreadHasScriptDataAccess); return; } scriptDataLock.assertOwnedByCurrentThread(); } bool currentThreadHasAtomsTableAccess() const { return js::CurrentThreadCanAccessRuntime(this) && atoms_->mainThreadHasAllLocks(); } #endif JS::HeapState heapState() const { return gc.heapState(); } // How many realms there are across all zones. This number includes // off-thread context realms, so it isn't necessarily equal to the // number of realms visited by RealmsIter. js::MainThreadData numRealms; // The Gecko Profiler may want to sample the allocations happening across the // browser. This callback can be registered to record the allocation. js::MainThreadData recordAllocationCallback; js::MainThreadData allocationSamplingProbability; private: // Number of debuggee realms in the runtime. js::MainThreadData numDebuggeeRealms_; // Number of debuggee realms in the runtime observing code coverage. js::MainThreadData numDebuggeeRealmsObservingCoverage_; public: void incrementNumDebuggeeRealms(); void decrementNumDebuggeeRealms(); size_t numDebuggeeRealms() const { return numDebuggeeRealms_; } void incrementNumDebuggeeRealmsObservingCoverage(); void decrementNumDebuggeeRealmsObservingCoverage(); void startRecordingAllocations(double probability, JS::RecordAllocationsCallback callback); void stopRecordingAllocations(); void ensureRealmIsRecordingAllocations(JS::Handle global); /* Locale-specific callbacks for string conversion. */ js::MainThreadData localeCallbacks; /* Default locale for Internationalization API */ js::MainThreadData defaultLocale; /* If true, new scripts must be created with PC counter information. */ js::MainThreadOrIonCompileData profilingScripts; /* Strong references on scripts held for PCCount profiling API. */ js::MainThreadData*> scriptAndCountsVector; private: /* Code coverage output. */ js::UnprotectedData lcovOutput_; public: js::coverage::LCovRuntime& lcovOutput() { return lcovOutput_.ref(); } private: js::UnprotectedData jitRuntime_; /* * Self-hosting state cloned on demand into other compartments. Shared with * the parent runtime if there is one. */ js::WriteOnceData selfHostingGlobal_; // Optional reference to an array which contains the XDR content to be used // instead of parsing the self-hosted source text. It is cleared once the // self-hosted global is initialized. JS::TranscodeRange selfHostedXDR = {}; // Callback to copy the XDR content of the self-hosted code. using TranscodeBufferWriter = bool (*)(JSContext* cx, const JS::TranscodeBuffer&); TranscodeBufferWriter selfHostedXDRWriter = nullptr; static js::GlobalObject* createSelfHostingGlobal(JSContext* cx); // Used internally to initialize the self-hosted global using XDR content. bool initSelfHostingFromXDR(JSContext* cx, const JS::CompileOptions& options, js::frontend::CompilationStencilSet& stencilSet, js::MutableHandle scriptOut); public: void getUnclonedSelfHostedValue(js::PropertyName* name, JS::Value* vp); JSFunction* getUnclonedSelfHostedFunction(js::PropertyName* name); MOZ_MUST_USE bool createJitRuntime(JSContext* cx); js::jit::JitRuntime* jitRuntime() const { return jitRuntime_.ref(); } bool hasJitRuntime() const { return !!jitRuntime_; } private: // Used to generate random keys for hash tables. mozilla::Maybe randomKeyGenerator_; mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator(); // Used to generate random hash codes for symbols. mozilla::Maybe randomHashCodeGenerator_; public: mozilla::HashCodeScrambler randomHashCodeScrambler(); mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator(); js::HashNumber randomHashCode(); //------------------------------------------------------------------------- // Self-hosting support //------------------------------------------------------------------------- // Optional XDR compiled data for self-hosting. If set this, will be used to // parse the self-hosting code instead of from source code. // // This field is cleared internally after self-hosting is initialized. void setSelfHostedXDR(JS::TranscodeRange enctext) { MOZ_RELEASE_ASSERT(!hasInitializedSelfHosting()); MOZ_RELEASE_ASSERT(enctext.length() > 0); new (&selfHostedXDR) mozilla::Range(enctext); } // Register a callback which would be used to return a buffer if the // self-hosted code should be serialized and stored in the returned buffer. void setSelfHostedXDRWriterCallback(TranscodeBufferWriter writer) { MOZ_RELEASE_ASSERT(!hasInitializedSelfHosting()); MOZ_RELEASE_ASSERT(!selfHostedXDRWriter); selfHostedXDRWriter = writer; } bool hasInitializedSelfHosting() const { return selfHostingGlobal_; } bool initSelfHosting(JSContext* cx); void finishSelfHosting(); void traceSelfHostingGlobal(JSTracer* trc); bool isSelfHostingGlobal(JSObject* global) { return global == selfHostingGlobal_; } js::GeneratorKind getSelfHostedFunctionGeneratorKind(JSAtom* name); bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName, js::HandleAtom name, unsigned nargs, js::NewObjectKind newKind, js::MutableHandleFunction fun); bool cloneSelfHostedFunctionScript(JSContext* cx, js::Handle name, js::Handle targetFun); bool cloneSelfHostedValue(JSContext* cx, js::Handle name, js::MutableHandleValue vp); void assertSelfHostedFunctionHasCanonicalName(JSContext* cx, js::HandlePropertyName name); #if DEBUG bool isSelfHostingZone(const JS::Zone* zone) const { return selfHostingGlobal_ && selfHostingGlobal_->zone() == zone; } #endif //------------------------------------------------------------------------- // Locale information //------------------------------------------------------------------------- /* * Set the default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). * Note that the Internationalization API encourages clients to * specify their own locales. * The locale string remains owned by the caller. */ bool setDefaultLocale(const char* locale); /* Reset the default locale to OS defaults. */ void resetDefaultLocale(); /* Gets current default locale. String remains owned by context. */ const char* getDefaultLocale(); /* Garbage collector state. */ js::gc::GCRuntime gc; /* Garbage collector state has been successfully initialized. */ js::WriteOnceData gcInitialized; bool hasZealMode(js::gc::ZealMode mode) { return gc.hasZealMode(mode); } void lockGC() { gc.lockGC(); } void unlockGC() { gc.unlockGC(); } js::WriteOnceData emptyString; private: js::MainThreadOrGCTaskData defaultFreeOp_; public: JSFreeOp* defaultFreeOp() { MOZ_ASSERT(defaultFreeOp_); return defaultFreeOp_; } #if !JS_HAS_INTL_API /* Number localization, used by jsnum.cpp. */ js::WriteOnceData thousandsSeparator; js::WriteOnceData decimalSeparator; js::WriteOnceData numGrouping; #endif private: mozilla::Maybe sharedImmutableStrings_; public: // If this particular JSRuntime has a SharedImmutableStringsCache, return a // pointer to it, otherwise return nullptr. js::SharedImmutableStringsCache* maybeThisRuntimeSharedImmutableStrings() { return sharedImmutableStrings_.isSome() ? &*sharedImmutableStrings_ : nullptr; } // Get a reference to this JSRuntime's or its parent's // SharedImmutableStringsCache. js::SharedImmutableStringsCache& sharedImmutableStrings() { MOZ_ASSERT_IF(parentRuntime, !sharedImmutableStrings_); MOZ_ASSERT_IF(!parentRuntime, sharedImmutableStrings_); return parentRuntime ? parentRuntime->sharedImmutableStrings() : *sharedImmutableStrings_; } private: js::WriteOnceData beingDestroyed_; public: bool isBeingDestroyed() const { return beingDestroyed_; } private: bool allowContentJS_; public: bool allowContentJS() const { return allowContentJS_; } friend class js::AutoAssertNoContentJS; private: // Table of all atoms other than those in permanentAtoms and staticStrings. js::WriteOnceData atoms_; // Set of all live symbols produced by Symbol.for(). All such symbols are // allocated in the atoms zone. Reading or writing the symbol registry // can only be done from the main thread. js::MainThreadOrGCTaskData symbolRegistry_; js::WriteOnceData permanentAtomsDuringInit_; js::WriteOnceData permanentAtoms_; public: bool initializeAtoms(JSContext* cx); bool initializeParserAtoms(JSContext* cx); void finishAtoms(); void finishParserAtoms(); bool atomsAreFinished() const { return !atoms_ && !permanentAtomsDuringInit_; } js::AtomsTable* atomsForSweeping() { MOZ_ASSERT(JS::RuntimeHeapIsCollecting()); return atoms_; } js::AtomsTable& atoms() { MOZ_ASSERT(atoms_); return *atoms_; } const JS::Zone* atomsZone(const js::AutoAccessAtomsZone& access) const { return gc.atomsZone; } JS::Zone* atomsZone(const js::AutoAccessAtomsZone& access) { return gc.atomsZone; } JS::Zone* unsafeAtomsZone() { return gc.atomsZone; } #ifdef DEBUG bool isAtomsZone(const JS::Zone* zone) const { return zone == gc.atomsZone; } #endif bool activeGCInAtomsZone(); js::SymbolRegistry& symbolRegistry() { return symbolRegistry_.ref(); } // Permanent atoms are fixed during initialization of the runtime and are // not modified or collected until the runtime is destroyed. These may be // shared with another, longer living runtime through |parentRuntime| and // can be freely accessed with no locking necessary. // Permanent atoms pre-allocated for general use. js::WriteOnceData staticStrings; // Cached pointers to various permanent property names. js::WriteOnceData commonNames; js::WriteOnceData commonParserNames; // All permanent atoms in the runtime, other than those in staticStrings. // Access to this does not require a lock because it is frozen and thus // read-only. const js::FrozenAtomSet* permanentAtoms() const { MOZ_ASSERT(permanentAtomsPopulated()); return permanentAtoms_.ref(); } // The permanent atoms table is populated during initialization. bool permanentAtomsPopulated() const { return permanentAtoms_; } // For internal use, return the permanent atoms table while it is being // populated. js::AtomSet* permanentAtomsDuringInit() const { MOZ_ASSERT(!permanentAtoms_); return permanentAtomsDuringInit_.ref(); } bool initMainAtomsTables(JSContext* cx); void tracePermanentAtoms(JSTracer* trc); // Cached well-known symbols (ES6 rev 24 6.1.5.1). Like permanent atoms, // these are shared with the parentRuntime, if any. js::WriteOnceData wellKnownSymbols; #ifdef JS_HAS_INTL_API /* Shared Intl data for this runtime. */ js::MainThreadData sharedIntlData; void traceSharedIntlData(JSTracer* trc); #endif // Table of bytecode and other data that may be shared across scripts // within the runtime. This may be modified by threads using // AutoLockScriptData. private: js::ScriptDataLockData scriptDataTable_; public: js::SharedImmutableScriptDataTable& scriptDataTable( const js::AutoLockScriptData& lock) { return scriptDataTable_.ref(); } private: static mozilla::Atomic liveRuntimesCount; public: static bool hasLiveRuntimes() { return liveRuntimesCount > 0; } explicit JSRuntime(JSRuntime* parentRuntime); ~JSRuntime(); // destroyRuntime is used instead of a destructor, to ensure the downcast // to JSContext remains valid. The final GC triggered here depends on this. void destroyRuntime(); bool init(JSContext* cx, uint32_t maxbytes); JSRuntime* thisFromCtor() { return this; } private: // Number of live SharedArrayBuffer objects, including those in Wasm shared // memories. uint64_t to avoid any risk of overflow. js::MainThreadData liveSABs; public: void incSABCount() { MOZ_RELEASE_ASSERT(liveSABs != UINT64_MAX); liveSABs++; } void decSABCount() { MOZ_RELEASE_ASSERT(liveSABs > 0); liveSABs--; } bool hasLiveSABs() const { return liveSABs > 0; } public: js::MainThreadData beforeWaitCallback; js::MainThreadData afterWaitCallback; public: void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); } /* * This should be called after system malloc/calloc/realloc returns nullptr * to try to recove some memory or to report an error. For realloc, the * original pointer must be passed as reallocPtr. * * The function must be called outside the GC lock. */ JS_FRIEND_API void* onOutOfMemory(js::AllocFunction allocator, arena_id_t arena, size_t nbytes, void* reallocPtr = nullptr, JSContext* maybecx = nullptr); /* onOutOfMemory but can call OnLargeAllocationFailure. */ JS_FRIEND_API void* onOutOfMemoryCanGC(js::AllocFunction allocator, arena_id_t arena, size_t nbytes, void* reallocPtr = nullptr); static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024; void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes); private: // Settings for how helper threads can be used. mozilla::Atomic offthreadIonCompilationEnabled_; mozilla::Atomic parallelParsingEnabled_; #ifdef DEBUG mozilla::Atomic offThreadParsesRunning_; mozilla::Atomic offThreadParsingBlocked_; #endif js::MainThreadData autoWritableJitCodeActive_; public: // Note: these values may be toggled dynamically (in response to about:config // prefs changing). void setOffthreadIonCompilationEnabled(bool value) { offthreadIonCompilationEnabled_ = value; } bool canUseOffthreadIonCompilation() const { return offthreadIonCompilationEnabled_; } void setParallelParsingEnabled(bool value) { parallelParsingEnabled_ = value; } bool canUseParallelParsing() const { return parallelParsingEnabled_; } #ifdef DEBUG void incOffThreadParsesRunning() { MOZ_ASSERT(!isOffThreadParsingBlocked()); offThreadParsesRunning_++; } void decOffThreadParsesRunning() { MOZ_ASSERT(isOffThreadParseRunning()); offThreadParsesRunning_--; } bool isOffThreadParseRunning() const { return offThreadParsesRunning_; } bool isOffThreadParsingBlocked() const { return offThreadParsingBlocked_; } void setOffThreadParsingBlocked(bool blocked) { MOZ_ASSERT(offThreadParsingBlocked_ != blocked); MOZ_ASSERT(!isOffThreadParseRunning()); offThreadParsingBlocked_ = blocked; } #endif void toggleAutoWritableJitCodeActive(bool b) { MOZ_ASSERT(autoWritableJitCodeActive_ != b, "AutoWritableJitCode should not be nested."); autoWritableJitCodeActive_ = b; } /* See comment for JS::SetOutOfMemoryCallback in jsapi.h. */ js::MainThreadData oomCallback; js::MainThreadData oomCallbackData; /* * Debugger.Memory functions like takeCensus use this embedding-provided * function to assess the size of malloc'd blocks of memory. */ js::MainThreadData debuggerMallocSizeOf; /* Last time at which an animation was played for this runtime. */ js::MainThreadData lastAnimationTime; private: /* The stack format for the current runtime. Only valid on non-child * runtimes. */ mozilla::Atomic stackFormat_; public: js::StackFormat stackFormat() const { const JSRuntime* rt = this; while (rt->parentRuntime) { MOZ_ASSERT(rt->stackFormat_ == js::StackFormat::Default); rt = rt->parentRuntime; } MOZ_ASSERT(rt->stackFormat_ != js::StackFormat::Default); return rt->stackFormat_; } void setStackFormat(js::StackFormat format) { MOZ_ASSERT(!parentRuntime); MOZ_ASSERT(format != js::StackFormat::Default); stackFormat_ = format; } private: js::MainThreadData caches_; public: js::RuntimeCaches& caches() { return caches_.ref(); } // List of all the live wasm::Instances in the runtime. Equal to the union // of all instances registered in all JS::Realms. Accessed from watchdog // threads for purposes of wasm::InterruptRunningCode(). js::ExclusiveData wasmInstances; // The implementation-defined abstract operation HostResolveImportedModule. js::MainThreadData moduleResolveHook; // A hook that implements the abstract operations // HostGetImportMetaProperties and HostFinalizeImportMeta. js::MainThreadData moduleMetadataHook; // A hook that implements the abstract operation // HostImportModuleDynamically. This is also used to enable/disable dynamic // module import and can accessed by off-thread parsing. mozilla::Atomic moduleDynamicImportHook; // Hooks called when script private references are created and destroyed. js::MainThreadData scriptPrivateAddRefHook; js::MainThreadData scriptPrivateReleaseHook; void addRefScriptPrivate(const JS::Value& value) { if (!value.isUndefined() && scriptPrivateAddRefHook) { scriptPrivateAddRefHook(value); } } void releaseScriptPrivate(const JS::Value& value) { if (!value.isUndefined() && scriptPrivateReleaseHook) { scriptPrivateReleaseHook(value); } } public: #if defined(NIGHTLY_BUILD) // Support for informing the embedding of any error thrown. // This mechanism is designed to let the embedding // log/report/fail in case certain errors are thrown // (e.g. SyntaxError, ReferenceError or TypeError // in critical code). struct ErrorInterceptionSupport { ErrorInterceptionSupport() : isExecuting(false), interceptor(nullptr) {} // true if the error interceptor is currently executing, // false otherwise. Used to avoid infinite loops. bool isExecuting; // if non-null, any call to `setPendingException` // in this runtime will trigger the call to `interceptor` JSErrorInterceptor* interceptor; }; ErrorInterceptionSupport errorInterception; #endif // defined(NIGHTLY_BUILD) }; // Context for sending telemetry to the embedder from any thread, main or // helper. Obtain a |JSTelemetrySender| by calling |getTelemetrySender()| on // the |JSRuntime|. struct JSTelemetrySender { private: friend struct JSRuntime; JSAccumulateTelemetryDataCallback callback_; explicit JSTelemetrySender(JSAccumulateTelemetryDataCallback callback) : callback_(callback) {} public: JSTelemetrySender() : callback_(nullptr) {} JSTelemetrySender(const JSTelemetrySender& other) = default; explicit JSTelemetrySender(JSRuntime* runtime) : JSTelemetrySender(runtime->getTelemetrySender()) {} // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* // histogram. |key| provides an additional key to identify the histogram. // |sample| is the data to add to the histogram. void addTelemetry(int id, uint32_t sample, const char* key = nullptr) { if (callback_) { callback_(id, sample, key); } } }; namespace js { static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* vec, size_t len) { // Don't PodZero here because JS::Value is non-trivial. for (size_t i = 0; i < len; i++) { vec[i].setDouble(+0.0); } } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* beg, Value* end) { MakeRangeGCSafe(beg, end - beg); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(jsid* beg, jsid* end) { std::fill(beg, end, INT_TO_JSID(0)); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(jsid* vec, size_t len) { MakeRangeGCSafe(vec, vec + len); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** beg, Shape** end) { std::fill(beg, end, nullptr); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** vec, size_t len) { MakeRangeGCSafe(vec, vec + len); } static MOZ_ALWAYS_INLINE void SetValueRangeToUndefined(Value* beg, Value* end) { for (Value* v = beg; v != end; ++v) { v->setUndefined(); } } static MOZ_ALWAYS_INLINE void SetValueRangeToUndefined(Value* vec, size_t len) { SetValueRangeToUndefined(vec, vec + len); } static MOZ_ALWAYS_INLINE void SetValueRangeToNull(Value* beg, Value* end) { for (Value* v = beg; v != end; ++v) { v->setNull(); } } static MOZ_ALWAYS_INLINE void SetValueRangeToNull(Value* vec, size_t len) { SetValueRangeToNull(vec, vec + len); } extern const JSSecurityCallbacks NullSecurityCallbacks; // This callback is set by JS::SetProcessLargeAllocationFailureCallback // and may be null. See comment in jsapi.h. extern mozilla::Atomic OnLargeAllocationFailure; // This callback is set by JS::SetBuildIdOp and may be null. See comment in // jsapi.h. extern mozilla::Atomic GetBuildId; extern JS::FilenameValidationCallback gFilenameValidationCallback; // This callback is set by js::SetHelperThreadTaskCallback and may be null. // See comment in jsapi.h. // Returns false if the thread pool fails to dispatch. extern bool (*HelperThreadTaskCallback)(js::UniquePtr); } /* namespace js */ #endif /* vm_Runtime_h */