summaryrefslogtreecommitdiffstats
path: root/js/src/vm/JSContext.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/JSContext.h')
-rw-r--r--js/src/vm/JSContext.h1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h
new file mode 100644
index 0000000000..450b4ac2c4
--- /dev/null
+++ b/js/src/vm/JSContext.h
@@ -0,0 +1,1139 @@
+/* -*- 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/. */
+
+/* JS execution context. */
+
+#ifndef vm_JSContext_h
+#define vm_JSContext_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "builtin/AtomicsObject.h"
+#include "ds/TraceableFifo.h"
+#include "frontend/NameCollections.h"
+#include "gc/Memory.h"
+#include "irregexp/RegExpTypes.h"
+#include "jit/PcScriptCache.h"
+#include "js/ContextOptions.h" // JS::ContextOptions
+#include "js/Exception.h"
+#include "js/GCVector.h"
+#include "js/Interrupt.h"
+#include "js/Promise.h"
+#include "js/Result.h"
+#include "js/Stack.h" // JS::NativeStackBase, JS::NativeStackLimit
+#include "js/Utility.h"
+#include "js/Vector.h"
+#include "threading/ProtectedData.h"
+#include "util/StructuredSpewer.h"
+#include "vm/Activation.h" // js::Activation
+#include "vm/MallocProvider.h"
+#include "vm/Runtime.h"
+#include "wasm/WasmContext.h"
+
+struct JS_PUBLIC_API JSContext;
+
+struct DtoaState;
+
+namespace js {
+
+class AutoAllocInAtomsZone;
+class AutoMaybeLeaveAtomsZone;
+class AutoRealm;
+
+namespace jit {
+class ICScript;
+class JitActivation;
+class JitContext;
+class DebugModeOSRVolatileJitFrameIter;
+} // namespace jit
+
+/* Detects cycles when traversing an object graph. */
+class MOZ_RAII AutoCycleDetector {
+ public:
+ using Vector = GCVector<JSObject*, 8>;
+
+ AutoCycleDetector(JSContext* cx, HandleObject objArg)
+ : cx(cx), obj(cx, objArg), cyclic(true) {}
+
+ ~AutoCycleDetector();
+
+ bool init();
+
+ bool foundCycle() { return cyclic; }
+
+ private:
+ JSContext* cx;
+ RootedObject obj;
+ bool cyclic;
+};
+
+struct AutoResolving;
+
+struct FrontendErrors; // vm/HelperThreadState.h
+
+class InternalJobQueue : public JS::JobQueue {
+ public:
+ explicit InternalJobQueue(JSContext* cx)
+ : queue(cx, SystemAllocPolicy()), draining_(false), interrupted_(false) {}
+ ~InternalJobQueue() = default;
+
+ // JS::JobQueue methods.
+ JSObject* getIncumbentGlobal(JSContext* cx) override;
+ bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise,
+ JS::HandleObject job, JS::HandleObject allocationSite,
+ JS::HandleObject incumbentGlobal) override;
+ void runJobs(JSContext* cx) override;
+ bool empty() const override;
+
+ // If we are currently in a call to runJobs(), make that call stop processing
+ // jobs once the current one finishes, and return. If we are not currently in
+ // a call to runJobs, make all future calls return immediately.
+ void interrupt() { interrupted_ = true; }
+
+ // Return the front element of the queue, or nullptr if the queue is empty.
+ // This is only used by shell testing functions.
+ JSObject* maybeFront() const;
+
+#ifdef DEBUG
+ JSObject* copyJobs(JSContext* cx);
+#endif
+
+ private:
+ using Queue = js::TraceableFifo<JSObject*, 0, SystemAllocPolicy>;
+
+ JS::PersistentRooted<Queue> queue;
+
+ // True if we are in the midst of draining jobs from this queue. We use this
+ // to avoid re-entry (nested calls simply return immediately).
+ bool draining_;
+
+ // True if we've been asked to interrupt draining jobs. Set by interrupt().
+ bool interrupted_;
+
+ class SavedQueue;
+ js::UniquePtr<JobQueue::SavedJobQueue> saveJobQueue(JSContext*) override;
+};
+
+class AutoLockScriptData;
+
+/* Thread Local Storage slot for storing the context for a thread. */
+extern MOZ_THREAD_LOCAL(JSContext*) TlsContext;
+
+enum class ContextKind {
+ Uninitialized,
+
+ // Context for the main thread of a JSRuntime.
+ MainThread,
+
+ // Context for a helper thread.
+ HelperThread
+};
+
+#ifdef DEBUG
+JSContext* MaybeGetJSContext();
+bool CurrentThreadIsParseThread();
+#endif
+
+enum class InterruptReason : uint32_t {
+ GC = 1 << 0,
+ AttachIonCompilations = 1 << 1,
+ CallbackUrgent = 1 << 2,
+ CallbackCanWait = 1 << 3,
+};
+
+enum class ShouldCaptureStack { Maybe, Always };
+
+} /* namespace js */
+
+/*
+ * A JSContext encapsulates the thread local state used when using the JS
+ * runtime.
+ */
+struct JS_PUBLIC_API JSContext : public JS::RootingContext,
+ public js::MallocProvider<JSContext> {
+ JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
+ ~JSContext();
+
+ bool init(js::ContextKind kind);
+
+ private:
+ js::UnprotectedData<JSRuntime*> runtime_;
+ js::WriteOnceData<js::ContextKind> kind_;
+
+ js::ContextData<JS::ContextOptions> options_;
+
+ // Thread that the JSContext is currently running on, if in use.
+ js::ThreadId currentThread_;
+
+ js::FrontendErrors* errors_;
+
+ // When a helper thread is using a context, it may need to periodically
+ // free unused memory.
+ mozilla::Atomic<bool, mozilla::ReleaseAcquire> freeUnusedMemory;
+
+ // Are we currently timing execution? This flag ensures that we do not
+ // double-count execution time in reentrant situations.
+ js::ContextData<bool> measuringExecutionTime_;
+
+ // This variable is used by the HelperThread scheduling to update the priority
+ // of task based on whether JavaScript is being executed on the main thread.
+ mozilla::Atomic<bool, mozilla::ReleaseAcquire> isExecuting_;
+
+ public:
+ // This is used by helper threads to change the runtime their context is
+ // currently operating on.
+ void setRuntime(JSRuntime* rt);
+
+ void setHelperThread(const JS::ContextOptions& options,
+ const js::AutoLockHelperThreadState& locked);
+ void clearHelperThread(const js::AutoLockHelperThreadState& locked);
+
+ bool contextAvailable(js::AutoLockHelperThreadState& locked) {
+ MOZ_ASSERT(kind_ == js::ContextKind::HelperThread);
+ return currentThread_ == js::ThreadId();
+ }
+
+ void setFreeUnusedMemory(bool shouldFree) { freeUnusedMemory = shouldFree; }
+
+ bool shouldFreeUnusedMemory() const {
+ return kind_ == js::ContextKind::HelperThread && freeUnusedMemory;
+ }
+
+ bool isMeasuringExecutionTime() const { return measuringExecutionTime_; }
+ void setIsMeasuringExecutionTime(bool value) {
+ measuringExecutionTime_ = value;
+ }
+
+ // While JSContexts are meant to be used on a single thread, this reference is
+ // meant to be shared to helper thread tasks. This is used by helper threads
+ // to change the priority of tasks based on whether JavaScript is executed on
+ // the main thread.
+ const mozilla::Atomic<bool, mozilla::ReleaseAcquire>& isExecutingRef() const {
+ return isExecuting_;
+ }
+ void setIsExecuting(bool value) { isExecuting_ = value; }
+
+#ifdef DEBUG
+ bool isInitialized() const { return kind_ != js::ContextKind::Uninitialized; }
+#endif
+
+ bool isMainThreadContext() const {
+ return kind_ == js::ContextKind::MainThread;
+ }
+
+ bool isHelperThreadContext() const {
+ return kind_ == js::ContextKind::HelperThread;
+ }
+
+ template <typename T>
+ bool isInsideCurrentZone(T thing) const {
+ return thing->zoneFromAnyThread() == zone_;
+ }
+
+ template <typename T>
+ inline bool isInsideCurrentCompartment(T thing) const {
+ return thing->compartment() == compartment();
+ }
+
+ void onOutOfMemory();
+ void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
+ size_t nbytes, void* reallocPtr = nullptr) {
+ if (isHelperThreadContext()) {
+ addPendingOutOfMemory();
+ return nullptr;
+ }
+ return runtime_->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr, this);
+ }
+
+ void onOverRecursed();
+
+ // Allocate a GC thing.
+ template <typename T, js::AllowGC allowGC = js::CanGC, typename... Args>
+ T* newCell(Args&&... args) {
+ return js::gc::CellAllocator::template NewCell<T, allowGC>(
+ this, std::forward<Args>(args)...);
+ }
+
+ /* Clear the pending exception (if any) due to OOM. */
+ void recoverFromOutOfMemory();
+
+ void reportAllocationOverflow();
+
+ // Accessors for immutable runtime data.
+ JSAtomState& names() { return *runtime_->commonNames; }
+ js::StaticStrings& staticStrings() { return *runtime_->staticStrings; }
+ bool permanentAtomsPopulated() { return runtime_->permanentAtomsPopulated(); }
+ const js::FrozenAtomSet& permanentAtoms() {
+ return *runtime_->permanentAtoms();
+ }
+ js::WellKnownSymbols& wellKnownSymbols() {
+ return *runtime_->wellKnownSymbols;
+ }
+ js::PropertyName* emptyString() { return runtime_->emptyString; }
+ JS::GCContext* gcContext() { return runtime_->gcContext(); }
+ JS::StackKind stackKindForCurrentPrincipal();
+ JS::NativeStackLimit stackLimitForCurrentPrincipal();
+ JS::NativeStackLimit stackLimit(JS::StackKind kind) {
+ MOZ_ASSERT(isMainThreadContext());
+ return nativeStackLimit[kind];
+ }
+ JS::NativeStackLimit stackLimitForJitCode(JS::StackKind kind);
+ size_t gcSystemPageSize() { return js::gc::SystemPageSize(); }
+
+ /*
+ * "Entering" a realm changes cx->realm (which changes cx->global). Note
+ * that this does not push an Activation so it's possible for the caller's
+ * realm to be != cx->realm(). This is not a problem since, in general, most
+ * places in the VM cannot know that they were called from script (e.g.,
+ * they may have been called through the JSAPI via JS_CallFunction) and thus
+ * cannot expect there is a scripted caller.
+ *
+ * Realms should be entered/left in a LIFO fasion. To enter a realm, code
+ * should prefer using AutoRealm over JS::EnterRealm/JS::LeaveRealm.
+ *
+ * Also note that the JIT can enter (same-compartment) realms without going
+ * through these methods - it will update cx->realm_ directly.
+ */
+ private:
+ inline void setRealm(JS::Realm* realm);
+ inline void enterRealm(JS::Realm* realm);
+
+ inline void enterAtomsZone();
+ inline void leaveAtomsZone(JS::Realm* oldRealm);
+ inline void setZone(js::Zone* zone);
+
+ friend class js::AutoAllocInAtomsZone;
+ friend class js::AutoMaybeLeaveAtomsZone;
+ friend class js::AutoRealm;
+
+ public:
+ inline void enterRealmOf(JSObject* target);
+ inline void enterRealmOf(JSScript* target);
+ inline void enterRealmOf(js::Shape* target);
+ inline void enterNullRealm();
+
+ inline void setRealmForJitExceptionHandler(JS::Realm* realm);
+
+ inline void leaveRealm(JS::Realm* oldRealm);
+
+ void setFrontendErrors(js::FrontendErrors* errors) { errors_ = errors; }
+ js::FrontendErrors* frontendErrors() const { return errors_; }
+
+ // Threads may freely access any data in their realm, compartment and zone.
+ JS::Compartment* compartment() const {
+ return realm_ ? JS::GetCompartmentForRealm(realm_) : nullptr;
+ }
+
+ JS::Realm* realm() const { return realm_; }
+
+#ifdef DEBUG
+ bool inAtomsZone() const;
+#endif
+
+ JS::Zone* zone() const {
+ MOZ_ASSERT_IF(!realm() && zone_, inAtomsZone());
+ MOZ_ASSERT_IF(realm(), js::GetRealmZone(realm()) == zone_);
+ return zoneRaw();
+ }
+
+ // For use when the context's zone is being read by another thread and the
+ // compartment and zone pointers might not be in sync.
+ JS::Zone* zoneRaw() const { return zone_; }
+
+ // For JIT use.
+ static size_t offsetOfZone() { return offsetof(JSContext, zone_); }
+
+ // Current global. This is only safe to use within the scope of the
+ // AutoRealm from which it's called.
+ inline js::Handle<js::GlobalObject*> global() const;
+
+ js::AtomsTable& atoms() { return runtime_->atoms(); }
+
+ js::SymbolRegistry& symbolRegistry() { return runtime_->symbolRegistry(); }
+
+ // Methods to access other runtime data that checks locking internally.
+ js::gc::AtomMarkingRuntime& atomMarking() { return runtime_->gc.atomMarking; }
+ void markAtom(JSAtom* atom) { atomMarking().markAtom(this, atom); }
+ void markAtom(JS::Symbol* symbol) { atomMarking().markAtom(this, symbol); }
+ void markId(jsid id) { atomMarking().markId(this, id); }
+ void markAtomValue(const js::Value& value) {
+ atomMarking().markAtomValue(this, value);
+ }
+
+ // Interface for recording telemetry metrics.
+ js::Metrics metrics() { return js::Metrics(runtime_); }
+
+ // Methods specific to any HelperThread for the context.
+ void addPendingOverRecursed();
+ void addPendingOutOfMemory();
+
+ JSRuntime* runtime() { return runtime_; }
+ const JSRuntime* runtime() const { return runtime_; }
+
+ static size_t offsetOfRealm() { return offsetof(JSContext, realm_); }
+
+ friend class JS::AutoSaveExceptionState;
+ friend class js::jit::DebugModeOSRVolatileJitFrameIter;
+ friend void js::ReportOutOfMemory(JSContext*);
+ friend void js::ReportOverRecursed(JSContext*);
+ friend void js::ReportOversizedAllocation(JSContext*, const unsigned);
+
+ public:
+ inline JS::Result<> boolToResult(bool ok);
+
+ /**
+ * Intentionally awkward signpost method that is stationed on the
+ * boundary between Result-using and non-Result-using code.
+ */
+ template <typename V, typename E>
+ bool resultToBool(const JS::Result<V, E>& result) {
+ return result.isOk();
+ }
+
+ template <typename V, typename E>
+ V* resultToPtr(JS::Result<V*, E>& result) {
+ return result.isOk() ? result.unwrap() : nullptr;
+ }
+
+ mozilla::GenericErrorResult<JS::OOM> alreadyReportedOOM();
+ mozilla::GenericErrorResult<JS::Error> alreadyReportedError();
+
+ /*
+ * Points to the most recent JitActivation pushed on the thread.
+ * See JitActivation constructor in vm/Stack.cpp
+ */
+ js::ContextData<js::jit::JitActivation*> jitActivation;
+
+ // Shim for V8 interfaces used by irregexp code
+ js::ContextData<js::irregexp::Isolate*> isolate;
+
+ /*
+ * Points to the most recent activation running on the thread.
+ * See Activation comment in vm/Stack.h.
+ */
+ js::ContextData<js::Activation*> activation_;
+
+ /*
+ * Points to the most recent profiling activation running on the
+ * thread.
+ */
+ js::Activation* volatile profilingActivation_;
+
+ public:
+ js::Activation* activation() const { return activation_; }
+ static size_t offsetOfActivation() {
+ return offsetof(JSContext, activation_);
+ }
+
+ js::Activation* profilingActivation() const { return profilingActivation_; }
+ static size_t offsetOfProfilingActivation() {
+ return offsetof(JSContext, profilingActivation_);
+ }
+
+ static size_t offsetOfJitActivation() {
+ return offsetof(JSContext, jitActivation);
+ }
+
+#ifdef DEBUG
+ static size_t offsetOfInUnsafeCallWithABI() {
+ return offsetof(JSContext, inUnsafeCallWithABI);
+ }
+#endif
+
+ static size_t offsetOfInlinedICScript() {
+ return offsetof(JSContext, inlinedICScript_);
+ }
+
+ public:
+ js::InterpreterStack& interpreterStack() {
+ return runtime()->interpreterStack();
+ }
+
+ private:
+ // Base address of the native stack for the current thread.
+ mozilla::Maybe<JS::NativeStackBase> nativeStackBase_;
+
+ public:
+ JS::NativeStackBase nativeStackBase() const {
+ MOZ_ASSERT(isMainThreadContext());
+ return *nativeStackBase_;
+ }
+
+ public:
+ /* If non-null, report JavaScript entry points to this monitor. */
+ js::ContextData<JS::dbg::AutoEntryMonitor*> entryMonitor;
+
+ /*
+ * Stack of debuggers that currently disallow debuggee execution.
+ *
+ * When we check for NX we are inside the debuggee compartment, and thus a
+ * stack of Debuggers that have prevented execution need to be tracked to
+ * enter the correct Debugger compartment to report the error.
+ */
+ js::ContextData<js::EnterDebuggeeNoExecute*> noExecuteDebuggerTop;
+
+#ifdef DEBUG
+ js::ContextData<uint32_t> inUnsafeCallWithABI;
+ js::ContextData<bool> hasAutoUnsafeCallWithABI;
+#endif
+
+#ifdef JS_SIMULATOR
+ private:
+ js::ContextData<js::jit::Simulator*> simulator_;
+
+ public:
+ js::jit::Simulator* simulator() const;
+ JS::NativeStackLimit* addressOfSimulatorStackLimit();
+#endif
+
+ public:
+ // State used by util/DoubleToString.cpp.
+ js::ContextData<DtoaState*> dtoaState;
+
+ /*
+ * When this flag is non-zero, any attempt to GC will be skipped. See the
+ * AutoSuppressGC class for for details.
+ */
+ js::ContextData<int32_t> suppressGC;
+
+#ifdef FUZZING_JS_FUZZILLI
+ uint32_t executionHash;
+ uint32_t executionHashInputs;
+#endif
+
+#ifdef DEBUG
+ js::ContextData<size_t> noNurseryAllocationCheck;
+
+ /*
+ * If this is 0, all cross-compartment proxies must be registered in the
+ * wrapper map. This checking must be disabled temporarily while creating
+ * new wrappers. When non-zero, this records the recursion depth of wrapper
+ * creation.
+ */
+ js::ContextData<uintptr_t> disableStrictProxyCheckingCount;
+
+ bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
+ void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
+ void allowNurseryAlloc() {
+ MOZ_ASSERT(!isNurseryAllocAllowed());
+ --noNurseryAllocationCheck;
+ }
+
+ bool isStrictProxyCheckingEnabled() {
+ return disableStrictProxyCheckingCount == 0;
+ }
+ void disableStrictProxyChecking() { ++disableStrictProxyCheckingCount; }
+ void enableStrictProxyChecking() {
+ MOZ_ASSERT(disableStrictProxyCheckingCount > 0);
+ --disableStrictProxyCheckingCount;
+ }
+#endif
+
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+ // We are currently running a simulated OOM test.
+ js::ContextData<bool> runningOOMTest;
+#endif
+
+ /*
+ * Some regions of code are hard for the static rooting hazard analysis to
+ * understand. In those cases, we trade the static analysis for a dynamic
+ * analysis. When this is non-zero, we should assert if we trigger, or
+ * might trigger, a GC.
+ */
+ js::ContextData<int> inUnsafeRegion;
+
+ // Count of AutoDisableGenerationalGC instances on the thread's stack.
+ js::ContextData<unsigned> generationalDisabled;
+
+ // Some code cannot tolerate compacting GC so it can be disabled temporarily
+ // with AutoDisableCompactingGC which uses this counter.
+ js::ContextData<unsigned> compactingDisabledCount;
+
+ private:
+ // Pools used for recycling name maps and vectors when parsing and
+ // emitting bytecode. Purged on GC when there are no active script
+ // compilations.
+ js::ContextData<js::frontend::NameCollectionPool> frontendCollectionPool_;
+
+ public:
+ js::frontend::NameCollectionPool& frontendCollectionPool() {
+ return frontendCollectionPool_.ref();
+ }
+
+ void verifyIsSafeToGC() {
+ MOZ_DIAGNOSTIC_ASSERT(!inUnsafeRegion,
+ "[AutoAssertNoGC] possible GC in GC-unsafe region");
+ }
+
+ bool isInUnsafeRegion() const { return bool(inUnsafeRegion); }
+
+ // For JIT use.
+ void resetInUnsafeRegion() {
+ MOZ_ASSERT(inUnsafeRegion >= 0);
+ inUnsafeRegion = 0;
+ }
+
+ static constexpr size_t offsetOfInUnsafeRegion() {
+ return offsetof(JSContext, inUnsafeRegion);
+ }
+
+ /* Whether sampling should be enabled or not. */
+ private:
+ mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
+ suppressProfilerSampling;
+
+ public:
+ bool isProfilerSamplingEnabled() const { return !suppressProfilerSampling; }
+ void disableProfilerSampling() { suppressProfilerSampling = true; }
+ void enableProfilerSampling() { suppressProfilerSampling = false; }
+
+ private:
+ js::wasm::Context wasm_;
+
+ public:
+ js::wasm::Context& wasm() { return wasm_; }
+
+ /* Temporary arena pool used while compiling and decompiling. */
+ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
+
+ private:
+ js::ContextData<js::LifoAlloc> tempLifoAlloc_;
+
+ public:
+ js::LifoAlloc& tempLifoAlloc() { return tempLifoAlloc_.ref(); }
+ const js::LifoAlloc& tempLifoAlloc() const { return tempLifoAlloc_.ref(); }
+ js::LifoAlloc& tempLifoAllocNoCheck() { return tempLifoAlloc_.refNoCheck(); }
+
+ js::ContextData<uint32_t> debuggerMutations;
+
+ // Cache for jit::GetPcScript().
+ js::ContextData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache;
+
+ private:
+ // Indicates if an exception is pending and the reason for it.
+ js::ContextData<JS::ExceptionStatus> status;
+ js::ContextData<JS::PersistentRooted<JS::Value>>
+ unwrappedException_; /* most-recently-thrown exception */
+ js::ContextData<JS::PersistentRooted<js::SavedFrame*>>
+ unwrappedExceptionStack_; /* stack when the exception was thrown */
+
+ JS::Value& unwrappedException() {
+ if (!unwrappedException_.ref().initialized()) {
+ unwrappedException_.ref().init(this);
+ }
+ return unwrappedException_.ref().get();
+ }
+
+ js::SavedFrame*& unwrappedExceptionStack() {
+ if (!unwrappedExceptionStack_.ref().initialized()) {
+ unwrappedExceptionStack_.ref().init(this);
+ }
+ return unwrappedExceptionStack_.ref().get();
+ }
+
+#ifdef DEBUG
+ // True if this context has ever thrown an exception because of an exceeded
+ // limit: stack space (ReportOverRecursed), memory (ReportOutOfMemory), or
+ // some other self-imposed limit (eg ReportOversizedAllocation). Used when
+ // detecting bailout loops in WarpOracle: bailout loops involving resource
+ // exhaustion are generally not interesting.
+ js::ContextData<bool> hadResourceExhaustion_;
+
+ public:
+ bool hadResourceExhaustion() const {
+ return hadResourceExhaustion_ || js::oom::simulator.isThreadSimulatingAny();
+ }
+#endif
+
+ public:
+ void reportResourceExhaustion() {
+#ifdef DEBUG
+ hadResourceExhaustion_ = true;
+#endif
+ }
+
+ js::ContextData<int32_t> reportGranularity; /* see vm/Probes.h */
+
+ js::ContextData<js::AutoResolving*> resolvingList;
+
+#ifdef DEBUG
+ js::ContextData<js::AutoEnterPolicy*> enteredPolicy;
+#endif
+
+ /* True if generating an error, to prevent runaway recursion. */
+ js::ContextData<bool> generatingError;
+
+ private:
+ /* State for object and array toSource conversion. */
+ js::ContextData<js::AutoCycleDetector::Vector> cycleDetectorVector_;
+
+ public:
+ js::AutoCycleDetector::Vector& cycleDetectorVector() {
+ return cycleDetectorVector_.ref();
+ }
+ const js::AutoCycleDetector::Vector& cycleDetectorVector() const {
+ return cycleDetectorVector_.ref();
+ }
+
+ /* Client opaque pointer. */
+ js::UnprotectedData<void*> data;
+
+ void initJitStackLimit();
+ void resetJitStackLimit();
+
+ public:
+ JS::ContextOptions& options() { return options_.ref(); }
+
+ bool runtimeMatches(JSRuntime* rt) const { return runtime_ == rt; }
+
+ private:
+ /*
+ * Youngest frame of a saved stack that will be picked up as an async stack
+ * by any new Activation, and is nullptr when no async stack should be used.
+ *
+ * The JS::AutoSetAsyncStackForNewCalls class can be used to set this.
+ *
+ * New activations will reset this to nullptr on construction after getting
+ * the current value, and will restore the previous value on destruction.
+ */
+ js::ContextData<JS::PersistentRooted<js::SavedFrame*>>
+ asyncStackForNewActivations_;
+
+ public:
+ js::SavedFrame*& asyncStackForNewActivations() {
+ if (!asyncStackForNewActivations_.ref().initialized()) {
+ asyncStackForNewActivations_.ref().init(this);
+ }
+ return asyncStackForNewActivations_.ref().get();
+ }
+
+ /*
+ * Value of asyncCause to be attached to asyncStackForNewActivations.
+ */
+ js::ContextData<const char*> asyncCauseForNewActivations;
+
+ /*
+ * True if the async call was explicitly requested, e.g. via
+ * callFunctionWithAsyncStack.
+ */
+ js::ContextData<bool> asyncCallIsExplicit;
+
+ bool currentlyRunningInInterpreter() const {
+ return activation()->isInterpreter();
+ }
+ bool currentlyRunningInJit() const { return activation()->isJit(); }
+ js::InterpreterFrame* interpreterFrame() const {
+ return activation()->asInterpreter()->current();
+ }
+ js::InterpreterRegs& interpreterRegs() const {
+ return activation()->asInterpreter()->regs();
+ }
+
+ /*
+ * Get the topmost script and optional pc on the stack. By default, this
+ * function only returns a JSScript in the current realm, returning nullptr
+ * if the current script is in a different realm. This behavior can be
+ * overridden by passing AllowCrossRealm::Allow.
+ */
+ enum class AllowCrossRealm { DontAllow = false, Allow = true };
+ inline JSScript* currentScript(
+ jsbytecode** pc = nullptr,
+ AllowCrossRealm allowCrossRealm = AllowCrossRealm::DontAllow) const;
+
+ inline js::Nursery& nursery();
+ inline void minorGC(JS::GCReason reason);
+
+ public:
+ bool isExceptionPending() const {
+ return JS::IsCatchableExceptionStatus(status);
+ }
+
+ [[nodiscard]] bool getPendingException(JS::MutableHandleValue rval);
+
+ js::SavedFrame* getPendingExceptionStack();
+
+ bool isThrowingDebuggeeWouldRun();
+ bool isClosingGenerator();
+
+ void setPendingException(JS::HandleValue v,
+ JS::Handle<js::SavedFrame*> stack);
+ void setPendingException(JS::HandleValue v,
+ js::ShouldCaptureStack captureStack);
+
+ void clearPendingException() {
+ status = JS::ExceptionStatus::None;
+ unwrappedException().setUndefined();
+ unwrappedExceptionStack() = nullptr;
+ }
+
+ bool isThrowingOutOfMemory() const {
+ return status == JS::ExceptionStatus::OutOfMemory;
+ }
+ bool isThrowingOverRecursed() const {
+ return status == JS::ExceptionStatus::OverRecursed;
+ }
+ bool isPropagatingForcedReturn() const {
+ return status == JS::ExceptionStatus::ForcedReturn;
+ }
+ void setPropagatingForcedReturn() {
+ MOZ_ASSERT(status == JS::ExceptionStatus::None);
+ status = JS::ExceptionStatus::ForcedReturn;
+ }
+ void clearPropagatingForcedReturn() {
+ MOZ_ASSERT(status == JS::ExceptionStatus::ForcedReturn);
+ status = JS::ExceptionStatus::None;
+ }
+
+ /*
+ * See JS_SetTrustedPrincipals in jsapi.h.
+ * Note: !cx->realm() is treated as trusted.
+ */
+ inline bool runningWithTrustedPrincipals();
+
+ // Checks if the page's Content-Security-Policy (CSP) allows
+ // runtime code generation "unsafe-eval", or "wasm-unsafe-eval" for Wasm.
+ bool isRuntimeCodeGenEnabled(JS::RuntimeCode kind, js::HandleString code);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ void trace(JSTracer* trc);
+
+ inline js::RuntimeCaches& caches();
+
+ public:
+ using InterruptCallbackVector =
+ js::Vector<JSInterruptCallback, 2, js::SystemAllocPolicy>;
+
+ private:
+ js::ContextData<InterruptCallbackVector> interruptCallbacks_;
+
+ public:
+ InterruptCallbackVector& interruptCallbacks() {
+ return interruptCallbacks_.ref();
+ }
+
+ js::ContextData<bool> interruptCallbackDisabled;
+
+ // Bitfield storing InterruptReason values.
+ mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptBits_;
+
+ // Any thread can call requestInterrupt() to request that this thread
+ // stop running. To stop this thread, requestInterrupt sets two fields:
+ // interruptBits_ (a bitset of InterruptReasons) and jitStackLimit (set to
+ // JS::NativeStackLimitMin). The JS engine must continually poll one of these
+ // fields and call handleInterrupt if either field has the interrupt value.
+ //
+ // The point of setting jitStackLimit to JS::NativeStackLimitMin is that JIT
+ // code already needs to guard on jitStackLimit in every function prologue to
+ // avoid stack overflow, so we avoid a second branch on interruptBits_ by
+ // setting jitStackLimit to a value that is guaranteed to fail the guard.)
+ //
+ // Note that the writes to interruptBits_ and jitStackLimit use a Relaxed
+ // Atomic so, while the writes are guaranteed to eventually be visible to
+ // this thread, it can happen in any order. handleInterrupt calls the
+ // interrupt callback if either is set, so it really doesn't matter as long
+ // as the JS engine is continually polling at least one field. In corner
+ // cases, this relaxed ordering could lead to an interrupt handler being
+ // called twice in succession after a single requestInterrupt call, but
+ // that's fine.
+ void requestInterrupt(js::InterruptReason reason);
+ bool handleInterrupt();
+
+ MOZ_ALWAYS_INLINE bool hasAnyPendingInterrupt() const {
+ static_assert(sizeof(interruptBits_) == sizeof(uint32_t),
+ "Assumed by JIT callers");
+ return interruptBits_ != 0;
+ }
+ bool hasPendingInterrupt(js::InterruptReason reason) const {
+ return interruptBits_ & uint32_t(reason);
+ }
+
+ // For JIT use. Points to the inlined ICScript for a baseline script
+ // being invoked as part of a trial inlining. Contains nullptr at
+ // all times except for the brief moment between being set in the
+ // caller and read in the callee's prologue.
+ js::ContextData<js::jit::ICScript*> inlinedICScript_;
+
+ public:
+ void* addressOfInterruptBits() { return &interruptBits_; }
+ void* addressOfJitStackLimit() {
+ MOZ_ASSERT(isMainThreadContext());
+ return &jitStackLimit;
+ }
+ void* addressOfJitStackLimitNoInterrupt() {
+ MOZ_ASSERT(isMainThreadContext());
+ return &jitStackLimitNoInterrupt;
+ }
+ void* addressOfZone() { return &zone_; }
+
+ const void* addressOfRealm() const { return &realm_; }
+
+ void* addressOfInlinedICScript() { return &inlinedICScript_; }
+
+ // Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics
+ // object.
+ js::FutexThread fx;
+
+ mozilla::Atomic<JS::NativeStackLimit, mozilla::Relaxed> jitStackLimit;
+
+ // Like jitStackLimit, but not reset to trigger interrupts.
+ js::ContextData<JS::NativeStackLimit> jitStackLimitNoInterrupt;
+
+ // Queue of pending jobs as described in ES2016 section 8.4.
+ //
+ // This is a non-owning pointer to either:
+ // - a JobQueue implementation the embedding provided by calling
+ // JS::SetJobQueue, owned by the embedding, or
+ // - our internal JobQueue implementation, established by calling
+ // js::UseInternalJobQueues, owned by JSContext::internalJobQueue below.
+ js::ContextData<JS::JobQueue*> jobQueue;
+
+ // If the embedding has called js::UseInternalJobQueues, this is the owning
+ // pointer to our internal JobQueue implementation, which JSContext::jobQueue
+ // borrows.
+ js::ContextData<js::UniquePtr<js::InternalJobQueue>> internalJobQueue;
+
+ // True if jobQueue is empty, or we are running the last job in the queue.
+ // Such conditions permit optimizations around `await` expressions.
+ js::ContextData<bool> canSkipEnqueuingJobs;
+
+ js::ContextData<JS::PromiseRejectionTrackerCallback>
+ promiseRejectionTrackerCallback;
+ js::ContextData<void*> promiseRejectionTrackerCallbackData;
+
+ JSObject* getIncumbentGlobal(JSContext* cx);
+ bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job,
+ js::HandleObject promise,
+ js::HandleObject incumbentGlobal);
+ void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
+ void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
+
+ private:
+ template <class... Args>
+ inline void checkImpl(const Args&... args);
+
+ bool contextChecksEnabled() const {
+ // Don't perform these checks when called from a finalizer. The checking
+ // depends on other objects not having been swept yet.
+ return !RuntimeHeapIsCollecting(runtime()->heapState());
+ }
+
+ public:
+ // Assert the arguments are in this context's realm (for scripts),
+ // compartment (for objects) or zone (for strings, symbols).
+ template <class... Args>
+ inline void check(const Args&... args);
+ template <class... Args>
+ inline void releaseCheck(const Args&... args);
+ template <class... Args>
+ MOZ_ALWAYS_INLINE void debugOnlyCheck(const Args&... args);
+
+#ifdef JS_STRUCTURED_SPEW
+ private:
+ // Spewer for this thread
+ js::UnprotectedData<js::StructuredSpewer> structuredSpewer_;
+
+ public:
+ js::StructuredSpewer& spewer() { return structuredSpewer_.ref(); }
+#endif
+
+ // During debugger evaluations which need to observe native calls, JITs are
+ // completely disabled. This flag indicates whether we are in this state, and
+ // the debugger which initiated the evaluation. This debugger has other
+ // references on the stack and does not need to be traced.
+ js::ContextData<js::Debugger*> insideDebuggerEvaluationWithOnNativeCallHook;
+
+}; /* struct JSContext */
+
+inline JS::Result<> JSContext::boolToResult(bool ok) {
+ if (MOZ_LIKELY(ok)) {
+ MOZ_ASSERT(!isExceptionPending());
+ MOZ_ASSERT(!isPropagatingForcedReturn());
+ return JS::Ok();
+ }
+ return JS::Result<>(JS::Error());
+}
+
+inline JSContext* JSRuntime::mainContextFromOwnThread() {
+ MOZ_ASSERT(mainContextFromAnyThread() == js::TlsContext.get());
+ return mainContextFromAnyThread();
+}
+
+namespace js {
+
+struct MOZ_RAII AutoResolving {
+ public:
+ enum Kind { LOOKUP, WATCH };
+
+ AutoResolving(JSContext* cx, HandleObject obj, HandleId id,
+ Kind kind = LOOKUP)
+ : context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList) {
+ MOZ_ASSERT(obj);
+ cx->resolvingList = this;
+ }
+
+ ~AutoResolving() {
+ MOZ_ASSERT(context->resolvingList == this);
+ context->resolvingList = link;
+ }
+
+ bool alreadyStarted() const { return link && alreadyStartedSlow(); }
+
+ private:
+ bool alreadyStartedSlow() const;
+
+ JSContext* const context;
+ HandleObject object;
+ HandleId id;
+ Kind const kind;
+ AutoResolving* const link;
+};
+
+/*
+ * Create and destroy functions for JSContext, which is manually allocated
+ * and exclusively owned.
+ */
+extern JSContext* NewContext(uint32_t maxBytes, JSRuntime* parentRuntime);
+
+extern void DestroyContext(JSContext* cx);
+
+/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
+extern void ReportUsageErrorASCII(JSContext* cx, HandleObject callee,
+ const char* msg);
+
+extern void ReportIsNotDefined(JSContext* cx, Handle<PropertyName*> name);
+
+extern void ReportIsNotDefined(JSContext* cx, HandleId id);
+
+/*
+ * Report an attempt to access the property of a null or undefined value (v).
+ */
+extern void ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx,
+ HandleValue v, int vIndex);
+extern void ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx,
+ HandleValue v, int vIndex,
+ HandleId key);
+
+/*
+ * Report error using js::DecompileValueGenerator(cx, spindex, v, fallback) as
+ * the first argument for the error message.
+ */
+extern bool ReportValueError(JSContext* cx, const unsigned errorNumber,
+ int spindex, HandleValue v, HandleString fallback,
+ const char* arg1 = nullptr,
+ const char* arg2 = nullptr);
+
+JSObject* CreateErrorNotesArray(JSContext* cx, JSErrorReport* report);
+
+/************************************************************************/
+
+/*
+ * Encapsulates an external array of values and adds a trace method, for use in
+ * Rooted.
+ */
+class MOZ_STACK_CLASS ExternalValueArray {
+ public:
+ ExternalValueArray(size_t len, Value* vec) : array_(vec), length_(len) {}
+
+ Value* begin() { return array_; }
+ size_t length() { return length_; }
+
+ void trace(JSTracer* trc);
+
+ private:
+ Value* array_;
+ size_t length_;
+};
+
+/* RootedExternalValueArray roots an external array of Values. */
+class MOZ_RAII RootedExternalValueArray
+ : public JS::Rooted<ExternalValueArray> {
+ public:
+ RootedExternalValueArray(JSContext* cx, size_t len, Value* vec)
+ : JS::Rooted<ExternalValueArray>(cx, ExternalValueArray(len, vec)) {}
+
+ private:
+};
+
+class AutoAssertNoPendingException {
+#ifdef DEBUG
+ JSContext* cx_;
+
+ public:
+ explicit AutoAssertNoPendingException(JSContext* cxArg) : cx_(cxArg) {
+ MOZ_ASSERT(!JS_IsExceptionPending(cx_));
+ }
+
+ ~AutoAssertNoPendingException() { MOZ_ASSERT(!JS_IsExceptionPending(cx_)); }
+#else
+ public:
+ explicit AutoAssertNoPendingException(JSContext* cxArg) {}
+#endif
+};
+
+class MOZ_RAII AutoNoteDebuggerEvaluationWithOnNativeCallHook {
+ JSContext* cx;
+ Debugger* oldValue;
+
+ public:
+ AutoNoteDebuggerEvaluationWithOnNativeCallHook(JSContext* cx, Debugger* dbg)
+ : cx(cx), oldValue(cx->insideDebuggerEvaluationWithOnNativeCallHook) {
+ cx->insideDebuggerEvaluationWithOnNativeCallHook = dbg;
+ }
+
+ ~AutoNoteDebuggerEvaluationWithOnNativeCallHook() {
+ cx->insideDebuggerEvaluationWithOnNativeCallHook = oldValue;
+ }
+};
+
+enum UnsafeABIStrictness {
+ NoExceptions,
+ AllowPendingExceptions,
+ AllowThrownExceptions
+};
+
+// Should be used in functions called directly from JIT code (with
+// masm.callWithABI) to assert invariants in debug builds.
+// In debug mode, masm.callWithABI inserts code to verify that the
+// callee function uses AutoUnsafeCallWithABI.
+// While this object is live:
+// 1. cx->hasAutoUnsafeCallWithABI must be true.
+// 2. We can't GC.
+// 3. Exceptions should not be pending/thrown.
+//
+// Note that #3 is a precaution, not a requirement. By default, we
+// assert that the function is not called with a pending exception,
+// and that it does not throw an exception itself.
+class MOZ_RAII AutoUnsafeCallWithABI {
+#ifdef DEBUG
+ JSContext* cx_;
+ bool nested_;
+ bool checkForPendingException_;
+#endif
+ JS::AutoCheckCannotGC nogc;
+
+ public:
+#ifdef DEBUG
+ explicit AutoUnsafeCallWithABI(
+ UnsafeABIStrictness strictness = UnsafeABIStrictness::NoExceptions);
+ ~AutoUnsafeCallWithABI();
+#else
+ explicit AutoUnsafeCallWithABI(
+ UnsafeABIStrictness unused_ = UnsafeABIStrictness::NoExceptions) {}
+#endif
+};
+
+} /* namespace js */
+
+#define CHECK_THREAD(cx) \
+ MOZ_ASSERT_IF(cx, !cx->isHelperThreadContext() && \
+ js::CurrentThreadCanAccessRuntime(cx->runtime()))
+
+#endif /* vm_JSContext_h */