| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * 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 jscntxt_h |
| #define jscntxt_h |
| |
| #include "mozilla/MemoryReporting.h" |
| |
| #include "js/TraceableVector.h" |
| #include "js/Vector.h" |
| #include "vm/Runtime.h" |
| |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ |
| #endif |
| |
| struct DtoaState; |
| |
| namespace js { |
| |
| namespace jit { |
| class JitContext; |
| class DebugModeOSRVolatileJitFrameIterator; |
| } // namespace jit |
| |
| typedef HashSet<Shape*> ShapeSet; |
| |
| /* Detects cycles when traversing an object graph. */ |
| class MOZ_RAII AutoCycleDetector |
| { |
| public: |
| using Set = HashSet<JSObject*, MovableCellHasher<JSObject*>>; |
| |
| 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: |
| Generation hashsetGenerationAtInit; |
| JSContext* cx; |
| RootedObject obj; |
| Set::AddPtr hashsetAddPointer; |
| bool cyclic; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| /* Updates references in the cycle detection set if the GC moves them. */ |
| extern void |
| TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set); |
| |
| struct AutoResolving; |
| |
| namespace frontend { struct CompileError; } |
| |
| /* |
| * Execution Context Overview: |
| * |
| * Several different structures may be used to provide a context for operations |
| * on the VM. Each context is thread local, but varies in what data it can |
| * access and what other threads may be running. |
| * |
| * - ExclusiveContext is used by threads operating in one compartment/zone, |
| * where other threads may operate in other compartments, but *not* the same |
| * compartment or zone which the ExclusiveContext is in. A thread with an |
| * ExclusiveContext may enter the atoms compartment and atomize strings, in |
| * which case a lock is used. |
| * |
| * - JSContext is used only by the runtime's main thread. The context may |
| * operate in any compartment or zone which is not used by an ExclusiveContext, |
| * and will only run in parallel with threads using such contexts. |
| * |
| * A JSContext coerces to an ExclusiveContext. |
| */ |
| |
| struct HelperThread; |
| |
| class ExclusiveContext : public ContextFriendFields, |
| public MallocProvider<ExclusiveContext> |
| { |
| friend class gc::ArenaLists; |
| friend class AutoCompartment; |
| friend class AutoLockForExclusiveAccess; |
| friend struct StackBaseShape; |
| friend void JSScript::initCompartment(ExclusiveContext* cx); |
| friend class jit::JitContext; |
| friend class Activation; |
| |
| // The thread on which this context is running, if this is not a JSContext. |
| HelperThread* helperThread_; |
| |
| public: |
| enum ContextKind { |
| Context_JS, |
| Context_Exclusive |
| }; |
| |
| private: |
| ContextKind contextKind_; |
| |
| public: |
| PerThreadData* perThreadData; |
| |
| ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind kind); |
| |
| bool isJSContext() const { |
| return contextKind_ == Context_JS; |
| } |
| |
| JSContext* maybeJSContext() const { |
| if (isJSContext()) |
| return (JSContext*) this; |
| return nullptr; |
| } |
| |
| JSContext* asJSContext() const { |
| // Note: there is no way to perform an unchecked coercion from a |
| // ThreadSafeContext to a JSContext. This ensures that trying to use |
| // the context as a JSContext off the main thread will nullptr crash |
| // rather than race. |
| MOZ_ASSERT(isJSContext()); |
| return maybeJSContext(); |
| } |
| |
| // In some cases we could potentially want to do operations that require a |
| // JSContext while running off the main thread. While this should never |
| // actually happen, the wide enough API for working off the main thread |
| // makes such operations impossible to rule out. Rather than blindly using |
| // asJSContext() and crashing afterwards, this method may be used to watch |
| // for such cases and produce either a soft failure in release builds or |
| // an assertion failure in debug builds. |
| bool shouldBeJSContext() const { |
| MOZ_ASSERT(isJSContext()); |
| return isJSContext(); |
| } |
| |
| protected: |
| js::gc::ArenaLists* arenas_; |
| |
| public: |
| inline js::gc::ArenaLists* arenas() const { return arenas_; } |
| |
| 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 (!isJSContext()) |
| return nullptr; |
| return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, asJSContext()); |
| } |
| |
| /* Clear the pending exception (if any) due to OOM. */ |
| void recoverFromOutOfMemory(); |
| |
| inline void updateMallocCounter(size_t nbytes) { |
| // Note: this is racy. |
| runtime_->updateMallocCounter(zone_, nbytes); |
| } |
| |
| void reportAllocationOverflow() { |
| js::ReportAllocationOverflow(this); |
| } |
| |
| // Accessors for immutable runtime data. |
| JSAtomState& names() { return *runtime_->commonNames; } |
| StaticStrings& staticStrings() { return *runtime_->staticStrings; } |
| bool isPermanentAtomsInitialized() { return !!runtime_->permanentAtoms; } |
| FrozenAtomSet& permanentAtoms() { return *runtime_->permanentAtoms; } |
| WellKnownSymbols& wellKnownSymbols() { return *runtime_->wellKnownSymbols; } |
| const JS::AsmJSCacheOps& asmJSCacheOps() { return runtime_->asmJSCacheOps; } |
| PropertyName* emptyString() { return runtime_->emptyString; } |
| FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); } |
| void* runtimeAddressForJit() { return runtime_; } |
| void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); } |
| void* stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } |
| void* stackLimitAddressForJitCode(StackKind kind); |
| uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; } |
| size_t gcSystemPageSize() { return gc::SystemPageSize(); } |
| bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); } |
| bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } |
| bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; } |
| bool lcovEnabled() const { return runtime_->lcovOutput.isEnabled(); } |
| |
| // Thread local data that may be accessed freely. |
| DtoaState* dtoaState() { |
| return perThreadData->dtoaState; |
| } |
| |
| /* |
| * "Entering" a compartment changes cx->compartment (which changes |
| * cx->global). Note that this does not push any InterpreterFrame which means |
| * that it is possible for cx->fp()->compartment() != cx->compartment. |
| * 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 fp. |
| * |
| * Compartments should be entered/left in a LIFO fasion. The depth of this |
| * enter/leave stack is maintained by enterCompartmentDepth_ and queried by |
| * hasEnteredCompartment. |
| * |
| * To enter a compartment, code should prefer using AutoCompartment over |
| * manually calling cx->enterCompartment/leaveCompartment. |
| */ |
| protected: |
| unsigned enterCompartmentDepth_; |
| inline void setCompartment(JSCompartment* comp); |
| public: |
| bool hasEnteredCompartment() const { |
| return enterCompartmentDepth_ > 0; |
| } |
| #ifdef DEBUG |
| unsigned getEnterCompartmentDepth() const { |
| return enterCompartmentDepth_; |
| } |
| #endif |
| |
| inline void enterCompartment(JSCompartment* c); |
| inline void enterNullCompartment(); |
| inline void leaveCompartment(JSCompartment* oldCompartment); |
| |
| void setHelperThread(HelperThread* helperThread); |
| HelperThread* helperThread() const { return helperThread_; } |
| |
| // Threads with an ExclusiveContext may freely access any data in their |
| // compartment and zone. |
| JSCompartment* compartment() const { |
| MOZ_ASSERT_IF(runtime_->isAtomsCompartment(compartment_), |
| runtime_->currentThreadHasExclusiveAccess()); |
| return compartment_; |
| } |
| JS::Zone* zone() const { |
| MOZ_ASSERT_IF(!compartment(), !zone_); |
| MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); |
| return zone_; |
| } |
| |
| // Zone local methods that can be used freely from an ExclusiveContext. |
| inline js::LifoAlloc& typeLifoAlloc(); |
| |
| // Current global. This is only safe to use within the scope of the |
| // AutoCompartment from which it's called. |
| inline js::Handle<js::GlobalObject*> global() const; |
| |
| // Methods to access runtime data that must be protected by locks. |
| frontend::ParseMapPool& parseMapPool() { |
| return runtime_->parseMapPool(); |
| } |
| AtomSet& atoms() { |
| return runtime_->atoms(); |
| } |
| JSCompartment* atomsCompartment() { |
| return runtime_->atomsCompartment(); |
| } |
| SymbolRegistry& symbolRegistry() { |
| return runtime_->symbolRegistry(); |
| } |
| ScriptDataTable& scriptDataTable() { |
| return runtime_->scriptDataTable(); |
| } |
| |
| // Methods specific to any HelperThread for the context. |
| frontend::CompileError& addPendingCompileError(); |
| void addPendingOverRecursed(); |
| }; |
| |
| } /* namespace js */ |
| |
| struct JSContext : public js::ExclusiveContext, |
| public mozilla::LinkedListElement<JSContext> |
| { |
| explicit JSContext(JSRuntime* rt); |
| ~JSContext(); |
| |
| JSRuntime* runtime() const { return runtime_; } |
| js::PerThreadData& mainThread() const { return runtime()->mainThread; } |
| |
| static size_t offsetOfRuntime() { |
| return offsetof(JSContext, runtime_); |
| } |
| static size_t offsetOfCompartment() { |
| return offsetof(JSContext, compartment_); |
| } |
| |
| friend class js::ExclusiveContext; |
| friend class JS::AutoSaveExceptionState; |
| friend class js::jit::DebugModeOSRVolatileJitFrameIterator; |
| friend void js::ReportOverRecursed(JSContext*); |
| |
| private: |
| /* Exception state -- the exception member is a GC root by definition. */ |
| bool throwing; /* is there a pending exception? */ |
| JS::PersistentRooted<JS::Value> unwrappedException_; /* most-recently-thrown exception */ |
| |
| /* Per-context options. */ |
| JS::ContextOptions options_; |
| |
| // True if the exception currently being thrown is by result of |
| // ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind. |
| bool overRecursed_; |
| |
| // True if propagating a forced return from an interrupt handler during |
| // debug mode. |
| bool propagatingForcedReturn_; |
| |
| // A stack of live iterators that need to be updated in case of debug mode |
| // OSR. |
| js::jit::DebugModeOSRVolatileJitFrameIterator* liveVolatileJitFrameIterators_; |
| |
| public: |
| int32_t reportGranularity; /* see vm/Probes.h */ |
| |
| js::AutoResolving* resolvingList; |
| |
| /* True if generating an error, to prevent runaway recursion. */ |
| bool generatingError; |
| |
| /* See JS_SaveFrameChain/JS_RestoreFrameChain. */ |
| private: |
| struct SavedFrameChain { |
| SavedFrameChain(JSCompartment* comp, unsigned count) |
| : compartment(comp), enterCompartmentCount(count) {} |
| JSCompartment* compartment; |
| unsigned enterCompartmentCount; |
| }; |
| typedef js::Vector<SavedFrameChain, 1, js::SystemAllocPolicy> SaveStack; |
| SaveStack savedFrameChains_; |
| public: |
| bool saveFrameChain(); |
| void restoreFrameChain(); |
| |
| public: |
| /* State for object and array toSource conversion. */ |
| js::AutoCycleDetector::Set cycleDetectorSet; |
| |
| /* Client opaque pointers. */ |
| void* data; |
| void* data2; |
| |
| public: |
| |
| /* |
| * Return: |
| * - The newest scripted frame's version, if there is such a frame. |
| * - The version from the compartment. |
| * - The default version. |
| * |
| * Note: if this ever shows up in a profile, just add caching! |
| */ |
| JSVersion findVersion() const; |
| |
| const JS::ContextOptions& options() const { |
| return options_; |
| } |
| |
| JS::ContextOptions& options() { |
| return options_; |
| } |
| |
| js::LifoAlloc& tempLifoAlloc() { return runtime()->tempLifoAlloc; } |
| |
| unsigned outstandingRequests;/* number of JS_BeginRequest calls |
| without the corresponding |
| JS_EndRequest. */ |
| |
| bool jitIsBroken; |
| |
| void updateJITEnabled(); |
| |
| /* Whether this context has JS frames on the stack. */ |
| bool currentlyRunning() const; |
| |
| bool currentlyRunningInInterpreter() const { |
| return runtime_->activation()->isInterpreter(); |
| } |
| bool currentlyRunningInJit() const { |
| return runtime_->activation()->isJit(); |
| } |
| js::InterpreterFrame* interpreterFrame() const { |
| return runtime_->activation()->asInterpreter()->current(); |
| } |
| js::InterpreterRegs& interpreterRegs() const { |
| return runtime_->activation()->asInterpreter()->regs(); |
| } |
| |
| /* |
| * Get the topmost script and optional pc on the stack. By default, this |
| * function only returns a JSScript in the current compartment, returning |
| * nullptr if the current script is in a different compartment. This |
| * behavior can be overridden by passing ALLOW_CROSS_COMPARTMENT. |
| */ |
| enum MaybeAllowCrossCompartment { |
| DONT_ALLOW_CROSS_COMPARTMENT = false, |
| ALLOW_CROSS_COMPARTMENT = true |
| }; |
| inline JSScript* currentScript(jsbytecode** pc = nullptr, |
| MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const; |
| |
| // The generational GC nursery may only be used on the main thread. |
| inline js::Nursery& nursery() { |
| return runtime_->gc.nursery; |
| } |
| |
| void minorGC(JS::gcreason::Reason reason) { |
| runtime_->gc.minorGC(this, reason); |
| } |
| |
| public: |
| bool isExceptionPending() { |
| return throwing; |
| } |
| |
| MOZ_WARN_UNUSED_RESULT |
| bool getPendingException(JS::MutableHandleValue rval); |
| |
| bool isThrowingOutOfMemory(); |
| bool isClosingGenerator(); |
| |
| void setPendingException(js::Value 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() const; |
| |
| JS_FRIEND_API(size_t) sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
| |
| void mark(JSTracer* trc); |
| |
| 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); |
| }; /* struct JSContext */ |
| |
| 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 |
| }; |
| |
| /* |
| * Enumerate all contexts in a runtime. |
| */ |
| class ContextIter |
| { |
| JSContext* iter; |
| |
| public: |
| explicit ContextIter(JSRuntime* rt) { |
| iter = rt->contextList.getFirst(); |
| } |
| |
| bool done() const { |
| return !iter; |
| } |
| |
| void next() { |
| MOZ_ASSERT(!done()); |
| iter = iter->getNext(); |
| } |
| |
| JSContext* get() const { |
| MOZ_ASSERT(!done()); |
| return iter; |
| } |
| |
| operator JSContext*() const { |
| return get(); |
| } |
| |
| JSContext* operator ->() const { |
| return get(); |
| } |
| }; |
| |
| /* |
| * Create and destroy functions for JSContext, which is manually allocated |
| * and exclusively owned. |
| */ |
| extern JSContext* |
| NewContext(JSRuntime* rt, size_t stackChunkSize); |
| |
| enum DestroyContextMode { |
| DCM_NO_GC, |
| DCM_FORCE_GC, |
| DCM_NEW_FAILED |
| }; |
| |
| extern void |
| DestroyContext(JSContext* cx, DestroyContextMode mode); |
| |
| enum ErrorArgumentsType { |
| ArgumentsAreUnicode, |
| ArgumentsAreASCII |
| }; |
| |
| /* |
| * Loads and returns a self-hosted function by name. For performance, define |
| * the property name in vm/CommonPropertyNames.h. |
| * |
| * Defined in SelfHosting.cpp. |
| */ |
| JSFunction* |
| SelfHostedFunction(JSContext* cx, HandlePropertyName propName); |
| |
| #ifdef va_start |
| extern bool |
| ReportErrorVA(JSContext* cx, unsigned flags, const char* format, va_list ap); |
| |
| 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(ExclusiveContext* cx, JSErrorCallback callback, |
| void* userRef, const unsigned errorNumber, |
| char** message, JSErrorReport* reportp, |
| ErrorArgumentsType argumentsType, va_list ap); |
| |
| /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ |
| extern void |
| ReportUsageError(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, printing just the message if the report is nullptr. |
| */ |
| extern bool |
| PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report, |
| bool reportWarnings); |
| |
| /* |
| * Send a JSErrorReport to the errorReporter callback. |
| */ |
| void |
| CallErrorReporter(JSContext* cx, const char* message, JSErrorReport* report); |
| |
| extern bool |
| ReportIsNotDefined(JSContext* cx, HandlePropertyName name); |
| |
| extern bool |
| ReportIsNotDefined(JSContext* cx, HandleId id); |
| |
| /* |
| * Report an attempt to access the property of a null or undefined value (v). |
| */ |
| extern bool |
| ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v, HandleString fallback); |
| |
| 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); |
| |
| #define ReportValueError(cx,errorNumber,spindex,v,fallback) \ |
| ((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, nullptr, nullptr)) |
| |
| #define ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \ |
| ((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, arg1, nullptr)) |
| |
| #define ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ |
| ((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, arg1, arg2)) |
| |
| } /* namespace js */ |
| |
| extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; |
| |
| namespace js { |
| |
| MOZ_ALWAYS_INLINE bool |
| CheckForInterrupt(JSContext* cx) |
| { |
| // Add an inline fast-path since we have to check for interrupts in some hot |
| // C++ loops of library builtins. |
| JSRuntime* rt = cx->runtime(); |
| if (MOZ_UNLIKELY(rt->hasPendingInterrupt())) |
| return rt->handleInterrupt(cx); |
| return true; |
| } |
| |
| /************************************************************************/ |
| |
| typedef JS::AutoVectorRooter<PropertyName*> AutoPropertyNameVector; |
| |
| using ShapeVector = js::TraceableVector<Shape*>; |
| using StringVector = js::TraceableVector<JSString*>; |
| |
| /* 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, len), array(vec) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| MOZ_ASSERT(tag_ >= 0); |
| } |
| |
| void changeLength(size_t newLength) { |
| tag_ = ptrdiff_t(newLength); |
| MOZ_ASSERT(tag_ >= 0); |
| } |
| |
| void changeArray(Value* newArray, size_t newLength) { |
| changeLength(newLength); |
| array = newArray; |
| } |
| |
| Value* start() { |
| return array; |
| } |
| |
| size_t length() { |
| MOZ_ASSERT(tag_ >= 0); |
| return size_t(tag_); |
| } |
| |
| MutableHandleValue handleAt(size_t i) { |
| MOZ_ASSERT(i < size_t(tag_)); |
| return MutableHandleValue::fromMarkedLocation(&array[i]); |
| } |
| HandleValue handleAt(size_t i) const { |
| MOZ_ASSERT(i < size_t(tag_)); |
| return HandleValue::fromMarkedLocation(&array[i]); |
| } |
| MutableHandleValue operator[](size_t i) { |
| MOZ_ASSERT(i < size_t(tag_)); |
| return MutableHandleValue::fromMarkedLocation(&array[i]); |
| } |
| HandleValue operator[](size_t i) const { |
| MOZ_ASSERT(i < size_t(tag_)); |
| return HandleValue::fromMarkedLocation(&array[i]); |
| } |
| |
| friend void JS::AutoGCRooter::trace(JSTracer* trc); |
| |
| private: |
| Value* array; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoAssertNoException |
| { |
| #ifdef DEBUG |
| JSContext* cx; |
| bool hadException; |
| #endif |
| |
| public: |
| explicit AutoAssertNoException(JSContext* cx) |
| #ifdef DEBUG |
| : cx(cx), |
| hadException(cx->isExceptionPending()) |
| #endif |
| { |
| } |
| |
| ~AutoAssertNoException() |
| { |
| MOZ_ASSERT_IF(!hadException, !cx->isExceptionPending()); |
| } |
| }; |
| |
| /* Exposed intrinsics for the JITs. */ |
| bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp); |
| |
| class MOZ_RAII AutoLockForExclusiveAccess |
| { |
| JSRuntime* runtime; |
| |
| void init(JSRuntime* rt) { |
| runtime = rt; |
| if (runtime->numExclusiveThreads) { |
| runtime->assertCanLock(ExclusiveAccessLock); |
| PR_Lock(runtime->exclusiveAccessLock); |
| #ifdef DEBUG |
| runtime->exclusiveAccessOwner = PR_GetCurrentThread(); |
| #endif |
| } else { |
| MOZ_ASSERT(!runtime->mainThreadHasExclusiveAccess); |
| runtime->mainThreadHasExclusiveAccess = true; |
| } |
| } |
| |
| public: |
| explicit AutoLockForExclusiveAccess(ExclusiveContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| init(cx->runtime_); |
| } |
| explicit AutoLockForExclusiveAccess(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| init(rt); |
| } |
| ~AutoLockForExclusiveAccess() { |
| if (runtime->numExclusiveThreads) { |
| MOZ_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread()); |
| runtime->exclusiveAccessOwner = nullptr; |
| PR_Unlock(runtime->exclusiveAccessLock); |
| } else { |
| MOZ_ASSERT(runtime->mainThreadHasExclusiveAccess); |
| runtime->mainThreadHasExclusiveAccess = false; |
| } |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| } /* namespace js */ |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #endif |
| |
| #endif /* jscntxt_h */ |