/* -*- 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/Vector.h" #include "vm/Runtime.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ #endif struct DtoaState; extern void js_ReportOutOfMemory(js::ThreadSafeContext *cx); extern void js_ReportAllocationOverflow(js::ThreadSafeContext *cx); extern void js_ReportOverRecursed(js::ThreadSafeContext *cx); namespace js { namespace jit { class IonContext; class CompileCompartment; } struct CallsiteCloneKey { /* The original function that we are cloning. */ JSFunction *original; /* The script of the call. */ JSScript *script; /* The offset of the call. */ uint32_t offset; CallsiteCloneKey(JSFunction *f, JSScript *s, uint32_t o) : original(f), script(s), offset(o) {} typedef CallsiteCloneKey Lookup; static inline uint32_t hash(CallsiteCloneKey key) { return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ size_t(key.original)); } static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) { return a.script == b.script && a.offset == b.offset && a.original == b.original; } }; typedef HashMap, CallsiteCloneKey, SystemAllocPolicy> CallsiteCloneTable; JSFunction * ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun, JSScript *script, jsbytecode *pc); JSFunction *CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc); typedef HashSet ObjectSet; typedef HashSet ShapeSet; /* Detects cycles when traversing an object graph. */ class AutoCycleDetector { JSContext *cx; RootedObject obj; bool cyclic; uint32_t hashsetGenerationAtInit; ObjectSet::AddPtr hashsetAddPointer; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER public: 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; } }; /* Updates references in the cycle detection set if the GC moves them. */ extern void TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set); struct AutoResolving; class DtoaCache; class ForkJoinContext; class RegExpCompartment; class RegExpStatics; 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. * * - ThreadSafeContext is used by threads operating in one compartment which * may run in parallel with other threads operating on the same or other * compartments. * * - 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 * or ThreadSafeContext, and will only run in parallel with threads using such * contexts. * * An ExclusiveContext coerces to a ThreadSafeContext, and a JSContext coerces * to an ExclusiveContext or ThreadSafeContext. * * Contexts which are a ThreadSafeContext but not an ExclusiveContext are used * to represent a ForkJoinContext, the per-thread parallel context used in PJS. */ struct ThreadSafeContext : ContextFriendFields, public MallocProvider { friend struct StackBaseShape; friend UnownedBaseShape *BaseShape::lookupUnowned(ThreadSafeContext *cx, const StackBaseShape &base); friend Shape *JSObject::lookupChildProperty(ThreadSafeContext *cx, JS::HandleObject obj, js::HandleShape parent, js::StackShape &child); public: enum ContextKind { Context_JS, Context_Exclusive, Context_ForkJoin }; private: ContextKind contextKind_; public: PerThreadData *perThreadData; ThreadSafeContext(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. JS_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 { JS_ASSERT(isJSContext()); return isJSContext(); } bool isExclusiveContext() const { return contextKind_ == Context_JS || contextKind_ == Context_Exclusive; } ExclusiveContext *maybeExclusiveContext() const { if (isExclusiveContext()) return (ExclusiveContext *) this; return nullptr; } ExclusiveContext *asExclusiveContext() const { JS_ASSERT(isExclusiveContext()); return maybeExclusiveContext(); } bool isForkJoinContext() const; ForkJoinContext *asForkJoinContext(); // The generational GC nursery may only be used on the main thread. #ifdef JSGC_GENERATIONAL inline bool hasNursery() const { return isJSContext(); } inline js::Nursery &nursery() { JS_ASSERT(hasNursery()); return runtime_->gcNursery; } #endif /* * Allocator used when allocating GCThings on this context. If we are a * JSContext, this is the Zone allocator of the JSContext's zone. * Otherwise, this is a per-thread allocator. * * This does not live in PerThreadData because the notion of an allocator * is only per-thread when off the main thread. The runtime (and the main * thread) can have more than one zone, each with its own allocator, and * it's up to the context to specify what compartment and zone we are * operating in. */ protected: Allocator *allocator_; public: static size_t offsetOfAllocator() { return offsetof(ThreadSafeContext, allocator_); } inline Allocator *const allocator(); // Allocations can only trigger GC when running on the main thread. inline AllowGC allowGC() const { return isJSContext() ? CanGC : NoGC; } template bool isInsideCurrentZone(T thing) const { return thing->zoneFromAnyThread() == zone_; } template inline bool isInsideCurrentCompartment(T thing) const { return thing->compartment() == compartment_; } template inline bool isThreadLocal(T thing) const; void *onOutOfMemory(void *p, size_t nbytes) { return runtime_->onOutOfMemory(p, nbytes, maybeJSContext()); } /* 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; } AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; } const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; } PropertyName *emptyString() { return runtime_->emptyString; } FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } bool useHelperThreads() { return runtime_->useHelperThreads(); } void *runtimeAddressForJit() { return runtime_; } void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } void *stackLimitAddressForJitCode(StackKind kind); size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; } bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); } bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } // Thread local data that may be accessed freely. DtoaState *dtoaState() { return perThreadData->dtoaState; } }; struct WorkerThread; class ExclusiveContext : public ThreadSafeContext { friend class gc::ArenaLists; friend class AutoCompartment; friend class AutoLockForExclusiveAccess; friend struct StackBaseShape; friend void JSScript::initCompartment(ExclusiveContext *cx); friend class jit::IonContext; // The worker on which this context is running, if this is not a JSContext. WorkerThread *workerThread_; public: ExclusiveContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind) : ThreadSafeContext(rt, pt, kind), workerThread_(nullptr), enterCompartmentDepth_(0) {} /* * "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 leaveCompartment(JSCompartment *oldCompartment); void setWorkerThread(WorkerThread *workerThread); WorkerThread *workerThread() const { return workerThread_; } // Threads with an ExclusiveContext may freely access any data in their // compartment and zone. JSCompartment *compartment() const { JS_ASSERT_IF(runtime_->isAtomsCompartment(compartment_), runtime_->currentThreadHasExclusiveAccess()); return compartment_; } JS::Zone *zone() const { JS_ASSERT_IF(!compartment(), !zone_); JS_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); return zone_; } // Zone local methods that can be used freely from an ExclusiveContext. types::TypeObject *getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun = nullptr); types::TypeObject *getSingletonType(const Class *clasp, TaggedProto proto); 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 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(); } ScriptDataTable &scriptDataTable() { return runtime_->scriptDataTable(); } // Methods specific to any WorkerThread for the context. frontend::CompileError &addPendingCompileError(); void addPendingOverRecursed(); }; } /* namespace js */ struct JSContext : public js::ExclusiveContext, public mozilla::LinkedListElement { explicit JSContext(JSRuntime *rt); ~JSContext(); JSRuntime *runtime() const { return runtime_; } js::PerThreadData &mainThread() const { return runtime()->mainThread; } friend class js::ExclusiveContext; friend class JS::AutoSaveExceptionState; private: /* Exception state -- the exception member is a GC root by definition. */ bool throwing; /* is there a pending exception? */ js::Value unwrappedException_; /* most-recently-thrown exception */ /* Per-context options. */ JS::ContextOptions options_; 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 SaveStack; SaveStack savedFrameChains_; public: bool saveFrameChain(); void restoreFrameChain(); /* * When no compartments have been explicitly entered, the context's * compartment will be set to the compartment of the "default compartment * object". */ private: JSObject *defaultCompartmentObject_; public: inline void setDefaultCompartmentObject(JSObject *obj); inline void setDefaultCompartmentObjectIfUnset(JSObject *obj); JSObject *maybeDefaultCompartmentObject() const { JS_ASSERT(!options().noDefaultCompartmentObject()); return defaultCompartmentObject_; } /* State for object and array toSource conversion. */ js::ObjectSet cycleDetectorSet; /* Per-context optional error reporter. */ JSErrorReporter errorReporter; /* 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; } #ifdef JS_THREADSAFE unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ #endif /* Stored here to avoid passing it around as a parameter. */ unsigned resolveFlags; /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ js::Value iterValue; bool jitIsBroken; void updateJITEnabled(); /* Whether this context has JS frames on the stack. */ bool currentlyRunning() const; bool currentlyRunningInInterpreter() const { return mainThread().activation()->isInterpreter(); } bool currentlyRunningInJit() const { return mainThread().activation()->isJit(); } js::InterpreterFrame *interpreterFrame() const { return mainThread().activation()->asInterpreter()->current(); } js::InterpreterRegs &interpreterRegs() const { return mainThread().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; #ifdef MOZ_TRACE_JSCALLS /* Function entry/exit debugging callback. */ JSFunctionCallback functionCallback; void doFunctionCallback(const JSFunction *fun, const JSScript *scr, int entering) const { if (functionCallback) functionCallback(fun, scr, this, entering); } #endif private: /* Innermost-executing generator or null if no generator are executing. */ JSGenerator *innermostGenerator_; public: JSGenerator *innermostGenerator() const { return innermostGenerator_; } void enterGenerator(JSGenerator *gen); void leaveGenerator(JSGenerator *gen); bool isExceptionPending() { return throwing; } MOZ_WARN_UNUSED_RESULT bool getPendingException(JS::MutableHandleValue rval); bool isThrowingOutOfMemory(); void setPendingException(js::Value v); void clearPendingException() { throwing = false; unwrappedException_.setUndefined(); } #ifdef DEBUG /* * Controls whether a quadratic-complexity assertion is performed during * stack iteration; defaults to true. */ bool stackIterAssertionEnabled; #endif /* * See JS_SetTrustedPrincipals in jsapi.h. * Note: !cx->compartment is treated as trusted. */ 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 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; JS_ASSERT(obj); cx->resolvingList = this; } ~AutoResolving() { JS_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 }; } /* namespace js */ class JSAutoResolveFlags { public: JSAutoResolveFlags(JSContext *cx, unsigned flags MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mContext(cx), mSaved(cx->resolveFlags) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; cx->resolveFlags = flags; } ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } private: JSContext *mContext; unsigned mSaved; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; namespace js { /* * 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() { JS_ASSERT(!done()); iter = iter->getNext(); } JSContext *get() const { JS_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); } /* namespace js */ #ifdef va_start extern bool js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap); extern bool js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback, void *userRef, const unsigned errorNumber, js::ErrorArgumentsType argumentsType, va_list ap); extern bool js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback, void *userRef, const unsigned errorNumber, const jschar **args); #endif extern bool js_ExpandErrorArguments(js::ExclusiveContext *cx, JSErrorCallback callback, void *userRef, const unsigned errorNumber, char **message, JSErrorReport *reportp, js::ErrorArgumentsType argumentsType, va_list ap); namespace js { /* |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); } /* namespace js */ extern void js_ReportIsNotDefined(JSContext *cx, const char *name); /* * Report an attempt to access the property of a null or undefined value (v). */ extern bool js_ReportIsNullOrUndefined(JSContext *cx, int spindex, js::HandleValue v, js::HandleString fallback); extern void js_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 js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber, int spindex, js::HandleValue v, js::HandleString fallback, const char *arg1, const char *arg2); #define js_ReportValueError(cx,errorNumber,spindex,v,fallback) \ ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ spindex, v, fallback, nullptr, nullptr)) #define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \ ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ spindex, v, fallback, arg1, nullptr)) #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ spindex, v, fallback, arg1, arg2)) extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; char * js_strdup(js::ExclusiveContext *cx, const char *s); #ifdef JS_THREADSAFE # define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime()->requestDepth >= 1) #else # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) #endif namespace js { /* * Invoke the interrupt callback and return false if the current execution * is to be terminated. */ bool InvokeInterruptCallback(JSContext *cx); bool HandleExecutionInterrupt(JSContext *cx); /* * Process any pending interrupt requests. Long-running inner loops in C++ must * call this periodically to make sure they are interruptible --- that is, to * make sure they do not prevent the slow script dialog from appearing. * * This can run a full GC or call the interrupt callback, which could do * anything. In the browser, it displays the slow script dialog. * * If this returns true, the caller can continue; if false, the caller must * break out of its loop. This happens if, for example, the user clicks "Stop * script" on the slow script dialog; treat it as an uncatchable error. */ inline bool CheckForInterrupt(JSContext *cx) { JS_ASSERT_REQUEST_DEPTH(cx); return !cx->runtime()->interrupt || InvokeInterruptCallback(cx); } /************************************************************************/ class AutoStringVector : public AutoVectorRooter { public: explicit AutoStringVector(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, STRINGVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoPropertyNameVector : public AutoVectorRooter { public: explicit AutoPropertyNameVector(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, STRINGVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoShapeVector : public AutoVectorRooter { public: explicit AutoShapeVector(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, SHAPEVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoObjectObjectHashMap : public AutoHashMapRooter { public: explicit AutoObjectObjectHashMap(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoHashMapRooter(cx, OBJOBJHASHMAP) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoObjectUnsigned32HashMap : public AutoHashMapRooter { public: explicit AutoObjectUnsigned32HashMap(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoHashMapRooter(cx, OBJU32HASHMAP) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoObjectHashSet : public AutoHashSetRooter { public: explicit AutoObjectHashSet(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoHashSetRooter(cx, OBJHASHSET) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /* AutoArrayRooter roots an external array of Values. */ class AutoArrayRooter : private AutoGCRooter { public: AutoArrayRooter(JSContext *cx, size_t len, Value *vec MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, len), array(vec) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; JS_ASSERT(tag_ >= 0); } void changeLength(size_t newLength) { tag_ = ptrdiff_t(newLength); JS_ASSERT(tag_ >= 0); } void changeArray(Value *newArray, size_t newLength) { changeLength(newLength); array = newArray; } Value *start() { return array; } size_t length() { JS_ASSERT(tag_ >= 0); return size_t(tag_); } MutableHandleValue handleAt(size_t i) { JS_ASSERT(i < size_t(tag_)); return MutableHandleValue::fromMarkedLocation(&array[i]); } HandleValue handleAt(size_t i) const { JS_ASSERT(i < size_t(tag_)); return HandleValue::fromMarkedLocation(&array[i]); } MutableHandleValue operator[](size_t i) { JS_ASSERT(i < size_t(tag_)); return MutableHandleValue::fromMarkedLocation(&array[i]); } HandleValue operator[](size_t i) const { JS_ASSERT(i < size_t(tag_)); return HandleValue::fromMarkedLocation(&array[i]); } friend void AutoGCRooter::trace(JSTracer *trc); private: Value *array; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoAssertNoException { #ifdef DEBUG JSContext *cx; bool hadException; #endif public: AutoAssertNoException(JSContext *cx) #ifdef DEBUG : cx(cx), hadException(cx->isExceptionPending()) #endif { } ~AutoAssertNoException() { JS_ASSERT_IF(!hadException, !cx->isExceptionPending()); } }; /* * FIXME bug 647103 - replace these *AllocPolicy names. */ class ContextAllocPolicy { ThreadSafeContext *const cx_; public: ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {} ThreadSafeContext *context() const { return cx_; } void *malloc_(size_t bytes) { return cx_->malloc_(bytes); } void *calloc_(size_t bytes) { return cx_->calloc_(bytes); } void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); } void free_(void *p) { js_free(p); } void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); } }; /* Exposed intrinsics so that Ion may inline them. */ bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsTypedObject(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_TypeDescrIsSimpleType(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_TypeDescrIsArrayType(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_TypeDescrIsUnsizedArrayType(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_TypeDescrIsSizedArrayType(JSContext *cx, unsigned argc, Value *vp); class AutoLockForExclusiveAccess { #ifdef JS_THREADSAFE 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 { JS_ASSERT(!runtime->mainThreadHasExclusiveAccess); runtime->mainThreadHasExclusiveAccess = true; } } public: AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; init(cx->runtime_); } AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; init(rt); } ~AutoLockForExclusiveAccess() { if (runtime->numExclusiveThreads) { JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread()); runtime->exclusiveAccessOwner = nullptr; PR_Unlock(runtime->exclusiveAccessLock); } else { JS_ASSERT(runtime->mainThreadHasExclusiveAccess); runtime->mainThreadHasExclusiveAccess = false; } } #else // JS_THREADSAFE public: AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoLockForExclusiveAccess() { // An empty destructor is needed to avoid warnings from clang about // unused local variables of this type. } #endif // JS_THREADSAFE MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; void CrashAtUnhandlableOOM(const char *reason); } /* namespace js */ #ifdef _MSC_VER #pragma warning(pop) #endif #endif /* jscntxt_h */