diff options
Diffstat (limited to 'js/src/vm/Runtime.h')
-rw-r--r-- | js/src/vm/Runtime.h | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h new file mode 100644 index 0000000000..fa8ad7f371 --- /dev/null +++ b/js/src/vm/Runtime.h @@ -0,0 +1,1221 @@ +/* -*- 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 <algorithm> +#include <setjmp.h> + +#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<OOM> 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<Symbol*>. + * + * 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<const ImmutableSymbolPtr*>(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<ScriptAndCounts, 0, SystemAllocPolicy>; + +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<js::InterpreterStack> 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<size_t> 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<bool> 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<uint64_t, mozilla::ReleaseAcquire> + profilerSampleBufferRangeStart_; + + mozilla::Maybe<uint64_t> 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<JSSetUseCounterCallback> useCounterCallback; + + js::MainThreadData<JSGetElementCallback> 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<js::OffThreadPromiseRuntimeState> offThreadPromiseState; + js::UnprotectedData<JS::ConsumeStreamCallback> consumeStreamCallback; + js::UnprotectedData<JS::ReportStreamErrorCallback> reportStreamErrorCallback; + + js::GlobalObject* getIncumbentGlobal(JSContext* cx); + bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, + js::HandleObject promise, + js::Handle<js::GlobalObject*> 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<bool, mozilla::SequentiallyConsistent> hadOutOfMemory; + + /* + * Allow relazifying functions in compartments that are active. This is + * only used by the relazifyFunctions() testing function. + */ + js::MainThreadData<bool> allowRelazificationForTesting; + + /* Zone destroy callback. */ + js::MainThreadData<JSDestroyZoneCallback> destroyZoneCallback; + + /* Compartment destroy callback. */ + js::MainThreadData<JSDestroyCompartmentCallback> destroyCompartmentCallback; + + /* Compartment memory reporting callback. */ + js::MainThreadData<JSSizeOfIncludingThisCompartmentCallback> + 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<JS::DestroyRealmCallback> destroyRealmCallback; + + /* Call this to get the name of a realm. */ + js::MainThreadData<JS::RealmNameCallback> realmNameCallback; + + js::MainThreadData<mozilla::UniquePtr<js::SourceHook>> sourceHook; + + js::MainThreadData<const JSSecurityCallbacks*> securityCallbacks; + js::MainThreadData<const js::DOMCallbacks*> DOMcallbacks; + js::MainThreadData<JSDestroyPrincipalsOp> destroyPrincipals; + js::MainThreadData<JSReadPrincipalsOp> readPrincipals; + + /* Optional warning reporter. */ + js::MainThreadData<JS::WarningReporter> 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<js::SelfHostedLazyScript> selfHostedLazyScript; + + private: + /* Gecko profiling metadata */ + js::UnprotectedData<js::GeckoProfilerRuntime> geckoProfiler_; + + public: + js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); } + + // Heap GC roots for PersistentRooted pointers. + js::MainThreadData<mozilla::EnumeratedArray< + JS::RootKind, JS::RootKind::Limit, + mozilla::LinkedList<JS::PersistentRooted<JS::detail::RootListEntry*>>>> + heapRoots; + + void tracePersistentRoots(JSTracer* trc); + void finishPersistentRoots(); + + void finishRoots(); + + private: + js::UnprotectedData<const JSPrincipals*> trustedPrincipals_; + + public: + void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; } + const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; } + + js::MainThreadData<const JSWrapObjectCallbacks*> wrapObjectCallbacks; + js::MainThreadData<js::PreserveWrapperCallback> preserveWrapperCallback; + js::MainThreadData<js::HasReleasedWrapperCallback> hasReleasedWrapperCallback; + + js::MainThreadData<js::ScriptEnvironmentPreparer*> scriptEnvironmentPreparer; + + js::MainThreadData<JS::CTypesActivityCallback> ctypesActivityCallback; + + private: + js::WriteOnceData<const JSClass*> windowProxyClass_; + + public: + const JSClass* maybeWindowProxyClass() const { return windowProxyClass_; } + void setWindowProxyClass(const JSClass* clasp) { windowProxyClass_ = clasp; } + + private: + js::WriteOnceData<const JSClass*> abortSignalClass_; + js::WriteOnceData<JS::AbortSignalIsAborted> 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<mozilla::LinkedList<JS::detail::WeakCacheBase>> + weakCaches_; + + public: + mozilla::LinkedList<JS::detail::WeakCacheBase>& weakCaches() { + return weakCaches_.ref(); + } + void registerWeakCache(JS::detail::WeakCacheBase* cachep) { + weakCaches().insertBack(cachep); + } + + template <typename T> + struct GlobalObjectWatchersLinkAccess { + static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) { + return aThis->onNewGlobalObjectWatchersLink; + } + }; + + template <typename T> + struct GarbageCollectionWatchersLinkAccess { + static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) { + return aThis->onGarbageCollectionWatchersLink; + } + }; + + using OnNewGlobalWatchersList = + mozilla::DoublyLinkedList<js::Debugger, + GlobalObjectWatchersLinkAccess<js::Debugger>>; + using OnGarbageCollectionWatchersList = mozilla::DoublyLinkedList< + js::Debugger, GarbageCollectionWatchersLinkAccess<js::Debugger>>; + + private: + /* + * List of all enabled Debuggers that have onNewGlobalObject handler + * methods established. + */ + js::MainThreadData<OnNewGlobalWatchersList> onNewGlobalObjectWatchers_; + + /* + * List of all enabled Debuggers that have onGarbageCollection handler + * methods established. + */ + js::MainThreadData<OnGarbageCollectionWatchersList> + onGarbageCollectionWatchers_; + + public: + OnNewGlobalWatchersList& onNewGlobalObjectWatchers() { + return onNewGlobalObjectWatchers_.ref(); + } + + OnGarbageCollectionWatchersList& onGarbageCollectionWatchers() { + return onGarbageCollectionWatchers_.ref(); + } + + private: + /* Linked list of all Debugger objects in the runtime. */ + js::MainThreadData<mozilla::LinkedList<js::Debugger>> debuggerList_; + + public: + mozilla::LinkedList<js::Debugger>& 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<size_t, mozilla::SequentiallyConsistent> numParseTasks; + + // Number of zones which may be operated on by helper threads. + mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> + 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<size_t> 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<JS::RecordAllocationsCallback> recordAllocationCallback; + js::MainThreadData<double> allocationSamplingProbability; + + private: + // Number of debuggee realms in the runtime. + js::MainThreadData<size_t> numDebuggeeRealms_; + + // Number of debuggee realms in the runtime observing code coverage. + js::MainThreadData<size_t> 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<js::GlobalObject*> global); + + /* Locale-specific callbacks for string conversion. */ + js::MainThreadData<const JSLocaleCallbacks*> localeCallbacks; + + /* Default locale for Internationalization API */ + js::MainThreadData<js::UniqueChars> defaultLocale; + + /* If true, new scripts must be created with PC counter information. */ + js::MainThreadOrIonCompileData<bool> profilingScripts; + + /* Strong references on scripts held for PCCount profiling API. */ + js::MainThreadData<JS::PersistentRooted<js::ScriptAndCountsVector>*> + scriptAndCountsVector; + + private: + /* Code coverage output. */ + js::UnprotectedData<js::coverage::LCovRuntime> lcovOutput_; + + public: + js::coverage::LCovRuntime& lcovOutput() { return lcovOutput_.ref(); } + + private: + js::UnprotectedData<js::jit::JitRuntime*> jitRuntime_; + + /* + * Self-hosting state cloned on demand into other compartments. Shared with + * the parent runtime if there is one. + */ + js::WriteOnceData<js::NativeObject*> 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<JSScript*> 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<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_; + mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator(); + + // Used to generate random hash codes for symbols. + mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> + 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<js::PropertyName*> name, + js::Handle<JSFunction*> targetFun); + bool cloneSelfHostedValue(JSContext* cx, js::Handle<js::PropertyName*> 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<bool> gcInitialized; + + bool hasZealMode(js::gc::ZealMode mode) { return gc.hasZealMode(mode); } + + void lockGC() { gc.lockGC(); } + + void unlockGC() { gc.unlockGC(); } + + js::WriteOnceData<js::PropertyName*> emptyString; + + private: + js::MainThreadOrGCTaskData<JSFreeOp*> defaultFreeOp_; + + public: + JSFreeOp* defaultFreeOp() { + MOZ_ASSERT(defaultFreeOp_); + return defaultFreeOp_; + } + +#if !JS_HAS_INTL_API + /* Number localization, used by jsnum.cpp. */ + js::WriteOnceData<const char*> thousandsSeparator; + js::WriteOnceData<const char*> decimalSeparator; + js::WriteOnceData<const char*> numGrouping; +#endif + + private: + mozilla::Maybe<js::SharedImmutableStringsCache> 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<bool> 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<js::AtomsTable*> 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<js::SymbolRegistry> symbolRegistry_; + + js::WriteOnceData<js::AtomSet*> permanentAtomsDuringInit_; + js::WriteOnceData<js::FrozenAtomSet*> 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<js::StaticStrings*> staticStrings; + + // Cached pointers to various permanent property names. + js::WriteOnceData<JSAtomState*> commonNames; + js::WriteOnceData<js::frontend::WellKnownParserAtoms*> 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<js::WellKnownSymbols*> wellKnownSymbols; + +#ifdef JS_HAS_INTL_API + /* Shared Intl data for this runtime. */ + js::MainThreadData<js::intl::SharedIntlData> 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<js::SharedImmutableScriptDataTable> scriptDataTable_; + + public: + js::SharedImmutableScriptDataTable& scriptDataTable( + const js::AutoLockScriptData& lock) { + return scriptDataTable_.ref(); + } + + private: + static mozilla::Atomic<size_t> 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<uint64_t> 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<JS::BeforeWaitCallback> beforeWaitCallback; + js::MainThreadData<JS::AfterWaitCallback> 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<bool, mozilla::SequentiallyConsistent> + offthreadIonCompilationEnabled_; + mozilla::Atomic<bool, mozilla::SequentiallyConsistent> + parallelParsingEnabled_; + +#ifdef DEBUG + mozilla::Atomic<uint32_t> offThreadParsesRunning_; + mozilla::Atomic<bool> offThreadParsingBlocked_; +#endif + + js::MainThreadData<bool> 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<JS::OutOfMemoryCallback> oomCallback; + js::MainThreadData<void*> oomCallbackData; + + /* + * Debugger.Memory functions like takeCensus use this embedding-provided + * function to assess the size of malloc'd blocks of memory. + */ + js::MainThreadData<mozilla::MallocSizeOf> debuggerMallocSizeOf; + + /* Last time at which an animation was played for this runtime. */ + js::MainThreadData<mozilla::TimeStamp> lastAnimationTime; + + private: + /* The stack format for the current runtime. Only valid on non-child + * runtimes. */ + mozilla::Atomic<js::StackFormat, mozilla::ReleaseAcquire> 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<js::RuntimeCaches> 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<js::wasm::InstanceVector> wasmInstances; + + // The implementation-defined abstract operation HostResolveImportedModule. + js::MainThreadData<JS::ModuleResolveHook> moduleResolveHook; + + // A hook that implements the abstract operations + // HostGetImportMetaProperties and HostFinalizeImportMeta. + js::MainThreadData<JS::ModuleMetadataHook> 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<JS::ModuleDynamicImportHook> moduleDynamicImportHook; + + // Hooks called when script private references are created and destroyed. + js::MainThreadData<JS::ScriptPrivateReferenceHook> scriptPrivateAddRefHook; + js::MainThreadData<JS::ScriptPrivateReferenceHook> 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<JS::LargeAllocationFailureCallback> + OnLargeAllocationFailure; + +// This callback is set by JS::SetBuildIdOp and may be null. See comment in +// jsapi.h. +extern mozilla::Atomic<JS::BuildIdOp> 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<js::RunnableTask>); + +} /* namespace js */ + +#endif /* vm_Runtime_h */ |