fune/js/src/vm/JSContext.h
Jim Blandy 689eea27da Bug 1510768: Move SpiderMonkey promise interface into its own header. r=arai
Move js/src/jsapi.h declarations related to promises and job queues into their
own public header file, js/public/Promise.h. Change the compilation units that
need these declarations to #include the new header.

There should be no changes to the actual functionality here, simply moving the
code to a new file, and removing the "JS" prefix from some typedefs which are
now in the JS namespace.

Differential Revision: https://phabricator.services.mozilla.com/D13345

--HG--
extra : moz-landing-system : lando
2018-12-17 23:21:04 +00:00

1307 lines
42 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* JS execution context. */
#ifndef vm_JSContext_h
#define vm_JSContext_h
#include "mozilla/MemoryReporting.h"
#include "ds/TraceableFifo.h"
#include "js/CharacterEncoding.h"
#include "js/GCVector.h"
#include "js/Promise.h"
#include "js/Result.h"
#include "js/Utility.h"
#include "js/Vector.h"
#include "threading/ProtectedData.h"
#include "util/StructuredSpewer.h"
#include "vm/ErrorReporting.h"
#include "vm/MallocProvider.h"
#include "vm/Runtime.h"
struct DtoaState;
namespace js {
class AutoAllocInAtomsZone;
class AutoRealm;
namespace jit {
class JitContext;
class DebugModeOSRVolatileJitFrameIter;
} // namespace jit
namespace gc {
class AutoCheckCanAccessAtomsDuringGC;
class AutoSuppressNurseryCellAlloc;
} // namespace gc
typedef HashSet<Shape*> ShapeSet;
/* Detects cycles when traversing an object graph. */
class MOZ_RAII AutoCycleDetector {
public:
using Vector = GCVector<JSObject*, 8>;
AutoCycleDetector(JSContext* cx,
HandleObject objArg MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx), obj(cx, objArg), cyclic(true) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoCycleDetector();
bool init();
bool foundCycle() { return cyclic; }
private:
JSContext* cx;
RootedObject obj;
bool cyclic;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
struct AutoResolving;
struct HelperThread;
using JobQueue = TraceableFifo<JSObject*, 0, SystemAllocPolicy>;
class AutoLockScriptData;
void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
/* Thread Local Storage slot for storing the context for a thread. */
extern MOZ_THREAD_LOCAL(JSContext*) TlsContext;
enum class ContextKind {
// Context for the main thread of a JSRuntime.
MainThread,
// Context for a helper thread.
HelperThread
};
#ifdef DEBUG
bool CurrentThreadIsParseThread();
#endif
enum class InterruptReason : uint32_t {
GC = 1 << 0,
AttachIonCompilations = 1 << 1,
CallbackUrgent = 1 << 2,
CallbackCanWait = 1 << 3,
};
} /* namespace js */
/*
* A JSContext encapsulates the thread local state used when using the JS
* runtime.
*/
struct 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_;
// The thread on which this context is running if this is not the main thread.
js::ThreadData<js::HelperThread*> helperThread_;
friend class js::gc::AutoSuppressNurseryCellAlloc;
js::ThreadData<size_t> nurserySuppressions_;
js::ThreadData<JS::ContextOptions> options_;
// Free lists for allocating in the current zone.
js::ThreadData<js::gc::FreeLists*> freeLists_;
// This is reset each time we switch zone, then added to the variable in the
// zone when we switch away from it. This would be a js::ThreadData but we
// need to take its address.
uint32_t allocsThisZoneSinceMinorGC_;
// Free lists for parallel allocation in the atoms zone on helper threads.
js::ThreadData<js::gc::FreeLists*> atomsZoneFreeLists_;
public:
// This is used by helper threads to change the runtime their context is
// currently operating on.
void setRuntime(JSRuntime* rt);
bool isMainThreadContext() const {
return kind_ == js::ContextKind::MainThread;
}
js::gc::FreeLists& freeLists() {
MOZ_ASSERT(freeLists_);
return *freeLists_;
}
js::gc::FreeLists& atomsZoneFreeLists() {
MOZ_ASSERT(atomsZoneFreeLists_);
return *atomsZoneFreeLists_;
}
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(js::AllocFunction allocFunc, size_t nbytes,
void* reallocPtr = nullptr) {
if (helperThread()) {
addPendingOutOfMemory();
return nullptr;
}
return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, this);
}
/* Clear the pending exception (if any) due to OOM. */
void recoverFromOutOfMemory();
/*
* This variation of calloc will call the large-allocation-failure callback
* on OOM and retry the allocation.
*/
template <typename T>
T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
T* p = maybe_pod_calloc<T>(numElems, arena);
if (MOZ_LIKELY(!!p)) {
return p;
}
size_t bytes;
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
reportAllocationOverflow();
return nullptr;
}
p = static_cast<T*>(
runtime()->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
if (!p) {
return nullptr;
}
updateMallocCounter(bytes);
return p;
}
void updateMallocCounter(size_t nbytes);
void reportAllocationOverflow() { js::ReportAllocationOverflow(this); }
void noteTenuredAlloc() { allocsThisZoneSinceMinorGC_++; }
uint32_t* addressOfTenuredAllocCount() {
return &allocsThisZoneSinceMinorGC_;
}
uint32_t getAndResetAllocsThisZoneSinceMinorGC() {
uint32_t allocs = allocsThisZoneSinceMinorGC_;
allocsThisZoneSinceMinorGC_ = 0;
return allocs;
}
// Accessors for immutable runtime data.
JSAtomState& names() { return *runtime_->commonNames; }
js::StaticStrings& staticStrings() { return *runtime_->staticStrings; }
js::SharedImmutableStringsCache& sharedImmutableStrings() {
return runtime_->sharedImmutableStrings();
}
bool permanentAtomsPopulated() { return runtime_->permanentAtomsPopulated(); }
const js::FrozenAtomSet& permanentAtoms() {
return *runtime_->permanentAtoms();
}
js::WellKnownSymbols& wellKnownSymbols() {
return *runtime_->wellKnownSymbols;
}
const JS::AsmJSCacheOps& asmJSCacheOps() { return runtime_->asmJSCacheOps; }
js::PropertyName* emptyString() { return runtime_->emptyString; }
js::FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); }
void* stackLimitAddress(JS::StackKind kind) {
return &nativeStackLimit[kind];
}
void* stackLimitAddressForJitCode(JS::StackKind kind);
uintptr_t stackLimit(JS::StackKind kind) { return nativeStackLimit[kind]; }
uintptr_t stackLimitForJitCode(JS::StackKind kind);
size_t gcSystemPageSize() { return js::gc::SystemPageSize(); }
bool jitSupportsFloatingPoint() const {
return runtime_->jitSupportsFloatingPoint;
}
bool jitSupportsUnalignedAccesses() const {
return runtime_->jitSupportsUnalignedAccesses;
}
bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
bool lcovEnabled() const { return runtime_->lcovOutput().isEnabled(); }
/*
* "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);
enum IsAtomsZone { AtomsZone, NotAtomsZone };
inline void setZone(js::Zone* zone, IsAtomsZone isAtomsZone);
friend class js::AutoAllocInAtomsZone;
friend class js::AutoRealm;
public:
inline void enterRealmOf(JSObject* target);
inline void enterRealmOf(JSScript* target);
inline void enterRealmOf(js::ObjectGroup* target);
inline void enterNullRealm();
inline void setRealmForJitExceptionHandler(JS::Realm* realm);
inline void leaveRealm(JS::Realm* oldRealm);
void setHelperThread(js::HelperThread* helperThread);
js::HelperThread* helperThread() const { return helperThread_; }
bool isNurseryAllocSuppressed() const { return nurserySuppressions_; }
// 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_); }
// Zone local methods that can be used freely.
inline js::LifoAlloc& typeLifoAlloc();
// 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(); }
const JS::Zone* atomsZone(const js::AutoAccessAtomsZone& access) {
return runtime_->atomsZone(access);
}
js::SymbolRegistry& symbolRegistry() { return runtime_->symbolRegistry(); }
// Methods to access runtime data that must be protected by locks.
js::ScriptDataTable& scriptDataTable(js::AutoLockScriptData& lock) {
return runtime_->scriptDataTable(lock);
}
// 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);
}
// Methods specific to any HelperThread for the context.
bool addPendingCompileError(js::CompileError** err);
void addPendingOverRecursed();
void addPendingOutOfMemory();
bool isCompileErrorPending() const;
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::ReportOverRecursed(JSContext*, unsigned errorNumber);
private:
static JS::Error reportedError;
static JS::OOM reportedOOM;
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(const 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::ThreadData<js::jit::JitActivation*> jitActivation;
// Information about the heap allocated backtrack stack used by RegExp JIT
// code.
js::ThreadData<js::irregexp::RegExpStack> regexpStack;
/*
* Points to the most recent activation running on the thread.
* See Activation comment in vm/Stack.h.
*/
js::ThreadData<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
public:
js::InterpreterStack& interpreterStack() {
return runtime()->interpreterStack();
}
/* Base address of the native stack for the current thread. */
const uintptr_t nativeStackBase;
/* The native stack size limit that runtime should not exceed. */
js::ThreadData<size_t> nativeStackQuota[JS::StackKindCount];
public:
/* If non-null, report JavaScript entry points to this monitor. */
js::ThreadData<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::ThreadData<js::EnterDebuggeeNoExecute*> noExecuteDebuggerTop;
#ifdef DEBUG
js::ThreadData<uint32_t> inUnsafeCallWithABI;
js::ThreadData<bool> hasAutoUnsafeCallWithABI;
#endif
#ifdef JS_SIMULATOR
private:
js::ThreadData<js::jit::Simulator*> simulator_;
public:
js::jit::Simulator* simulator() const;
uintptr_t* addressOfSimulatorStackLimit();
#endif
#ifdef JS_TRACE_LOGGING
js::UnprotectedData<js::TraceLoggerThread*> traceLogger;
#endif
private:
/* Pointer to the current AutoFlushICache. */
js::ThreadData<js::jit::AutoFlushICache*> autoFlushICache_;
public:
js::jit::AutoFlushICache* autoFlushICache() const;
void setAutoFlushICache(js::jit::AutoFlushICache* afc);
// State used by util/DoubleToString.cpp.
js::ThreadData<DtoaState*> dtoaState;
/*
* When this flag is non-zero, any attempt to GC will be skipped. It is used
* to suppress GC when reporting an OOM (see ReportOutOfMemory) and in
* debugging facilities that cannot tolerate a GC and would rather OOM
* immediately, such as utilities exposed to GDB. Setting this flag is
* extremely dangerous and should only be used when in an OOM situation or
* in non-exposed debugging facilities.
*/
js::ThreadData<int32_t> suppressGC;
#ifdef DEBUG
// Whether this thread is actively Ion compiling.
js::ThreadData<bool> ionCompiling;
// Whether this thread is actively Ion compiling in a context where a minor
// GC could happen simultaneously. If this is true, this thread cannot use
// any pointers into the nursery.
js::ThreadData<bool> ionCompilingSafeForMinorGC;
// Whether this thread is currently performing GC. This thread could be the
// main thread or a helper thread while the main thread is running the
// collector.
js::ThreadData<bool> performingGC;
// Whether this thread is currently sweeping GC things. This thread could
// be the main thread or a helper thread while the main thread is running
// the mutator. This is used to assert that destruction of GCPtr only
// happens when we are sweeping.
js::ThreadData<bool> gcSweeping;
// Whether this thread is performing work in the background for a runtime's
// GCHelperState.
js::ThreadData<bool> gcHelperStateThread;
// Whether this thread is currently manipulating possibly-gray GC things.
js::ThreadData<size_t> isTouchingGrayThings;
js::ThreadData<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::ThreadData<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::ThreadData<bool> runningOOMTest;
#endif
// True if we should assert that
// !comp->validAccessPtr || *comp->validAccessPtr
// is true for every |comp| that we run JS code in.
js::ThreadData<unsigned> enableAccessValidation;
/*
* 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::ThreadData<int> inUnsafeRegion;
// Count of AutoDisableGenerationalGC instances on the thread's stack.
js::ThreadData<unsigned> generationalDisabled;
// Some code cannot tolerate compacting GC so it can be disabled temporarily
// with AutoDisableCompactingGC which uses this counter.
js::ThreadData<unsigned> compactingDisabledCount;
bool canCollectAtoms() const {
// TODO: We may be able to improve this by collecting if
// !isOffThreadParseRunning() (bug 1468422).
return !runtime()->hasHelperThreadZones();
}
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::ThreadData<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");
}
/* Whether sampling should be enabled or not. */
private:
mozilla::Atomic<bool, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve>
suppressProfilerSampling;
public:
bool isProfilerSamplingEnabled() const { return !suppressProfilerSampling; }
void disableProfilerSampling() { suppressProfilerSampling = true; }
void enableProfilerSampling() { suppressProfilerSampling = false; }
// Used by wasm::EnsureThreadSignalHandlers(cx) to install thread signal
// handlers once per JSContext/thread.
bool wasmTriedToInstallSignalHandlers;
bool wasmHaveSignalHandlers;
/* Temporary arena pool used while compiling and decompiling. */
static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
private:
js::ThreadData<js::LifoAlloc> tempLifoAlloc_;
public:
js::LifoAlloc& tempLifoAlloc() { return tempLifoAlloc_.ref(); }
const js::LifoAlloc& tempLifoAlloc() const { return tempLifoAlloc_.ref(); }
js::ThreadData<uint32_t> debuggerMutations;
// Cache for jit::GetPcScript().
js::ThreadData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache;
private:
/* Exception state -- the exception member is a GC root by definition. */
js::ThreadData<bool> throwing; /* is there a pending exception? */
js::ThreadData<JS::PersistentRooted<JS::Value>>
unwrappedException_; /* most-recently-thrown exception */
JS::Value& unwrappedException() {
if (!unwrappedException_.ref().initialized()) {
unwrappedException_.ref().init(this);
}
return unwrappedException_.ref().get();
}
// True if the exception currently being thrown is by result of
// ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
js::ThreadData<bool> overRecursed_;
// True if propagating a forced return from an interrupt handler during
// debug mode.
js::ThreadData<bool> propagatingForcedReturn_;
// A stack of live iterators that need to be updated in case of debug mode
// OSR.
js::ThreadData<js::jit::DebugModeOSRVolatileJitFrameIter*>
liveVolatileJitFrameIter_;
public:
js::ThreadData<int32_t> reportGranularity; /* see vm/Probes.h */
js::ThreadData<js::AutoResolving*> resolvingList;
#ifdef DEBUG
js::ThreadData<js::AutoEnterPolicy*> enteredPolicy;
#endif
/* True if generating an error, to prevent runaway recursion. */
js::ThreadData<bool> generatingError;
private:
/* State for object and array toSource conversion. */
js::ThreadData<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; }
js::ThreadData<bool> jitIsBroken;
void updateJITEnabled();
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::ThreadData<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::ThreadData<const char*> asyncCauseForNewActivations;
/*
* True if the async call was explicitly requested, e.g. via
* callFunctionWithAsyncStack.
*/
js::ThreadData<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 reason);
public:
bool isExceptionPending() const { return throwing; }
MOZ_MUST_USE
bool getPendingException(JS::MutableHandleValue rval);
bool isThrowingOutOfMemory();
bool isThrowingDebuggeeWouldRun();
bool isClosingGenerator();
void setPendingException(JS::HandleValue v);
void clearPendingException() {
throwing = false;
overRecursed_ = false;
unwrappedException().setUndefined();
}
bool isThrowingOverRecursed() const { return throwing && overRecursed_; }
bool isPropagatingForcedReturn() const { return propagatingForcedReturn_; }
void setPropagatingForcedReturn() { propagatingForcedReturn_ = true; }
void clearPropagatingForcedReturn() { propagatingForcedReturn_ = false; }
/*
* See JS_SetTrustedPrincipals in jsapi.h.
* Note: !cx->compartment is treated as trusted.
*/
inline bool runningWithTrustedPrincipals();
JS_FRIEND_API size_t
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
void trace(JSTracer* trc);
inline js::RuntimeCaches& caches();
private:
/*
* The allocation code calls the function to indicate either OOM failure
* when p is null or that a memory pressure counter has reached some
* threshold when p is not null. The function takes the pointer and not
* a boolean flag to minimize the amount of code in its inlined callers.
*/
JS_FRIEND_API void checkMallocGCPressure(void* p);
public:
using InterruptCallbackVector =
js::Vector<JSInterruptCallback, 2, js::SystemAllocPolicy>;
private:
js::ThreadData<InterruptCallbackVector> interruptCallbacks_;
public:
InterruptCallbackVector& interruptCallbacks() {
return interruptCallbacks_.ref();
}
js::ThreadData<bool> interruptCallbackDisabled;
// Bitfield storing InterruptReason values.
mozilla::Atomic<uint32_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
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
// UINTPTR_MAX). 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 UINTPTR_MAX 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);
}
public:
void* addressOfInterruptBits() { return &interruptBits_; }
void* addressOfJitStackLimit() { return &jitStackLimit; }
void* addressOfJitStackLimitNoInterrupt() {
return &jitStackLimitNoInterrupt;
}
// Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics
// object.
js::FutexThread fx;
// Buffer for OSR from baseline to Ion. To avoid holding on to this for
// too long, it's also freed in EnterBaseline (after returning from JIT code).
js::ThreadData<uint8_t*> osrTempData_;
uint8_t* allocateOsrTempData(size_t size);
void freeOsrTempData();
// In certain cases, we want to optimize certain opcodes to typed
// instructions, to avoid carrying an extra register to feed into an unbox.
// Unfortunately, that's not always possible. For example, a GetPropertyCacheT
// could return a typed double, but if it takes its out-of-line path, it could
// return an object, and trigger invalidation. The invalidation bailout will
// consider the return value to be a double, and create a garbage Value.
//
// To allow the GetPropertyCacheT optimization, we allow the ability for
// GetPropertyCache to override the return value at the top of the stack - the
// value that will be temporarily corrupt. This special override value is set
// only in callVM() targets that are about to return *and* have invalidated
// their callee.
js::ThreadData<js::Value> ionReturnOverride_;
bool hasIonReturnOverride() const {
return !ionReturnOverride_.ref().isMagic(JS_ARG_POISON);
}
js::Value takeIonReturnOverride() {
js::Value v = ionReturnOverride_;
ionReturnOverride_ = js::MagicValue(JS_ARG_POISON);
return v;
}
void setIonReturnOverride(const js::Value& v) {
MOZ_ASSERT(!hasIonReturnOverride());
MOZ_ASSERT(!v.isMagic());
ionReturnOverride_ = v;
}
mozilla::Atomic<uintptr_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
jitStackLimit;
// Like jitStackLimit, but not reset to trigger interrupts.
js::ThreadData<uintptr_t> jitStackLimitNoInterrupt;
// Promise callbacks.
js::ThreadData<JS::GetIncumbentGlobalCallback> getIncumbentGlobalCallback;
js::ThreadData<JS::EnqueuePromiseJobCallback> enqueuePromiseJobCallback;
js::ThreadData<void*> enqueuePromiseJobCallbackData;
// Queue of pending jobs as described in ES2016 section 8.4.
// Only used if internal job queue handling was activated using
// `js::UseInternalJobQueues`.
js::ThreadData<JS::PersistentRooted<js::JobQueue>*> jobQueue;
js::ThreadData<bool> drainingJobQueue;
js::ThreadData<bool> stopDrainingJobQueue;
js::ThreadData<bool> canSkipEnqueuingJobs;
js::ThreadData<JS::PromiseRejectionTrackerCallback>
promiseRejectionTrackerCallback;
js::ThreadData<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:
// Base case for the recursive function below.
inline void checkImpl(int argIndex) {}
template <class Head, class... Tail>
inline void checkImpl(int argIndex, const Head& head, const Tail&... tail);
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::ThreadData<js::StructuredSpewer> structuredSpewer_;
public:
js::StructuredSpewer& spewer() { return structuredSpewer_.ref(); }
#endif
}; /* 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<>(reportedError);
}
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 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
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;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/*
* Create and destroy functions for JSContext, which is manually allocated
* and exclusively owned.
*/
extern JSContext* NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes,
JSRuntime* parentRuntime);
extern void DestroyContext(JSContext* cx);
enum ErrorArgumentsType {
ArgumentsAreUnicode,
ArgumentsAreASCII,
ArgumentsAreLatin1,
ArgumentsAreUTF8
};
/**
* Report an exception, using printf-style APIs to generate the error
* message.
*/
#ifdef va_start
extern bool ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
ErrorArgumentsType argumentsType, va_list ap)
MOZ_FORMAT_PRINTF(3, 0);
extern bool ReportErrorNumberVA(JSContext* cx, unsigned flags,
JSErrorCallback callback, void* userRef,
const unsigned errorNumber,
ErrorArgumentsType argumentsType, va_list ap);
extern bool ReportErrorNumberUCArray(JSContext* cx, unsigned flags,
JSErrorCallback callback, void* userRef,
const unsigned errorNumber,
const char16_t** args);
#endif
extern bool ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
const char16_t** messageArgs,
ErrorArgumentsType argumentsType,
JSErrorReport* reportp, va_list ap);
extern bool ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
const char16_t** messageArgs,
ErrorArgumentsType argumentsType,
JSErrorNotes::Note* notep, va_list ap);
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
extern void ReportUsageErrorASCII(JSContext* cx, HandleObject callee,
const char* msg);
/*
* Prints a full report and returns true if the given report is non-nullptr
* and the report doesn't have the JSREPORT_WARNING flag set or reportWarnings
* is true.
* Returns false otherwise.
*/
extern bool PrintError(JSContext* cx, FILE* file,
JS::ConstUTF8CharsZ toStringResult,
JSErrorReport* report, bool reportWarnings);
extern void ReportIsNotDefined(JSContext* cx, HandlePropertyName name);
extern void ReportIsNotDefined(JSContext* cx, HandleId id);
/*
* Report an attempt to access the property of a null or undefined value (v).
*/
extern void ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v);
extern void ReportMissingArg(JSContext* cx, js::HandleValue v, unsigned arg);
/*
* Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as
* the first argument for the error message. If the error message has less
* then 3 arguments, use null for arg1 or arg2.
*/
extern bool ReportValueErrorFlags(JSContext* cx, unsigned flags,
const unsigned errorNumber, int spindex,
HandleValue v, HandleString fallback,
const char* arg1, const char* arg2);
inline void ReportValueError(JSContext* cx, const unsigned errorNumber,
int spindex, HandleValue v, HandleString fallback,
const char* arg1 = nullptr,
const char* arg2 = nullptr) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, spindex, v, fallback,
arg1, arg2);
}
JSObject* CreateErrorNotesArray(JSContext* cx, JSErrorReport* report);
} /* namespace js */
extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
namespace js {
/************************************************************************/
/* AutoArrayRooter roots an external array of Values. */
class MOZ_RAII AutoArrayRooter : private JS::AutoGCRooter {
public:
AutoArrayRooter(JSContext* cx, size_t len,
Value* vec MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::AutoGCRooter(cx, JS::AutoGCRooter::Tag::Array),
array_(vec),
length_(len) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
Value* begin() { return array_; }
size_t length() { return length_; }
friend void JS::AutoGCRooter::trace(JSTracer* trc);
private:
Value* array_;
size_t length_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
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 AutoLockScriptData {
JSRuntime* runtime;
public:
explicit AutoLockScriptData(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt) ||
CurrentThreadIsParseThread());
runtime = rt;
if (runtime->hasHelperThreadZones()) {
runtime->scriptDataLock.lock();
} else {
MOZ_ASSERT(!runtime->activeThreadHasScriptDataAccess);
#ifdef DEBUG
runtime->activeThreadHasScriptDataAccess = true;
#endif
}
}
~AutoLockScriptData() {
if (runtime->hasHelperThreadZones()) {
runtime->scriptDataLock.unlock();
} else {
MOZ_ASSERT(runtime->activeThreadHasScriptDataAccess);
#ifdef DEBUG
runtime->activeThreadHasScriptDataAccess = false;
#endif
}
}
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
// A token used to prove you can safely access the atoms zone. This zone is
// accessed by the main thread and by off-thread parsing. There are two
// situations in which it is safe:
//
// - the current thread holds all atoms table locks (off-thread parsing may be
// running and must also take one of these locks for access)
//
// - the GC is running and is collecting the atoms zone (this cannot be started
// while off-thread parsing is happening)
class MOZ_STACK_CLASS AutoAccessAtomsZone {
public:
MOZ_IMPLICIT AutoAccessAtomsZone(const AutoLockAllAtoms& lock) {}
MOZ_IMPLICIT AutoAccessAtomsZone(
const gc::AutoCheckCanAccessAtomsDuringGC& canAccess) {}
};
class MOZ_RAII AutoKeepAtoms {
JSContext* cx;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit inline AutoKeepAtoms(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
inline ~AutoKeepAtoms();
};
// Debugging RAII class which marks the current thread as performing an Ion
// compilation, for use by CurrentThreadCan{Read,Write}CompilationData
class MOZ_RAII AutoEnterIonCompilation {
public:
explicit AutoEnterIonCompilation(
bool safeForMinorGC MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
#ifdef DEBUG
JSContext* cx = TlsContext.get();
MOZ_ASSERT(!cx->ionCompiling);
MOZ_ASSERT(!cx->ionCompilingSafeForMinorGC);
cx->ionCompiling = true;
cx->ionCompilingSafeForMinorGC = safeForMinorGC;
#endif
}
~AutoEnterIonCompilation() {
#ifdef DEBUG
JSContext* cx = TlsContext.get();
MOZ_ASSERT(cx->ionCompiling);
cx->ionCompiling = false;
cx->ionCompilingSafeForMinorGC = false;
#endif
}
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
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 gc {
// In debug builds, set/unset the performing GC flag for the current thread.
struct MOZ_RAII AutoSetThreadIsPerformingGC {
#ifdef DEBUG
AutoSetThreadIsPerformingGC() : cx(TlsContext.get()) {
MOZ_ASSERT(!cx->performingGC);
cx->performingGC = true;
}
~AutoSetThreadIsPerformingGC() {
MOZ_ASSERT(cx->performingGC);
cx->performingGC = false;
}
private:
JSContext* cx;
#else
AutoSetThreadIsPerformingGC() {}
#endif
};
// In debug builds, set/reset the GC sweeping flag for the current thread.
struct MOZ_RAII AutoSetThreadIsSweeping {
#ifdef DEBUG
AutoSetThreadIsSweeping() : cx(TlsContext.get()), prevState(cx->gcSweeping) {
cx->gcSweeping = true;
}
~AutoSetThreadIsSweeping() { cx->gcSweeping = prevState; }
private:
JSContext* cx;
bool prevState;
#else
AutoSetThreadIsSweeping() {}
#endif
};
} // namespace gc
} /* namespace js */
#define CHECK_THREAD(cx) \
MOZ_ASSERT_IF(cx && !cx->helperThread(), \
CurrentThreadCanAccessRuntime(cx->runtime()))
#endif /* vm_JSContext_h */