michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* JS execution context. */ michael@0: michael@0: #ifndef jscntxt_h michael@0: #define jscntxt_h michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "js/Vector.h" michael@0: #include "vm/Runtime.h" michael@0: michael@0: #ifdef _MSC_VER michael@0: #pragma warning(push) michael@0: #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ michael@0: #endif michael@0: michael@0: struct DtoaState; michael@0: michael@0: extern void michael@0: js_ReportOutOfMemory(js::ThreadSafeContext *cx); michael@0: michael@0: extern void michael@0: js_ReportAllocationOverflow(js::ThreadSafeContext *cx); michael@0: michael@0: extern void michael@0: js_ReportOverRecursed(js::ThreadSafeContext *cx); michael@0: michael@0: namespace js { michael@0: michael@0: namespace jit { michael@0: class IonContext; michael@0: class CompileCompartment; michael@0: } michael@0: michael@0: struct CallsiteCloneKey { michael@0: /* The original function that we are cloning. */ michael@0: JSFunction *original; michael@0: michael@0: /* The script of the call. */ michael@0: JSScript *script; michael@0: michael@0: /* The offset of the call. */ michael@0: uint32_t offset; michael@0: michael@0: CallsiteCloneKey(JSFunction *f, JSScript *s, uint32_t o) : original(f), script(s), offset(o) {} michael@0: michael@0: typedef CallsiteCloneKey Lookup; michael@0: michael@0: static inline uint32_t hash(CallsiteCloneKey key) { michael@0: return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ size_t(key.original)); michael@0: } michael@0: michael@0: static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) { michael@0: return a.script == b.script && a.offset == b.offset && a.original == b.original; michael@0: } michael@0: }; michael@0: michael@0: typedef HashMap, michael@0: CallsiteCloneKey, michael@0: SystemAllocPolicy> CallsiteCloneTable; michael@0: michael@0: JSFunction * michael@0: ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun, michael@0: JSScript *script, jsbytecode *pc); michael@0: michael@0: JSFunction *CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, michael@0: HandleScript script, jsbytecode *pc); michael@0: michael@0: typedef HashSet ObjectSet; michael@0: typedef HashSet ShapeSet; michael@0: michael@0: /* Detects cycles when traversing an object graph. */ michael@0: class AutoCycleDetector michael@0: { michael@0: JSContext *cx; michael@0: RootedObject obj; michael@0: bool cyclic; michael@0: uint32_t hashsetGenerationAtInit; michael@0: ObjectSet::AddPtr hashsetAddPointer; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: michael@0: public: michael@0: AutoCycleDetector(JSContext *cx, HandleObject objArg michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : cx(cx), obj(cx, objArg), cyclic(true) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: ~AutoCycleDetector(); michael@0: michael@0: bool init(); michael@0: michael@0: bool foundCycle() { return cyclic; } michael@0: }; michael@0: michael@0: /* Updates references in the cycle detection set if the GC moves them. */ michael@0: extern void michael@0: TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set); michael@0: michael@0: struct AutoResolving; michael@0: class DtoaCache; michael@0: class ForkJoinContext; michael@0: class RegExpCompartment; michael@0: class RegExpStatics; michael@0: michael@0: namespace frontend { struct CompileError; } michael@0: michael@0: /* michael@0: * Execution Context Overview: michael@0: * michael@0: * Several different structures may be used to provide a context for operations michael@0: * on the VM. Each context is thread local, but varies in what data it can michael@0: * access and what other threads may be running. michael@0: * michael@0: * - ThreadSafeContext is used by threads operating in one compartment which michael@0: * may run in parallel with other threads operating on the same or other michael@0: * compartments. michael@0: * michael@0: * - ExclusiveContext is used by threads operating in one compartment/zone, michael@0: * where other threads may operate in other compartments, but *not* the same michael@0: * compartment or zone which the ExclusiveContext is in. A thread with an michael@0: * ExclusiveContext may enter the atoms compartment and atomize strings, in michael@0: * which case a lock is used. michael@0: * michael@0: * - JSContext is used only by the runtime's main thread. The context may michael@0: * operate in any compartment or zone which is not used by an ExclusiveContext michael@0: * or ThreadSafeContext, and will only run in parallel with threads using such michael@0: * contexts. michael@0: * michael@0: * An ExclusiveContext coerces to a ThreadSafeContext, and a JSContext coerces michael@0: * to an ExclusiveContext or ThreadSafeContext. michael@0: * michael@0: * Contexts which are a ThreadSafeContext but not an ExclusiveContext are used michael@0: * to represent a ForkJoinContext, the per-thread parallel context used in PJS. michael@0: */ michael@0: michael@0: struct ThreadSafeContext : ContextFriendFields, michael@0: public MallocProvider michael@0: { michael@0: friend struct StackBaseShape; michael@0: friend UnownedBaseShape *BaseShape::lookupUnowned(ThreadSafeContext *cx, michael@0: const StackBaseShape &base); michael@0: friend Shape *JSObject::lookupChildProperty(ThreadSafeContext *cx, michael@0: JS::HandleObject obj, js::HandleShape parent, michael@0: js::StackShape &child); michael@0: michael@0: public: michael@0: enum ContextKind { michael@0: Context_JS, michael@0: Context_Exclusive, michael@0: Context_ForkJoin michael@0: }; michael@0: michael@0: private: michael@0: ContextKind contextKind_; michael@0: michael@0: public: michael@0: PerThreadData *perThreadData; michael@0: michael@0: ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind); michael@0: michael@0: bool isJSContext() const { michael@0: return contextKind_ == Context_JS; michael@0: } michael@0: michael@0: JSContext *maybeJSContext() const { michael@0: if (isJSContext()) michael@0: return (JSContext *) this; michael@0: return nullptr; michael@0: } michael@0: michael@0: JSContext *asJSContext() const { michael@0: // Note: there is no way to perform an unchecked coercion from a michael@0: // ThreadSafeContext to a JSContext. This ensures that trying to use michael@0: // the context as a JSContext off the main thread will nullptr crash michael@0: // rather than race. michael@0: JS_ASSERT(isJSContext()); michael@0: return maybeJSContext(); michael@0: } michael@0: michael@0: // In some cases we could potentially want to do operations that require a michael@0: // JSContext while running off the main thread. While this should never michael@0: // actually happen, the wide enough API for working off the main thread michael@0: // makes such operations impossible to rule out. Rather than blindly using michael@0: // asJSContext() and crashing afterwards, this method may be used to watch michael@0: // for such cases and produce either a soft failure in release builds or michael@0: // an assertion failure in debug builds. michael@0: bool shouldBeJSContext() const { michael@0: JS_ASSERT(isJSContext()); michael@0: return isJSContext(); michael@0: } michael@0: michael@0: bool isExclusiveContext() const { michael@0: return contextKind_ == Context_JS || contextKind_ == Context_Exclusive; michael@0: } michael@0: michael@0: ExclusiveContext *maybeExclusiveContext() const { michael@0: if (isExclusiveContext()) michael@0: return (ExclusiveContext *) this; michael@0: return nullptr; michael@0: } michael@0: michael@0: ExclusiveContext *asExclusiveContext() const { michael@0: JS_ASSERT(isExclusiveContext()); michael@0: return maybeExclusiveContext(); michael@0: } michael@0: michael@0: bool isForkJoinContext() const; michael@0: ForkJoinContext *asForkJoinContext(); michael@0: michael@0: // The generational GC nursery may only be used on the main thread. michael@0: #ifdef JSGC_GENERATIONAL michael@0: inline bool hasNursery() const { michael@0: return isJSContext(); michael@0: } michael@0: michael@0: inline js::Nursery &nursery() { michael@0: JS_ASSERT(hasNursery()); michael@0: return runtime_->gcNursery; michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * Allocator used when allocating GCThings on this context. If we are a michael@0: * JSContext, this is the Zone allocator of the JSContext's zone. michael@0: * Otherwise, this is a per-thread allocator. michael@0: * michael@0: * This does not live in PerThreadData because the notion of an allocator michael@0: * is only per-thread when off the main thread. The runtime (and the main michael@0: * thread) can have more than one zone, each with its own allocator, and michael@0: * it's up to the context to specify what compartment and zone we are michael@0: * operating in. michael@0: */ michael@0: protected: michael@0: Allocator *allocator_; michael@0: michael@0: public: michael@0: static size_t offsetOfAllocator() { return offsetof(ThreadSafeContext, allocator_); } michael@0: michael@0: inline Allocator *const allocator(); michael@0: michael@0: // Allocations can only trigger GC when running on the main thread. michael@0: inline AllowGC allowGC() const { return isJSContext() ? CanGC : NoGC; } michael@0: michael@0: template michael@0: bool isInsideCurrentZone(T thing) const { michael@0: return thing->zoneFromAnyThread() == zone_; michael@0: } michael@0: michael@0: template michael@0: inline bool isInsideCurrentCompartment(T thing) const { michael@0: return thing->compartment() == compartment_; michael@0: } michael@0: michael@0: template michael@0: inline bool isThreadLocal(T thing) const; michael@0: michael@0: void *onOutOfMemory(void *p, size_t nbytes) { michael@0: return runtime_->onOutOfMemory(p, nbytes, maybeJSContext()); michael@0: } michael@0: michael@0: /* Clear the pending exception (if any) due to OOM. */ michael@0: void recoverFromOutOfMemory(); michael@0: michael@0: inline void updateMallocCounter(size_t nbytes) { michael@0: // Note: this is racy. michael@0: runtime_->updateMallocCounter(zone_, nbytes); michael@0: } michael@0: michael@0: void reportAllocationOverflow() { michael@0: js_ReportAllocationOverflow(this); michael@0: } michael@0: michael@0: // Accessors for immutable runtime data. michael@0: JSAtomState &names() { return *runtime_->commonNames; } michael@0: StaticStrings &staticStrings() { return *runtime_->staticStrings; } michael@0: AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; } michael@0: const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; } michael@0: PropertyName *emptyString() { return runtime_->emptyString; } michael@0: FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } michael@0: bool useHelperThreads() { return runtime_->useHelperThreads(); } michael@0: void *runtimeAddressForJit() { return runtime_; } michael@0: void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } michael@0: void *stackLimitAddressForJitCode(StackKind kind); michael@0: size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; } michael@0: bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); } michael@0: bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } michael@0: michael@0: // Thread local data that may be accessed freely. michael@0: DtoaState *dtoaState() { michael@0: return perThreadData->dtoaState; michael@0: } michael@0: }; michael@0: michael@0: struct WorkerThread; michael@0: michael@0: class ExclusiveContext : public ThreadSafeContext michael@0: { michael@0: friend class gc::ArenaLists; michael@0: friend class AutoCompartment; michael@0: friend class AutoLockForExclusiveAccess; michael@0: friend struct StackBaseShape; michael@0: friend void JSScript::initCompartment(ExclusiveContext *cx); michael@0: friend class jit::IonContext; michael@0: michael@0: // The worker on which this context is running, if this is not a JSContext. michael@0: WorkerThread *workerThread_; michael@0: michael@0: public: michael@0: michael@0: ExclusiveContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind) michael@0: : ThreadSafeContext(rt, pt, kind), michael@0: workerThread_(nullptr), michael@0: enterCompartmentDepth_(0) michael@0: {} michael@0: michael@0: /* michael@0: * "Entering" a compartment changes cx->compartment (which changes michael@0: * cx->global). Note that this does not push any InterpreterFrame which means michael@0: * that it is possible for cx->fp()->compartment() != cx->compartment. michael@0: * This is not a problem since, in general, most places in the VM cannot michael@0: * know that they were called from script (e.g., they may have been called michael@0: * through the JSAPI via JS_CallFunction) and thus cannot expect fp. michael@0: * michael@0: * Compartments should be entered/left in a LIFO fasion. The depth of this michael@0: * enter/leave stack is maintained by enterCompartmentDepth_ and queried by michael@0: * hasEnteredCompartment. michael@0: * michael@0: * To enter a compartment, code should prefer using AutoCompartment over michael@0: * manually calling cx->enterCompartment/leaveCompartment. michael@0: */ michael@0: protected: michael@0: unsigned enterCompartmentDepth_; michael@0: inline void setCompartment(JSCompartment *comp); michael@0: public: michael@0: bool hasEnteredCompartment() const { michael@0: return enterCompartmentDepth_ > 0; michael@0: } michael@0: #ifdef DEBUG michael@0: unsigned getEnterCompartmentDepth() const { michael@0: return enterCompartmentDepth_; michael@0: } michael@0: #endif michael@0: michael@0: inline void enterCompartment(JSCompartment *c); michael@0: inline void enterNullCompartment(); michael@0: inline void leaveCompartment(JSCompartment *oldCompartment); michael@0: michael@0: void setWorkerThread(WorkerThread *workerThread); michael@0: WorkerThread *workerThread() const { return workerThread_; } michael@0: michael@0: // Threads with an ExclusiveContext may freely access any data in their michael@0: // compartment and zone. michael@0: JSCompartment *compartment() const { michael@0: JS_ASSERT_IF(runtime_->isAtomsCompartment(compartment_), michael@0: runtime_->currentThreadHasExclusiveAccess()); michael@0: return compartment_; michael@0: } michael@0: JS::Zone *zone() const { michael@0: JS_ASSERT_IF(!compartment(), !zone_); michael@0: JS_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); michael@0: return zone_; michael@0: } michael@0: michael@0: // Zone local methods that can be used freely from an ExclusiveContext. michael@0: types::TypeObject *getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun = nullptr); michael@0: types::TypeObject *getSingletonType(const Class *clasp, TaggedProto proto); michael@0: inline js::LifoAlloc &typeLifoAlloc(); michael@0: michael@0: // Current global. This is only safe to use within the scope of the michael@0: // AutoCompartment from which it's called. michael@0: inline js::Handle global() const; michael@0: michael@0: // Methods to access runtime data that must be protected by locks. michael@0: frontend::ParseMapPool &parseMapPool() { michael@0: return runtime_->parseMapPool(); michael@0: } michael@0: AtomSet &atoms() { michael@0: return runtime_->atoms(); michael@0: } michael@0: JSCompartment *atomsCompartment() { michael@0: return runtime_->atomsCompartment(); michael@0: } michael@0: ScriptDataTable &scriptDataTable() { michael@0: return runtime_->scriptDataTable(); michael@0: } michael@0: michael@0: // Methods specific to any WorkerThread for the context. michael@0: frontend::CompileError &addPendingCompileError(); michael@0: void addPendingOverRecursed(); michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: struct JSContext : public js::ExclusiveContext, michael@0: public mozilla::LinkedListElement michael@0: { michael@0: explicit JSContext(JSRuntime *rt); michael@0: ~JSContext(); michael@0: michael@0: JSRuntime *runtime() const { return runtime_; } michael@0: js::PerThreadData &mainThread() const { return runtime()->mainThread; } michael@0: michael@0: static size_t offsetOfRuntime() { michael@0: return offsetof(JSContext, runtime_); michael@0: } michael@0: michael@0: friend class js::ExclusiveContext; michael@0: friend class JS::AutoSaveExceptionState; michael@0: michael@0: private: michael@0: /* Exception state -- the exception member is a GC root by definition. */ michael@0: bool throwing; /* is there a pending exception? */ michael@0: js::Value unwrappedException_; /* most-recently-thrown exception */ michael@0: michael@0: /* Per-context options. */ michael@0: JS::ContextOptions options_; michael@0: michael@0: public: michael@0: int32_t reportGranularity; /* see vm/Probes.h */ michael@0: michael@0: js::AutoResolving *resolvingList; michael@0: michael@0: /* True if generating an error, to prevent runaway recursion. */ michael@0: bool generatingError; michael@0: michael@0: /* See JS_SaveFrameChain/JS_RestoreFrameChain. */ michael@0: private: michael@0: struct SavedFrameChain { michael@0: SavedFrameChain(JSCompartment *comp, unsigned count) michael@0: : compartment(comp), enterCompartmentCount(count) {} michael@0: JSCompartment *compartment; michael@0: unsigned enterCompartmentCount; michael@0: }; michael@0: typedef js::Vector SaveStack; michael@0: SaveStack savedFrameChains_; michael@0: public: michael@0: bool saveFrameChain(); michael@0: void restoreFrameChain(); michael@0: michael@0: /* michael@0: * When no compartments have been explicitly entered, the context's michael@0: * compartment will be set to the compartment of the "default compartment michael@0: * object". michael@0: */ michael@0: private: michael@0: JSObject *defaultCompartmentObject_; michael@0: public: michael@0: inline void setDefaultCompartmentObject(JSObject *obj); michael@0: inline void setDefaultCompartmentObjectIfUnset(JSObject *obj); michael@0: JSObject *maybeDefaultCompartmentObject() const { michael@0: JS_ASSERT(!options().noDefaultCompartmentObject()); michael@0: return defaultCompartmentObject_; michael@0: } michael@0: michael@0: /* State for object and array toSource conversion. */ michael@0: js::ObjectSet cycleDetectorSet; michael@0: michael@0: /* Per-context optional error reporter. */ michael@0: JSErrorReporter errorReporter; michael@0: michael@0: /* Client opaque pointers. */ michael@0: void *data; michael@0: void *data2; michael@0: michael@0: public: michael@0: michael@0: /* michael@0: * Return: michael@0: * - The newest scripted frame's version, if there is such a frame. michael@0: * - The version from the compartment. michael@0: * - The default version. michael@0: * michael@0: * Note: if this ever shows up in a profile, just add caching! michael@0: */ michael@0: JSVersion findVersion() const; michael@0: michael@0: const JS::ContextOptions &options() const { michael@0: return options_; michael@0: } michael@0: michael@0: JS::ContextOptions &options() { michael@0: return options_; michael@0: } michael@0: michael@0: js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; } michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: unsigned outstandingRequests;/* number of JS_BeginRequest calls michael@0: without the corresponding michael@0: JS_EndRequest. */ michael@0: #endif michael@0: michael@0: /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ michael@0: js::Value iterValue; michael@0: michael@0: bool jitIsBroken; michael@0: michael@0: void updateJITEnabled(); michael@0: michael@0: /* Whether this context has JS frames on the stack. */ michael@0: bool currentlyRunning() const; michael@0: michael@0: bool currentlyRunningInInterpreter() const { michael@0: return mainThread().activation()->isInterpreter(); michael@0: } michael@0: bool currentlyRunningInJit() const { michael@0: return mainThread().activation()->isJit(); michael@0: } michael@0: js::InterpreterFrame *interpreterFrame() const { michael@0: return mainThread().activation()->asInterpreter()->current(); michael@0: } michael@0: js::InterpreterRegs &interpreterRegs() const { michael@0: return mainThread().activation()->asInterpreter()->regs(); michael@0: } michael@0: michael@0: /* michael@0: * Get the topmost script and optional pc on the stack. By default, this michael@0: * function only returns a JSScript in the current compartment, returning michael@0: * nullptr if the current script is in a different compartment. This michael@0: * behavior can be overridden by passing ALLOW_CROSS_COMPARTMENT. michael@0: */ michael@0: enum MaybeAllowCrossCompartment { michael@0: DONT_ALLOW_CROSS_COMPARTMENT = false, michael@0: ALLOW_CROSS_COMPARTMENT = true michael@0: }; michael@0: inline JSScript *currentScript(jsbytecode **pc = nullptr, michael@0: MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const; michael@0: michael@0: #ifdef MOZ_TRACE_JSCALLS michael@0: /* Function entry/exit debugging callback. */ michael@0: JSFunctionCallback functionCallback; michael@0: michael@0: void doFunctionCallback(const JSFunction *fun, michael@0: const JSScript *scr, michael@0: int entering) const michael@0: { michael@0: if (functionCallback) michael@0: functionCallback(fun, scr, this, entering); michael@0: } michael@0: #endif michael@0: michael@0: private: michael@0: /* Innermost-executing generator or null if no generator are executing. */ michael@0: JSGenerator *innermostGenerator_; michael@0: public: michael@0: JSGenerator *innermostGenerator() const { return innermostGenerator_; } michael@0: void enterGenerator(JSGenerator *gen); michael@0: void leaveGenerator(JSGenerator *gen); michael@0: michael@0: bool isExceptionPending() { michael@0: return throwing; michael@0: } michael@0: michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool getPendingException(JS::MutableHandleValue rval); michael@0: michael@0: bool isThrowingOutOfMemory(); michael@0: michael@0: void setPendingException(js::Value v); michael@0: michael@0: void clearPendingException() { michael@0: throwing = false; michael@0: unwrappedException_.setUndefined(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* michael@0: * Controls whether a quadratic-complexity assertion is performed during michael@0: * stack iteration; defaults to true. michael@0: */ michael@0: bool stackIterAssertionEnabled; michael@0: #endif michael@0: michael@0: /* michael@0: * See JS_SetTrustedPrincipals in jsapi.h. michael@0: * Note: !cx->compartment is treated as trusted. michael@0: */ michael@0: bool runningWithTrustedPrincipals() const; michael@0: michael@0: JS_FRIEND_API(size_t) sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: michael@0: void mark(JSTracer *trc); michael@0: michael@0: private: michael@0: /* michael@0: * The allocation code calls the function to indicate either OOM failure michael@0: * when p is null or that a memory pressure counter has reached some michael@0: * threshold when p is not null. The function takes the pointer and not michael@0: * a boolean flag to minimize the amount of code in its inlined callers. michael@0: */ michael@0: JS_FRIEND_API(void) checkMallocGCPressure(void *p); michael@0: }; /* struct JSContext */ michael@0: michael@0: namespace js { michael@0: michael@0: struct AutoResolving { michael@0: public: michael@0: enum Kind { michael@0: LOOKUP, michael@0: WATCH michael@0: }; michael@0: michael@0: AutoResolving(JSContext *cx, HandleObject obj, HandleId id, Kind kind = LOOKUP michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: JS_ASSERT(obj); michael@0: cx->resolvingList = this; michael@0: } michael@0: michael@0: ~AutoResolving() { michael@0: JS_ASSERT(context->resolvingList == this); michael@0: context->resolvingList = link; michael@0: } michael@0: michael@0: bool alreadyStarted() const { michael@0: return link && alreadyStartedSlow(); michael@0: } michael@0: michael@0: private: michael@0: bool alreadyStartedSlow() const; michael@0: michael@0: JSContext *const context; michael@0: HandleObject object; michael@0: HandleId id; michael@0: Kind const kind; michael@0: AutoResolving *const link; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: /* michael@0: * Enumerate all contexts in a runtime. michael@0: */ michael@0: class ContextIter { michael@0: JSContext *iter; michael@0: michael@0: public: michael@0: explicit ContextIter(JSRuntime *rt) { michael@0: iter = rt->contextList.getFirst(); michael@0: } michael@0: michael@0: bool done() const { michael@0: return !iter; michael@0: } michael@0: michael@0: void next() { michael@0: JS_ASSERT(!done()); michael@0: iter = iter->getNext(); michael@0: } michael@0: michael@0: JSContext *get() const { michael@0: JS_ASSERT(!done()); michael@0: return iter; michael@0: } michael@0: michael@0: operator JSContext *() const { michael@0: return get(); michael@0: } michael@0: michael@0: JSContext *operator ->() const { michael@0: return get(); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Create and destroy functions for JSContext, which is manually allocated michael@0: * and exclusively owned. michael@0: */ michael@0: extern JSContext * michael@0: NewContext(JSRuntime *rt, size_t stackChunkSize); michael@0: michael@0: enum DestroyContextMode { michael@0: DCM_NO_GC, michael@0: DCM_FORCE_GC, michael@0: DCM_NEW_FAILED michael@0: }; michael@0: michael@0: extern void michael@0: DestroyContext(JSContext *cx, DestroyContextMode mode); michael@0: michael@0: enum ErrorArgumentsType { michael@0: ArgumentsAreUnicode, michael@0: ArgumentsAreASCII michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * Loads and returns a self-hosted function by name. For performance, define michael@0: * the property name in vm/CommonPropertyNames.h. michael@0: * michael@0: * Defined in SelfHosting.cpp. michael@0: */ michael@0: JSFunction * michael@0: SelfHostedFunction(JSContext *cx, HandlePropertyName propName); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #ifdef va_start michael@0: extern bool michael@0: js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap); michael@0: michael@0: extern bool michael@0: js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: js::ErrorArgumentsType argumentsType, va_list ap); michael@0: michael@0: extern bool michael@0: js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: const jschar **args); michael@0: #endif michael@0: michael@0: extern bool michael@0: js_ExpandErrorArguments(js::ExclusiveContext *cx, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: char **message, JSErrorReport *reportp, michael@0: js::ErrorArgumentsType argumentsType, va_list ap); michael@0: michael@0: namespace js { michael@0: michael@0: /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ michael@0: extern void michael@0: ReportUsageError(JSContext *cx, HandleObject callee, const char *msg); michael@0: michael@0: /* michael@0: * Prints a full report and returns true if the given report is non-nullptr michael@0: * and the report doesn't have the JSREPORT_WARNING flag set or reportWarnings michael@0: * is true. michael@0: * Returns false otherwise, printing just the message if the report is nullptr. michael@0: */ michael@0: extern bool michael@0: PrintError(JSContext *cx, FILE *file, const char *message, JSErrorReport *report, michael@0: bool reportWarnings); michael@0: michael@0: /* michael@0: * Send a JSErrorReport to the errorReporter callback. michael@0: */ michael@0: void michael@0: CallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: extern void michael@0: js_ReportIsNotDefined(JSContext *cx, const char *name); michael@0: michael@0: /* michael@0: * Report an attempt to access the property of a null or undefined value (v). michael@0: */ michael@0: extern bool michael@0: js_ReportIsNullOrUndefined(JSContext *cx, int spindex, js::HandleValue v, michael@0: js::HandleString fallback); michael@0: michael@0: extern void michael@0: js_ReportMissingArg(JSContext *cx, js::HandleValue v, unsigned arg); michael@0: michael@0: /* michael@0: * Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as michael@0: * the first argument for the error message. If the error message has less michael@0: * then 3 arguments, use null for arg1 or arg2. michael@0: */ michael@0: extern bool michael@0: js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber, michael@0: int spindex, js::HandleValue v, js::HandleString fallback, michael@0: const char *arg1, const char *arg2); michael@0: michael@0: #define js_ReportValueError(cx,errorNumber,spindex,v,fallback) \ michael@0: ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ michael@0: spindex, v, fallback, nullptr, nullptr)) michael@0: michael@0: #define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \ michael@0: ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ michael@0: spindex, v, fallback, arg1, nullptr)) michael@0: michael@0: #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ michael@0: ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ michael@0: spindex, v, fallback, arg1, arg2)) michael@0: michael@0: extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; michael@0: michael@0: char * michael@0: js_strdup(js::ExclusiveContext *cx, const char *s); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: # define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime()->requestDepth >= 1) michael@0: #else michael@0: # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) michael@0: #endif michael@0: michael@0: namespace js { michael@0: michael@0: /* michael@0: * Invoke the interrupt callback and return false if the current execution michael@0: * is to be terminated. michael@0: */ michael@0: bool michael@0: InvokeInterruptCallback(JSContext *cx); michael@0: michael@0: bool michael@0: HandleExecutionInterrupt(JSContext *cx); michael@0: michael@0: /* michael@0: * Process any pending interrupt requests. Long-running inner loops in C++ must michael@0: * call this periodically to make sure they are interruptible --- that is, to michael@0: * make sure they do not prevent the slow script dialog from appearing. michael@0: * michael@0: * This can run a full GC or call the interrupt callback, which could do michael@0: * anything. In the browser, it displays the slow script dialog. michael@0: * michael@0: * If this returns true, the caller can continue; if false, the caller must michael@0: * break out of its loop. This happens if, for example, the user clicks "Stop michael@0: * script" on the slow script dialog; treat it as an uncatchable error. michael@0: */ michael@0: inline bool michael@0: CheckForInterrupt(JSContext *cx) michael@0: { michael@0: JS_ASSERT_REQUEST_DEPTH(cx); michael@0: return !cx->runtime()->interrupt || InvokeInterruptCallback(cx); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: class AutoStringVector : public AutoVectorRooter michael@0: { michael@0: public: michael@0: explicit AutoStringVector(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoVectorRooter(cx, STRINGVECTOR) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoPropertyNameVector : public AutoVectorRooter michael@0: { michael@0: public: michael@0: explicit AutoPropertyNameVector(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoVectorRooter(cx, STRINGVECTOR) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoShapeVector : public AutoVectorRooter michael@0: { michael@0: public: michael@0: explicit AutoShapeVector(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoVectorRooter(cx, SHAPEVECTOR) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoObjectObjectHashMap : public AutoHashMapRooter michael@0: { michael@0: public: michael@0: explicit AutoObjectObjectHashMap(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoHashMapRooter(cx, OBJOBJHASHMAP) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoObjectUnsigned32HashMap : public AutoHashMapRooter michael@0: { michael@0: public: michael@0: explicit AutoObjectUnsigned32HashMap(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoHashMapRooter(cx, OBJU32HASHMAP) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoObjectHashSet : public AutoHashSetRooter michael@0: { michael@0: public: michael@0: explicit AutoObjectHashSet(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoHashSetRooter(cx, OBJHASHSET) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: /* AutoArrayRooter roots an external array of Values. */ michael@0: class AutoArrayRooter : private AutoGCRooter michael@0: { michael@0: public: michael@0: AutoArrayRooter(JSContext *cx, size_t len, Value *vec michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoGCRooter(cx, len), array(vec) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: JS_ASSERT(tag_ >= 0); michael@0: } michael@0: michael@0: void changeLength(size_t newLength) { michael@0: tag_ = ptrdiff_t(newLength); michael@0: JS_ASSERT(tag_ >= 0); michael@0: } michael@0: michael@0: void changeArray(Value *newArray, size_t newLength) { michael@0: changeLength(newLength); michael@0: array = newArray; michael@0: } michael@0: michael@0: Value *start() { michael@0: return array; michael@0: } michael@0: michael@0: size_t length() { michael@0: JS_ASSERT(tag_ >= 0); michael@0: return size_t(tag_); michael@0: } michael@0: michael@0: MutableHandleValue handleAt(size_t i) { michael@0: JS_ASSERT(i < size_t(tag_)); michael@0: return MutableHandleValue::fromMarkedLocation(&array[i]); michael@0: } michael@0: HandleValue handleAt(size_t i) const { michael@0: JS_ASSERT(i < size_t(tag_)); michael@0: return HandleValue::fromMarkedLocation(&array[i]); michael@0: } michael@0: MutableHandleValue operator[](size_t i) { michael@0: JS_ASSERT(i < size_t(tag_)); michael@0: return MutableHandleValue::fromMarkedLocation(&array[i]); michael@0: } michael@0: HandleValue operator[](size_t i) const { michael@0: JS_ASSERT(i < size_t(tag_)); michael@0: return HandleValue::fromMarkedLocation(&array[i]); michael@0: } michael@0: michael@0: friend void AutoGCRooter::trace(JSTracer *trc); michael@0: michael@0: private: michael@0: Value *array; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoAssertNoException michael@0: { michael@0: #ifdef DEBUG michael@0: JSContext *cx; michael@0: bool hadException; michael@0: #endif michael@0: michael@0: public: michael@0: AutoAssertNoException(JSContext *cx) michael@0: #ifdef DEBUG michael@0: : cx(cx), michael@0: hadException(cx->isExceptionPending()) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: ~AutoAssertNoException() michael@0: { michael@0: JS_ASSERT_IF(!hadException, !cx->isExceptionPending()); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * FIXME bug 647103 - replace these *AllocPolicy names. michael@0: */ michael@0: class ContextAllocPolicy michael@0: { michael@0: ThreadSafeContext *const cx_; michael@0: michael@0: public: michael@0: ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {} michael@0: ThreadSafeContext *context() const { return cx_; } michael@0: void *malloc_(size_t bytes) { return cx_->malloc_(bytes); } michael@0: void *calloc_(size_t bytes) { return cx_->calloc_(bytes); } michael@0: void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); } michael@0: void free_(void *p) { js_free(p); } michael@0: void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); } michael@0: }; michael@0: michael@0: /* Exposed intrinsics so that Ion may inline them. */ michael@0: bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: bool intrinsic_ObjectIsTypedObject(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_TypeDescrIsSimpleType(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_TypeDescrIsArrayType(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_TypeDescrIsUnsizedArrayType(JSContext *cx, unsigned argc, Value *vp); michael@0: bool intrinsic_TypeDescrIsSizedArrayType(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: class AutoLockForExclusiveAccess michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: JSRuntime *runtime; michael@0: michael@0: void init(JSRuntime *rt) { michael@0: runtime = rt; michael@0: if (runtime->numExclusiveThreads) { michael@0: runtime->assertCanLock(ExclusiveAccessLock); michael@0: PR_Lock(runtime->exclusiveAccessLock); michael@0: #ifdef DEBUG michael@0: runtime->exclusiveAccessOwner = PR_GetCurrentThread(); michael@0: #endif michael@0: } else { michael@0: JS_ASSERT(!runtime->mainThreadHasExclusiveAccess); michael@0: runtime->mainThreadHasExclusiveAccess = true; michael@0: } michael@0: } michael@0: michael@0: public: michael@0: AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: init(cx->runtime_); michael@0: } michael@0: AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: init(rt); michael@0: } michael@0: ~AutoLockForExclusiveAccess() { michael@0: if (runtime->numExclusiveThreads) { michael@0: JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread()); michael@0: runtime->exclusiveAccessOwner = nullptr; michael@0: PR_Unlock(runtime->exclusiveAccessLock); michael@0: } else { michael@0: JS_ASSERT(runtime->mainThreadHasExclusiveAccess); michael@0: runtime->mainThreadHasExclusiveAccess = false; michael@0: } michael@0: } michael@0: #else // JS_THREADSAFE michael@0: public: michael@0: AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: ~AutoLockForExclusiveAccess() { michael@0: // An empty destructor is needed to avoid warnings from clang about michael@0: // unused local variables of this type. michael@0: } michael@0: #endif // JS_THREADSAFE michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: void michael@0: CrashAtUnhandlableOOM(const char *reason); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #ifdef _MSC_VER michael@0: #pragma warning(pop) michael@0: #endif michael@0: michael@0: #endif /* jscntxt_h */