forked from mirrors/gecko-dev
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
1307 lines
42 KiB
C++
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 */
|