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: #ifndef vm_Runtime_h michael@0: #define vm_Runtime_h michael@0: michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/LinkedList.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/PodOperations.h" michael@0: #include "mozilla/Scoped.h" michael@0: #include "mozilla/ThreadLocal.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsatom.h" michael@0: #include "jsclist.h" michael@0: #include "jsgc.h" michael@0: #ifdef DEBUG michael@0: # include "jsproxy.h" michael@0: #endif michael@0: #include "jsscript.h" michael@0: michael@0: #include "ds/FixedSizeHash.h" michael@0: #include "frontend/ParseMaps.h" michael@0: #ifdef JSGC_GENERATIONAL michael@0: # include "gc/Nursery.h" michael@0: #endif michael@0: #include "gc/Statistics.h" michael@0: #ifdef JSGC_GENERATIONAL michael@0: # include "gc/StoreBuffer.h" michael@0: #endif michael@0: #include "gc/Tracer.h" michael@0: #ifdef XP_MACOSX michael@0: # include "jit/AsmJSSignalHandlers.h" michael@0: #endif michael@0: #include "js/HashTable.h" michael@0: #include "js/Vector.h" michael@0: #include "vm/CommonPropertyNames.h" michael@0: #include "vm/DateTime.h" michael@0: #include "vm/MallocProvider.h" michael@0: #include "vm/SPSProfiler.h" michael@0: #include "vm/Stack.h" michael@0: #include "vm/ThreadPool.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: namespace js { michael@0: michael@0: class PerThreadData; michael@0: class ThreadSafeContext; michael@0: class AutoKeepAtoms; michael@0: #ifdef JS_TRACE_LOGGING michael@0: class TraceLogger; michael@0: #endif michael@0: michael@0: /* Thread Local Storage slot for storing the runtime for a thread. */ michael@0: extern mozilla::ThreadLocal TlsPerThreadData; michael@0: michael@0: } // namespace js 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 JSC { class ExecutableAllocator; } michael@0: michael@0: namespace WTF { class BumpPointerAllocator; } michael@0: michael@0: namespace js { michael@0: michael@0: typedef Rooted RootedLinearString; michael@0: michael@0: class Activation; michael@0: class ActivationIterator; michael@0: class AsmJSActivation; michael@0: class MathCache; michael@0: michael@0: namespace jit { michael@0: class JitRuntime; michael@0: class JitActivation; michael@0: struct PcScriptCache; michael@0: class Simulator; michael@0: class SimulatorRuntime; michael@0: class AutoFlushICache; michael@0: } michael@0: michael@0: /* michael@0: * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a michael@0: * given pc in a script. We use the script->code pointer to tag the cache, michael@0: * instead of the script address itself, so that source notes are always found michael@0: * by offset from the bytecode with which they were generated. michael@0: */ michael@0: struct GSNCache { michael@0: typedef HashMap, michael@0: SystemAllocPolicy> Map; michael@0: michael@0: jsbytecode *code; michael@0: Map map; michael@0: michael@0: GSNCache() : code(nullptr) { } michael@0: michael@0: void purge(); michael@0: }; michael@0: michael@0: /* michael@0: * ScopeCoordinateName cache to avoid O(n^2) growth in finding the name michael@0: * associated with a given aliasedvar operation. michael@0: */ michael@0: struct ScopeCoordinateNameCache { michael@0: typedef HashMap, michael@0: SystemAllocPolicy> Map; michael@0: michael@0: Shape *shape; michael@0: Map map; michael@0: michael@0: ScopeCoordinateNameCache() : shape(nullptr) {} michael@0: void purge(); michael@0: }; michael@0: michael@0: typedef Vector ScriptAndCountsVector; michael@0: michael@0: struct ConservativeGCData michael@0: { michael@0: /* michael@0: * The GC scans conservatively between ThreadData::nativeStackBase and michael@0: * nativeStackTop unless the latter is nullptr. michael@0: */ michael@0: uintptr_t *nativeStackTop; michael@0: michael@0: union { michael@0: jmp_buf jmpbuf; michael@0: uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; michael@0: } registerSnapshot; michael@0: michael@0: ConservativeGCData() { michael@0: mozilla::PodZero(this); michael@0: } michael@0: michael@0: ~ConservativeGCData() { michael@0: #ifdef JS_THREADSAFE michael@0: /* michael@0: * The conservative GC scanner should be disabled when the thread leaves michael@0: * the last request. michael@0: */ michael@0: JS_ASSERT(!hasStackToScan()); michael@0: #endif michael@0: } michael@0: michael@0: MOZ_NEVER_INLINE void recordStackTop(); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: void updateForRequestEnd() { michael@0: nativeStackTop = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: bool hasStackToScan() const { michael@0: return !!nativeStackTop; michael@0: } michael@0: }; michael@0: michael@0: struct EvalCacheEntry michael@0: { michael@0: JSScript *script; michael@0: JSScript *callerScript; michael@0: jsbytecode *pc; michael@0: }; michael@0: michael@0: struct EvalCacheLookup michael@0: { michael@0: EvalCacheLookup(JSContext *cx) : str(cx), callerScript(cx) {} michael@0: RootedLinearString str; michael@0: RootedScript callerScript; michael@0: JSVersion version; michael@0: jsbytecode *pc; michael@0: }; michael@0: michael@0: struct EvalCacheHashPolicy michael@0: { michael@0: typedef EvalCacheLookup Lookup; michael@0: michael@0: static HashNumber hash(const Lookup &l); michael@0: static bool match(const EvalCacheEntry &entry, const EvalCacheLookup &l); michael@0: }; michael@0: michael@0: typedef HashSet EvalCache; michael@0: michael@0: struct LazyScriptHashPolicy michael@0: { michael@0: struct Lookup { michael@0: JSContext *cx; michael@0: LazyScript *lazy; michael@0: michael@0: Lookup(JSContext *cx, LazyScript *lazy) michael@0: : cx(cx), lazy(lazy) michael@0: {} michael@0: }; michael@0: michael@0: static const size_t NumHashes = 3; michael@0: michael@0: static void hash(const Lookup &lookup, HashNumber hashes[NumHashes]); michael@0: static bool match(JSScript *script, const Lookup &lookup); michael@0: michael@0: // Alternate methods for use when removing scripts from the hash without an michael@0: // explicit LazyScript lookup. michael@0: static void hash(JSScript *script, HashNumber hashes[NumHashes]); michael@0: static bool match(JSScript *script, JSScript *lookup) { return script == lookup; } michael@0: michael@0: static void clear(JSScript **pscript) { *pscript = nullptr; } michael@0: static bool isCleared(JSScript *script) { return !script; } michael@0: }; michael@0: michael@0: typedef FixedSizeHashSet LazyScriptCache; michael@0: michael@0: class PropertyIteratorObject; michael@0: michael@0: class NativeIterCache michael@0: { michael@0: static const size_t SIZE = size_t(1) << 8; michael@0: michael@0: /* Cached native iterators. */ michael@0: PropertyIteratorObject *data[SIZE]; michael@0: michael@0: static size_t getIndex(uint32_t key) { michael@0: return size_t(key) % SIZE; michael@0: } michael@0: michael@0: public: michael@0: /* Native iterator most recently started. */ michael@0: PropertyIteratorObject *last; michael@0: michael@0: NativeIterCache() michael@0: : last(nullptr) michael@0: { michael@0: mozilla::PodArrayZero(data); michael@0: } michael@0: michael@0: void purge() { michael@0: last = nullptr; michael@0: mozilla::PodArrayZero(data); michael@0: } michael@0: michael@0: PropertyIteratorObject *get(uint32_t key) const { michael@0: return data[getIndex(key)]; michael@0: } michael@0: michael@0: void set(uint32_t key, PropertyIteratorObject *iterobj) { michael@0: data[getIndex(key)] = iterobj; michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Cache for speeding up repetitive creation of objects in the VM. michael@0: * When an object is created which matches the criteria in the 'key' section michael@0: * below, an entry is filled with the resulting object. michael@0: */ michael@0: class NewObjectCache michael@0: { michael@0: /* Statically asserted to be equal to sizeof(JSObject_Slots16) */ michael@0: static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value); michael@0: michael@0: static void staticAsserts() { michael@0: JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16)); michael@0: JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND); michael@0: } michael@0: michael@0: struct Entry michael@0: { michael@0: /* Class of the constructed object. */ michael@0: const Class *clasp; michael@0: michael@0: /* michael@0: * Key with one of three possible values: michael@0: * michael@0: * - Global for the object. The object must have a standard class for michael@0: * which the global's prototype can be determined, and the object's michael@0: * parent will be the global. michael@0: * michael@0: * - Prototype for the object (cannot be global). The object's parent michael@0: * will be the prototype's parent. michael@0: * michael@0: * - Type for the object. The object's parent will be the type's michael@0: * prototype's parent. michael@0: */ michael@0: gc::Cell *key; michael@0: michael@0: /* Allocation kind for the constructed object. */ michael@0: gc::AllocKind kind; michael@0: michael@0: /* Number of bytes to copy from the template object. */ michael@0: uint32_t nbytes; michael@0: michael@0: /* michael@0: * Template object to copy from, with the initial values of fields, michael@0: * fixed slots (undefined) and private data (nullptr). michael@0: */ michael@0: char templateObject[MAX_OBJ_SIZE]; michael@0: }; michael@0: michael@0: Entry entries[41]; // TODO: reconsider size michael@0: michael@0: public: michael@0: michael@0: typedef int EntryIndex; michael@0: michael@0: NewObjectCache() { mozilla::PodZero(this); } michael@0: void purge() { mozilla::PodZero(this); } michael@0: michael@0: /* Remove any cached items keyed on moved objects. */ michael@0: void clearNurseryObjects(JSRuntime *rt); michael@0: michael@0: /* michael@0: * Get the entry index for the given lookup, return whether there was a hit michael@0: * on an existing entry. michael@0: */ michael@0: inline bool lookupProto(const Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry); michael@0: inline bool lookupGlobal(const Class *clasp, js::GlobalObject *global, gc::AllocKind kind, michael@0: EntryIndex *pentry); michael@0: michael@0: bool lookupType(js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry) { michael@0: return lookup(type->clasp(), type, kind, pentry); michael@0: } michael@0: michael@0: /* michael@0: * Return a new object from a cache hit produced by a lookup method, or michael@0: * nullptr if returning the object could possibly trigger GC (does not michael@0: * indicate failure). michael@0: */ michael@0: template michael@0: inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap); michael@0: michael@0: /* Fill an entry after a cache miss. */ michael@0: void fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto, gc::AllocKind kind, JSObject *obj); michael@0: michael@0: inline void fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global, michael@0: gc::AllocKind kind, JSObject *obj); michael@0: michael@0: void fillType(EntryIndex entry, js::types::TypeObject *type, gc::AllocKind kind, michael@0: JSObject *obj) michael@0: { michael@0: JS_ASSERT(obj->type() == type); michael@0: return fill(entry, type->clasp(), type, kind, obj); michael@0: } michael@0: michael@0: /* Invalidate any entries which might produce an object with shape/proto. */ michael@0: void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto); michael@0: michael@0: private: michael@0: bool lookup(const Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry) { michael@0: uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind; michael@0: *pentry = hash % mozilla::ArrayLength(entries); michael@0: michael@0: Entry *entry = &entries[*pentry]; michael@0: michael@0: /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */ michael@0: return entry->clasp == clasp && entry->key == key; michael@0: } michael@0: michael@0: void fill(EntryIndex entry_, const Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj) { michael@0: JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries)); michael@0: Entry *entry = &entries[entry_]; michael@0: michael@0: JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements()); michael@0: michael@0: entry->clasp = clasp; michael@0: entry->key = key; michael@0: entry->kind = kind; michael@0: michael@0: entry->nbytes = gc::Arena::thingSize(kind); michael@0: js_memcpy(&entry->templateObject, obj, entry->nbytes); michael@0: } michael@0: michael@0: static void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind) { michael@0: js_memcpy(dst, src, gc::Arena::thingSize(kind)); michael@0: #ifdef JSGC_GENERATIONAL michael@0: Shape::writeBarrierPost(dst->shape_, &dst->shape_); michael@0: types::TypeObject::writeBarrierPost(dst->type_, &dst->type_); michael@0: #endif michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * A FreeOp can do one thing: free memory. For convenience, it has delete_ michael@0: * convenience methods that also call destructors. michael@0: * michael@0: * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not michael@0: * need to pass a JSContext to those hooks. michael@0: */ michael@0: class FreeOp : public JSFreeOp { michael@0: bool shouldFreeLater_; michael@0: michael@0: public: michael@0: static FreeOp *get(JSFreeOp *fop) { michael@0: return static_cast(fop); michael@0: } michael@0: michael@0: FreeOp(JSRuntime *rt, bool shouldFreeLater) michael@0: : JSFreeOp(rt), michael@0: shouldFreeLater_(shouldFreeLater) michael@0: { michael@0: } michael@0: michael@0: bool shouldFreeLater() const { michael@0: return shouldFreeLater_; michael@0: } michael@0: michael@0: inline void free_(void *p); michael@0: michael@0: template michael@0: inline void delete_(T *p) { michael@0: if (p) { michael@0: p->~T(); michael@0: free_(p); michael@0: } michael@0: } michael@0: michael@0: static void staticAsserts() { michael@0: /* michael@0: * Check that JSFreeOp is the first base class for FreeOp and we can michael@0: * reinterpret a pointer to JSFreeOp as a pointer to FreeOp without michael@0: * any offset adjustments. JSClass::finalize <-> Class::finalize depends michael@0: * on this. michael@0: */ michael@0: JS_STATIC_ASSERT(offsetof(FreeOp, shouldFreeLater_) == sizeof(JSFreeOp)); michael@0: } michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: namespace JS { michael@0: struct RuntimeSizes; michael@0: } michael@0: michael@0: /* Various built-in or commonly-used names pinned on first context. */ michael@0: struct JSAtomState michael@0: { michael@0: #define PROPERTYNAME_FIELD(idpart, id, text) js::FixedHeapPtr id; michael@0: FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD) michael@0: #undef PROPERTYNAME_FIELD michael@0: #define PROPERTYNAME_FIELD(name, code, init, clasp) js::FixedHeapPtr name; michael@0: JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD) michael@0: #undef PROPERTYNAME_FIELD michael@0: }; michael@0: michael@0: namespace js { michael@0: michael@0: #define NAME_OFFSET(name) offsetof(JSAtomState, name) michael@0: michael@0: inline HandlePropertyName michael@0: AtomStateOffsetToName(const JSAtomState &atomState, size_t offset) michael@0: { michael@0: return *(js::FixedHeapPtr*)((char*)&atomState + offset); michael@0: } michael@0: michael@0: // There are several coarse locks in the enum below. These may be either michael@0: // per-runtime or per-process. When acquiring more than one of these locks, michael@0: // the acquisition must be done in the order below to avoid deadlocks. michael@0: enum RuntimeLock { michael@0: ExclusiveAccessLock, michael@0: WorkerThreadStateLock, michael@0: InterruptLock, michael@0: GCLock michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: void AssertCurrentThreadCanLock(RuntimeLock which); michael@0: #else michael@0: inline void AssertCurrentThreadCanLock(RuntimeLock which) {} michael@0: #endif michael@0: michael@0: /* michael@0: * Encapsulates portions of the runtime/context that are tied to a michael@0: * single active thread. Instances of this structure can occur for michael@0: * the main thread as |JSRuntime::mainThread|, for select operations michael@0: * performed off thread, such as parsing, and for Parallel JS worker michael@0: * threads. michael@0: */ michael@0: class PerThreadData : public PerThreadDataFriendFields michael@0: { michael@0: /* michael@0: * Backpointer to the full shared JSRuntime* with which this michael@0: * thread is associated. This is private because accessing the michael@0: * fields of this runtime can provoke race conditions, so the michael@0: * intention is that access will be mediated through safe michael@0: * functions like |runtimeFromMainThread| and |associatedWith()| below. michael@0: */ michael@0: JSRuntime *runtime_; michael@0: michael@0: public: michael@0: /* michael@0: * We save all conservative scanned roots in this vector so that michael@0: * conservative scanning can be "replayed" deterministically. In DEBUG mode, michael@0: * this allows us to run a non-incremental GC after every incremental GC to michael@0: * ensure that no objects were missed. michael@0: */ michael@0: #ifdef DEBUG michael@0: struct SavedGCRoot { michael@0: void *thing; michael@0: JSGCTraceKind kind; michael@0: michael@0: SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {} michael@0: }; michael@0: js::Vector gcSavedRoots; michael@0: #endif michael@0: michael@0: /* michael@0: * If Ion code is on the stack, and has called into C++, this will be michael@0: * aligned to an Ion exit frame. michael@0: */ michael@0: uint8_t *ionTop; michael@0: michael@0: /* michael@0: * The current JSContext when entering JIT code. This field may only be used michael@0: * from JIT code and C++ directly called by JIT code (otherwise it may refer michael@0: * to the wrong JSContext). michael@0: */ michael@0: JSContext *jitJSContext; michael@0: michael@0: /* michael@0: * The stack limit checked by JIT code. This stack limit may be temporarily michael@0: * set to null to force JIT code to exit (e.g., for the operation callback). michael@0: */ michael@0: uintptr_t jitStackLimit; michael@0: michael@0: inline void setJitStackLimit(uintptr_t limit); michael@0: michael@0: #ifdef JS_TRACE_LOGGING michael@0: TraceLogger *traceLogger; michael@0: #endif michael@0: michael@0: /* michael@0: * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This michael@0: * stack is used by JSRuntime::requestInterrupt to stop long-running asm.js michael@0: * without requiring dynamic polling operations in the generated michael@0: * code. Since requestInterrupt may run on a separate thread than the michael@0: * JSRuntime's owner thread all reads/writes must be synchronized (by michael@0: * rt->interruptLock). michael@0: */ michael@0: private: michael@0: friend class js::Activation; michael@0: friend class js::ActivationIterator; michael@0: friend class js::jit::JitActivation; michael@0: friend class js::AsmJSActivation; michael@0: #ifdef DEBUG michael@0: friend void js::AssertCurrentThreadCanLock(RuntimeLock which); michael@0: #endif michael@0: michael@0: /* michael@0: * Points to the most recent activation running on the thread. michael@0: * See Activation comment in vm/Stack.h. michael@0: */ michael@0: js::Activation *activation_; michael@0: michael@0: /* See AsmJSActivation comment. Protected by rt->interruptLock. */ michael@0: js::AsmJSActivation *asmJSActivationStack_; michael@0: michael@0: /* Pointer to the current AutoFlushICache. */ michael@0: js::jit::AutoFlushICache *autoFlushICache_; michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js::jit::Simulator *simulator_; michael@0: uintptr_t simulatorStackLimit_; michael@0: #endif michael@0: michael@0: public: michael@0: js::Activation *const *addressOfActivation() const { michael@0: return &activation_; michael@0: } michael@0: static unsigned offsetOfAsmJSActivationStackReadOnly() { michael@0: return offsetof(PerThreadData, asmJSActivationStack_); michael@0: } michael@0: static unsigned offsetOfActivation() { michael@0: return offsetof(PerThreadData, activation_); michael@0: } michael@0: michael@0: js::AsmJSActivation *asmJSActivationStackFromAnyThread() const { michael@0: return asmJSActivationStack_; michael@0: } michael@0: js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const { michael@0: return asmJSActivationStack_; michael@0: } michael@0: michael@0: js::Activation *activation() const { michael@0: return activation_; michael@0: } michael@0: michael@0: /* State used by jsdtoa.cpp. */ michael@0: DtoaState *dtoaState; michael@0: michael@0: /* michael@0: * When this flag is non-zero, any attempt to GC will be skipped. It is used michael@0: * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in michael@0: * debugging facilities that cannot tolerate a GC and would rather OOM michael@0: * immediately, such as utilities exposed to GDB. Setting this flag is michael@0: * extremely dangerous and should only be used when in an OOM situation or michael@0: * in non-exposed debugging facilities. michael@0: */ michael@0: int32_t suppressGC; michael@0: michael@0: // Number of active bytecode compilation on this thread. michael@0: unsigned activeCompilations; michael@0: michael@0: PerThreadData(JSRuntime *runtime); michael@0: ~PerThreadData(); michael@0: michael@0: bool init(); michael@0: michael@0: bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } michael@0: inline JSRuntime *runtimeFromMainThread(); michael@0: inline JSRuntime *runtimeIfOnOwnerThread(); michael@0: michael@0: inline bool exclusiveThreadsPresent(); michael@0: inline void addActiveCompilation(); michael@0: inline void removeActiveCompilation(); michael@0: michael@0: // For threads which may be associated with different runtimes, depending michael@0: // on the work they are doing. michael@0: class MOZ_STACK_CLASS AutoEnterRuntime michael@0: { michael@0: PerThreadData *pt; michael@0: michael@0: public: michael@0: AutoEnterRuntime(PerThreadData *pt, JSRuntime *rt) michael@0: : pt(pt) michael@0: { michael@0: JS_ASSERT(!pt->runtime_); michael@0: pt->runtime_ = rt; michael@0: } michael@0: michael@0: ~AutoEnterRuntime() { michael@0: pt->runtime_ = nullptr; michael@0: } michael@0: }; michael@0: michael@0: js::jit::AutoFlushICache *autoFlushICache() const; michael@0: void setAutoFlushICache(js::jit::AutoFlushICache *afc); michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js::jit::Simulator *simulator() const; michael@0: void setSimulator(js::jit::Simulator *sim); michael@0: js::jit::SimulatorRuntime *simulatorRuntime() const; michael@0: uintptr_t *addressOfSimulatorStackLimit(); michael@0: #endif michael@0: }; michael@0: michael@0: namespace gc { michael@0: class MarkingValidator; michael@0: } // namespace gc michael@0: michael@0: typedef Vector ZoneVector; michael@0: michael@0: class AutoLockForExclusiveAccess; michael@0: michael@0: void RecomputeStackLimit(JSRuntime *rt, StackKind kind); michael@0: michael@0: } // namespace js michael@0: michael@0: struct JSRuntime : public JS::shadow::Runtime, michael@0: public js::MallocProvider michael@0: { michael@0: /* michael@0: * Per-thread data for the main thread that is associated with michael@0: * this JSRuntime, as opposed to any worker threads used in michael@0: * parallel sections. See definition of |PerThreadData| struct michael@0: * above for more details. michael@0: * michael@0: * NB: This field is statically asserted to be at offset michael@0: * sizeof(js::shadow::Runtime). See michael@0: * PerThreadDataFriendFields::getMainThread. michael@0: */ michael@0: js::PerThreadData mainThread; michael@0: michael@0: /* michael@0: * If non-null, another runtime guaranteed to outlive this one and whose michael@0: * permanent data may be used by this one where possible. michael@0: */ michael@0: JSRuntime *parentRuntime; michael@0: michael@0: /* michael@0: * If true, we've been asked to call the interrupt callback as soon as michael@0: * possible. michael@0: */ michael@0: mozilla::Atomic interrupt; michael@0: michael@0: #if defined(JS_THREADSAFE) && defined(JS_ION) michael@0: /* michael@0: * If non-zero, ForkJoin should service an interrupt. This is a separate michael@0: * flag from |interrupt| because we cannot use the mprotect trick with PJS michael@0: * code and ignore the TriggerCallbackAnyThreadDontStopIon trigger. michael@0: */ michael@0: mozilla::Atomic interruptPar; michael@0: #endif michael@0: michael@0: /* Set when handling a signal for a thread associated with this runtime. */ michael@0: bool handlingSignal; michael@0: michael@0: JSInterruptCallback interruptCallback; michael@0: michael@0: #ifdef DEBUG michael@0: void assertCanLock(js::RuntimeLock which); michael@0: #else michael@0: void assertCanLock(js::RuntimeLock which) {} michael@0: #endif michael@0: michael@0: private: michael@0: /* michael@0: * Lock taken when triggering an interrupt from another thread. michael@0: * Protects all data that is touched in this process. michael@0: */ michael@0: #ifdef JS_THREADSAFE michael@0: PRLock *interruptLock; michael@0: PRThread *interruptLockOwner; michael@0: #else michael@0: bool interruptLockTaken; michael@0: #endif // JS_THREADSAFE michael@0: public: michael@0: michael@0: class AutoLockForInterrupt { michael@0: JSRuntime *rt; michael@0: public: michael@0: AutoLockForInterrupt(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: rt->assertCanLock(js::InterruptLock); michael@0: #ifdef JS_THREADSAFE michael@0: PR_Lock(rt->interruptLock); michael@0: rt->interruptLockOwner = PR_GetCurrentThread(); michael@0: #else michael@0: rt->interruptLockTaken = true; michael@0: #endif // JS_THREADSAFE michael@0: } michael@0: ~AutoLockForInterrupt() { michael@0: JS_ASSERT(rt->currentThreadOwnsInterruptLock()); michael@0: #ifdef JS_THREADSAFE michael@0: rt->interruptLockOwner = nullptr; michael@0: PR_Unlock(rt->interruptLock); michael@0: #else michael@0: rt->interruptLockTaken = false; michael@0: #endif // JS_THREADSAFE michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: bool currentThreadOwnsInterruptLock() { michael@0: #if defined(JS_THREADSAFE) michael@0: return interruptLockOwner == PR_GetCurrentThread(); michael@0: #else michael@0: return interruptLockTaken; michael@0: #endif michael@0: } michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: michael@0: private: michael@0: /* michael@0: * Lock taken when using per-runtime or per-zone data that could otherwise michael@0: * be accessed simultaneously by both the main thread and another thread michael@0: * with an ExclusiveContext. michael@0: * michael@0: * Locking this only occurs if there is actually a thread other than the michael@0: * main thread with an ExclusiveContext which could access such data. michael@0: */ michael@0: PRLock *exclusiveAccessLock; michael@0: mozilla::DebugOnly exclusiveAccessOwner; michael@0: mozilla::DebugOnly mainThreadHasExclusiveAccess; michael@0: michael@0: /* Number of non-main threads with an ExclusiveContext. */ michael@0: size_t numExclusiveThreads; michael@0: michael@0: friend class js::AutoLockForExclusiveAccess; michael@0: michael@0: public: michael@0: void setUsedByExclusiveThread(JS::Zone *zone); michael@0: void clearUsedByExclusiveThread(JS::Zone *zone); michael@0: michael@0: #endif // JS_THREADSAFE michael@0: michael@0: #ifdef DEBUG michael@0: bool currentThreadHasExclusiveAccess() { michael@0: #ifdef JS_THREADSAFE michael@0: return (!numExclusiveThreads && mainThreadHasExclusiveAccess) || michael@0: exclusiveAccessOwner == PR_GetCurrentThread(); michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: #endif // DEBUG michael@0: michael@0: bool exclusiveThreadsPresent() const { michael@0: #ifdef JS_THREADSAFE michael@0: return numExclusiveThreads > 0; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: /* Embedders can use this zone however they wish. */ michael@0: JS::Zone *systemZone; michael@0: michael@0: /* List of compartments and zones (protected by the GC lock). */ michael@0: js::ZoneVector zones; michael@0: michael@0: /* How many compartments there are across all zones. */ michael@0: size_t numCompartments; michael@0: michael@0: /* Locale-specific callbacks for string conversion. */ michael@0: JSLocaleCallbacks *localeCallbacks; michael@0: michael@0: /* Default locale for Internationalization API */ michael@0: char *defaultLocale; michael@0: michael@0: /* Default JSVersion. */ michael@0: JSVersion defaultVersion_; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: private: michael@0: /* See comment for JS_AbortIfWrongThread in jsapi.h. */ michael@0: void *ownerThread_; michael@0: friend bool js::CurrentThreadCanAccessRuntime(JSRuntime *rt); michael@0: public: michael@0: #endif michael@0: michael@0: /* Temporary arena pool used while compiling and decompiling. */ michael@0: static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; michael@0: js::LifoAlloc tempLifoAlloc; michael@0: michael@0: /* michael@0: * Free LIFO blocks are transferred to this allocator before being freed on michael@0: * the background GC thread. michael@0: */ michael@0: js::LifoAlloc freeLifoAlloc; michael@0: michael@0: private: michael@0: /* michael@0: * Both of these allocators are used for regular expression code which is shared at the michael@0: * thread-data level. michael@0: */ michael@0: JSC::ExecutableAllocator *execAlloc_; michael@0: WTF::BumpPointerAllocator *bumpAlloc_; michael@0: js::jit::JitRuntime *jitRuntime_; michael@0: michael@0: /* michael@0: * Self-hosting state cloned on demand into other compartments. Shared with the parent michael@0: * runtime if there is one. michael@0: */ michael@0: JSObject *selfHostingGlobal_; michael@0: michael@0: /* Space for interpreter frames. */ michael@0: js::InterpreterStack interpreterStack_; michael@0: michael@0: JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); michael@0: WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); michael@0: js::jit::JitRuntime *createJitRuntime(JSContext *cx); michael@0: michael@0: public: michael@0: JSC::ExecutableAllocator *getExecAlloc(JSContext *cx) { michael@0: return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx); michael@0: } michael@0: JSC::ExecutableAllocator &execAlloc() { michael@0: JS_ASSERT(execAlloc_); michael@0: return *execAlloc_; michael@0: } michael@0: JSC::ExecutableAllocator *maybeExecAlloc() { michael@0: return execAlloc_; michael@0: } michael@0: WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) { michael@0: return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx); michael@0: } michael@0: js::jit::JitRuntime *getJitRuntime(JSContext *cx) { michael@0: return jitRuntime_ ? jitRuntime_ : createJitRuntime(cx); michael@0: } michael@0: js::jit::JitRuntime *jitRuntime() const { michael@0: return jitRuntime_; michael@0: } michael@0: bool hasJitRuntime() const { michael@0: return !!jitRuntime_; michael@0: } michael@0: js::InterpreterStack &interpreterStack() { michael@0: return interpreterStack_; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // Self-hosting support michael@0: //------------------------------------------------------------------------- michael@0: michael@0: bool initSelfHosting(JSContext *cx); michael@0: void finishSelfHosting(); michael@0: void markSelfHostingGlobal(JSTracer *trc); michael@0: bool isSelfHostingGlobal(JSObject *global) { michael@0: return global == selfHostingGlobal_; michael@0: } michael@0: bool isSelfHostingCompartment(JSCompartment *comp); michael@0: bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle name, michael@0: js::Handle targetFun); michael@0: bool cloneSelfHostedValue(JSContext *cx, js::Handle name, michael@0: js::MutableHandleValue vp); michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // Locale information michael@0: //------------------------------------------------------------------------- michael@0: michael@0: /* michael@0: * Set the default locale for the ECMAScript Internationalization API michael@0: * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). michael@0: * Note that the Internationalization API encourages clients to michael@0: * specify their own locales. michael@0: * The locale string remains owned by the caller. michael@0: */ michael@0: bool setDefaultLocale(const char *locale); michael@0: michael@0: /* Reset the default locale to OS defaults. */ michael@0: void resetDefaultLocale(); michael@0: michael@0: /* Gets current default locale. String remains owned by context. */ michael@0: const char *getDefaultLocale(); michael@0: michael@0: JSVersion defaultVersion() { return defaultVersion_; } michael@0: void setDefaultVersion(JSVersion v) { defaultVersion_ = v; } michael@0: michael@0: /* Base address of the native stack for the current thread. */ michael@0: uintptr_t nativeStackBase; michael@0: michael@0: /* The native stack size limit that runtime should not exceed. */ michael@0: size_t nativeStackQuota[js::StackKindCount]; michael@0: michael@0: /* Context create/destroy callback. */ michael@0: JSContextCallback cxCallback; michael@0: void *cxCallbackData; michael@0: michael@0: /* Compartment destroy callback. */ michael@0: JSDestroyCompartmentCallback destroyCompartmentCallback; michael@0: michael@0: /* Zone destroy callback. */ michael@0: JSZoneCallback destroyZoneCallback; michael@0: michael@0: /* Zone sweep callback. */ michael@0: JSZoneCallback sweepZoneCallback; michael@0: michael@0: /* Call this to get the name of a compartment. */ michael@0: JSCompartmentNameCallback compartmentNameCallback; michael@0: michael@0: js::ActivityCallback activityCallback; michael@0: void *activityCallbackArg; michael@0: void triggerActivityCallback(bool active); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: /* The request depth for this thread. */ michael@0: unsigned requestDepth; michael@0: michael@0: # ifdef DEBUG michael@0: unsigned checkRequestDepth; michael@0: # endif michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: /* michael@0: * To help embedders enforce their invariants, we allow them to specify in michael@0: * advance which JSContext should be passed to JSAPI calls. If this is set michael@0: * to a non-null value, the assertSameCompartment machinery does double- michael@0: * duty (in debug builds) to verify that it matches the cx being used. michael@0: */ michael@0: JSContext *activeContext; michael@0: #endif michael@0: michael@0: /* Garbage collector state, used by jsgc.c. */ michael@0: michael@0: /* Garbase collector state has been sucessfully initialized. */ michael@0: bool gcInitialized; michael@0: michael@0: /* michael@0: * Set of all GC chunks with at least one allocated thing. The michael@0: * conservative GC uses it to quickly check if a possible GC thing points michael@0: * into an allocated chunk. michael@0: */ michael@0: js::GCChunkSet gcChunkSet; michael@0: michael@0: /* michael@0: * Doubly-linked lists of chunks from user and system compartments. The GC michael@0: * allocates its arenas from the corresponding list and when all arenas michael@0: * in the list head are taken, then the chunk is removed from the list. michael@0: * During the GC when all arenas in a chunk become free, that chunk is michael@0: * removed from the list and scheduled for release. michael@0: */ michael@0: js::gc::Chunk *gcSystemAvailableChunkListHead; michael@0: js::gc::Chunk *gcUserAvailableChunkListHead; michael@0: js::gc::ChunkPool gcChunkPool; michael@0: michael@0: js::RootedValueMap gcRootsHash; michael@0: michael@0: /* This is updated by both the main and GC helper threads. */ michael@0: mozilla::Atomic gcBytes; michael@0: michael@0: size_t gcMaxBytes; michael@0: size_t gcMaxMallocBytes; michael@0: michael@0: /* michael@0: * Number of the committed arenas in all GC chunks including empty chunks. michael@0: */ michael@0: mozilla::Atomic gcNumArenasFreeCommitted; michael@0: js::GCMarker gcMarker; michael@0: void *gcVerifyPreData; michael@0: void *gcVerifyPostData; michael@0: bool gcChunkAllocationSinceLastGC; michael@0: int64_t gcNextFullGCTime; michael@0: int64_t gcLastGCTime; michael@0: int64_t gcJitReleaseTime; michael@0: private: michael@0: JSGCMode gcMode_; michael@0: michael@0: public: michael@0: JSGCMode gcMode() const { return gcMode_; } michael@0: void setGCMode(JSGCMode mode) { michael@0: gcMode_ = mode; michael@0: gcMarker.setGCMode(mode); michael@0: } michael@0: michael@0: size_t gcAllocationThreshold; michael@0: bool gcHighFrequencyGC; michael@0: uint64_t gcHighFrequencyTimeThreshold; michael@0: uint64_t gcHighFrequencyLowLimitBytes; michael@0: uint64_t gcHighFrequencyHighLimitBytes; michael@0: double gcHighFrequencyHeapGrowthMax; michael@0: double gcHighFrequencyHeapGrowthMin; michael@0: double gcLowFrequencyHeapGrowth; michael@0: bool gcDynamicHeapGrowth; michael@0: bool gcDynamicMarkSlice; michael@0: uint64_t gcDecommitThreshold; michael@0: michael@0: /* During shutdown, the GC needs to clean up every possible object. */ michael@0: bool gcShouldCleanUpEverything; michael@0: michael@0: /* michael@0: * The gray bits can become invalid if UnmarkGray overflows the stack. A michael@0: * full GC will reset this bit, since it fills in all the gray bits. michael@0: */ michael@0: bool gcGrayBitsValid; michael@0: michael@0: /* michael@0: * These flags must be kept separate so that a thread requesting a michael@0: * compartment GC doesn't cancel another thread's concurrent request for a michael@0: * full GC. michael@0: */ michael@0: volatile uintptr_t gcIsNeeded; michael@0: michael@0: js::gcstats::Statistics gcStats; michael@0: michael@0: /* Incremented on every GC slice. */ michael@0: uint64_t gcNumber; michael@0: michael@0: /* The gcNumber at the time of the most recent GC's first slice. */ michael@0: uint64_t gcStartNumber; michael@0: michael@0: /* Whether the currently running GC can finish in multiple slices. */ michael@0: bool gcIsIncremental; michael@0: michael@0: /* Whether all compartments are being collected in first GC slice. */ michael@0: bool gcIsFull; michael@0: michael@0: /* The reason that an interrupt-triggered GC should be called. */ michael@0: JS::gcreason::Reason gcTriggerReason; michael@0: michael@0: /* michael@0: * If this is true, all marked objects must belong to a compartment being michael@0: * GCed. This is used to look for compartment bugs. michael@0: */ michael@0: bool gcStrictCompartmentChecking; michael@0: michael@0: #ifdef DEBUG michael@0: /* michael@0: * If this is 0, all cross-compartment proxies must be registered in the michael@0: * wrapper map. This checking must be disabled temporarily while creating michael@0: * new wrappers. When non-zero, this records the recursion depth of wrapper michael@0: * creation. michael@0: */ michael@0: uintptr_t gcDisableStrictProxyCheckingCount; michael@0: #else michael@0: uintptr_t unused1; michael@0: #endif michael@0: michael@0: /* michael@0: * The current incremental GC phase. This is also used internally in michael@0: * non-incremental GC. michael@0: */ michael@0: js::gc::State gcIncrementalState; michael@0: michael@0: /* Indicates that the last incremental slice exhausted the mark stack. */ michael@0: bool gcLastMarkSlice; michael@0: michael@0: /* Whether any sweeping will take place in the separate GC helper thread. */ michael@0: bool gcSweepOnBackgroundThread; michael@0: michael@0: /* Whether any black->gray edges were found during marking. */ michael@0: bool gcFoundBlackGrayEdges; michael@0: michael@0: /* List head of zones to be swept in the background. */ michael@0: JS::Zone *gcSweepingZones; michael@0: michael@0: /* Index of current zone group (for stats). */ michael@0: unsigned gcZoneGroupIndex; michael@0: michael@0: /* michael@0: * Incremental sweep state. michael@0: */ michael@0: JS::Zone *gcZoneGroups; michael@0: JS::Zone *gcCurrentZoneGroup; michael@0: int gcSweepPhase; michael@0: JS::Zone *gcSweepZone; michael@0: int gcSweepKindIndex; michael@0: bool gcAbortSweepAfterCurrentGroup; michael@0: michael@0: /* michael@0: * List head of arenas allocated during the sweep phase. michael@0: */ michael@0: js::gc::ArenaHeader *gcArenasAllocatedDuringSweep; michael@0: michael@0: #ifdef DEBUG michael@0: js::gc::MarkingValidator *gcMarkingValidator; michael@0: #endif michael@0: michael@0: /* michael@0: * Indicates that a GC slice has taken place in the middle of an animation michael@0: * frame, rather than at the beginning. In this case, the next slice will be michael@0: * delayed so that we don't get back-to-back slices. michael@0: */ michael@0: volatile uintptr_t gcInterFrameGC; michael@0: michael@0: /* Default budget for incremental GC slice. See SliceBudget in jsgc.h. */ michael@0: int64_t gcSliceBudget; michael@0: michael@0: /* michael@0: * We disable incremental GC if we encounter a js::Class with a trace hook michael@0: * that does not implement write barriers. michael@0: */ michael@0: bool gcIncrementalEnabled; michael@0: michael@0: /* michael@0: * GGC can be enabled from the command line while testing. michael@0: */ michael@0: unsigned gcGenerationalDisabled; michael@0: michael@0: /* michael@0: * This is true if we are in the middle of a brain transplant (e.g., michael@0: * JS_TransplantObject) or some other operation that can manipulate michael@0: * dead zones. michael@0: */ michael@0: bool gcManipulatingDeadZones; michael@0: michael@0: /* michael@0: * This field is incremented each time we mark an object inside a michael@0: * zone with no incoming cross-compartment pointers. Typically if michael@0: * this happens it signals that an incremental GC is marking too much michael@0: * stuff. At various times we check this counter and, if it has changed, we michael@0: * run an immediate, non-incremental GC to clean up the dead michael@0: * zones. This should happen very rarely. michael@0: */ michael@0: unsigned gcObjectsMarkedInDeadZones; michael@0: michael@0: bool gcPoke; michael@0: michael@0: volatile js::HeapState heapState; michael@0: michael@0: bool isHeapBusy() { return heapState != js::Idle; } michael@0: bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; } michael@0: bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; } michael@0: bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: js::Nursery gcNursery; michael@0: js::gc::StoreBuffer gcStoreBuffer; michael@0: #endif michael@0: michael@0: /* michael@0: * These options control the zealousness of the GC. The fundamental values michael@0: * are gcNextScheduled and gcDebugCompartmentGC. At every allocation, michael@0: * gcNextScheduled is decremented. When it reaches zero, we do either a michael@0: * full or a compartmental GC, based on gcDebugCompartmentGC. michael@0: * michael@0: * At this point, if gcZeal_ is one of the types that trigger periodic michael@0: * collection, then gcNextScheduled is reset to the value of michael@0: * gcZealFrequency. Otherwise, no additional GCs take place. michael@0: * michael@0: * You can control these values in several ways: michael@0: * - Pass the -Z flag to the shell (see the usage info for details) michael@0: * - Call gczeal() or schedulegc() from inside shell-executed JS code michael@0: * (see the help for details) michael@0: * michael@0: * If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and michael@0: * whenever a GC poke happens). This option is mainly useful to embedders. michael@0: * michael@0: * We use gcZeal_ == 4 to enable write barrier verification. See the comment michael@0: * in jsgc.cpp for more information about this. michael@0: * michael@0: * gcZeal_ values from 8 to 10 periodically run different types of michael@0: * incremental GC. michael@0: */ michael@0: #ifdef JS_GC_ZEAL michael@0: int gcZeal_; michael@0: int gcZealFrequency; michael@0: int gcNextScheduled; michael@0: bool gcDeterministicOnly; michael@0: int gcIncrementalLimit; michael@0: michael@0: js::Vector gcSelectedForMarking; michael@0: michael@0: int gcZeal() { return gcZeal_; } michael@0: michael@0: bool upcomingZealousGC() { michael@0: return gcNextScheduled == 1; michael@0: } michael@0: michael@0: bool needZealousGC() { michael@0: if (gcNextScheduled > 0 && --gcNextScheduled == 0) { michael@0: if (gcZeal() == js::gc::ZealAllocValue || michael@0: gcZeal() == js::gc::ZealGenerationalGCValue || michael@0: (gcZeal() >= js::gc::ZealIncrementalRootsThenFinish && michael@0: gcZeal() <= js::gc::ZealIncrementalMultipleSlices)) michael@0: { michael@0: gcNextScheduled = gcZealFrequency; michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: #else michael@0: int gcZeal() { return 0; } michael@0: bool upcomingZealousGC() { return false; } michael@0: bool needZealousGC() { return false; } michael@0: #endif michael@0: michael@0: bool gcValidate; michael@0: bool gcFullCompartmentChecks; michael@0: michael@0: JSGCCallback gcCallback; michael@0: JS::GCSliceCallback gcSliceCallback; michael@0: JSFinalizeCallback gcFinalizeCallback; michael@0: michael@0: void *gcCallbackData; michael@0: michael@0: private: michael@0: /* michael@0: * Malloc counter to measure memory pressure for GC scheduling. It runs michael@0: * from gcMaxMallocBytes down to zero. michael@0: */ michael@0: mozilla::Atomic gcMallocBytes; michael@0: michael@0: /* michael@0: * Whether a GC has been triggered as a result of gcMallocBytes falling michael@0: * below zero. michael@0: */ michael@0: mozilla::Atomic gcMallocGCTriggered; michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js::jit::SimulatorRuntime *simulatorRuntime_; michael@0: #endif michael@0: michael@0: public: michael@0: void setNeedsBarrier(bool needs) { michael@0: needsBarrier_ = needs; michael@0: } michael@0: michael@0: struct ExtraTracer { michael@0: JSTraceDataOp op; michael@0: void *data; michael@0: michael@0: ExtraTracer() michael@0: : op(nullptr), data(nullptr) michael@0: {} michael@0: ExtraTracer(JSTraceDataOp op, void *data) michael@0: : op(op), data(data) michael@0: {} michael@0: }; michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js::jit::SimulatorRuntime *simulatorRuntime() const; michael@0: void setSimulatorRuntime(js::jit::SimulatorRuntime *srt); michael@0: #endif michael@0: michael@0: /* michael@0: * The trace operations to trace embedding-specific GC roots. One is for michael@0: * tracing through black roots and the other is for tracing through gray michael@0: * roots. The black/gray distinction is only relevant to the cycle michael@0: * collector. michael@0: */ michael@0: typedef js::Vector ExtraTracerVector; michael@0: ExtraTracerVector gcBlackRootTracers; michael@0: ExtraTracer gcGrayRootTracer; michael@0: michael@0: /* michael@0: * The GC can only safely decommit memory when the page size of the michael@0: * running process matches the compiled arena size. michael@0: */ michael@0: size_t gcSystemPageSize; michael@0: michael@0: /* The OS allocation granularity may not match the page size. */ michael@0: size_t gcSystemAllocGranularity; michael@0: michael@0: /* Strong references on scripts held for PCCount profiling API. */ michael@0: js::ScriptAndCountsVector *scriptAndCountsVector; michael@0: michael@0: /* Well-known numbers held for use by this runtime's contexts. */ michael@0: const js::Value NaNValue; michael@0: const js::Value negativeInfinityValue; michael@0: const js::Value positiveInfinityValue; michael@0: michael@0: js::PropertyName *emptyString; michael@0: michael@0: /* List of active contexts sharing this runtime. */ michael@0: mozilla::LinkedList contextList; michael@0: michael@0: bool hasContexts() const { michael@0: return !contextList.isEmpty(); michael@0: } michael@0: michael@0: mozilla::ScopedDeletePtr sourceHook; michael@0: michael@0: /* Per runtime debug hooks -- see js/OldDebugAPI.h. */ michael@0: JSDebugHooks debugHooks; michael@0: michael@0: /* If true, new compartments are initially in debug mode. */ michael@0: bool debugMode; michael@0: michael@0: /* SPS profiling metadata */ michael@0: js::SPSProfiler spsProfiler; michael@0: michael@0: /* If true, new scripts must be created with PC counter information. */ michael@0: bool profilingScripts; michael@0: michael@0: /* Always preserve JIT code during GCs, for testing. */ michael@0: bool alwaysPreserveCode; michael@0: michael@0: /* Had an out-of-memory error which did not populate an exception. */ michael@0: bool hadOutOfMemory; michael@0: michael@0: /* A context has been created on this runtime. */ michael@0: bool haveCreatedContext; michael@0: michael@0: /* Linked list of all Debugger objects in the runtime. */ michael@0: mozilla::LinkedList debuggerList; michael@0: michael@0: /* michael@0: * Head of circular list of all enabled Debuggers that have michael@0: * onNewGlobalObject handler methods established. michael@0: */ michael@0: JSCList onNewGlobalObjectWatchers; michael@0: michael@0: /* Client opaque pointers */ michael@0: void *data; michael@0: michael@0: private: michael@0: /* Synchronize GC heap access between main thread and GCHelperThread. */ michael@0: PRLock *gcLock; michael@0: mozilla::DebugOnly gcLockOwner; michael@0: michael@0: friend class js::GCHelperThread; michael@0: public: michael@0: michael@0: void lockGC() { michael@0: #ifdef JS_THREADSAFE michael@0: assertCanLock(js::GCLock); michael@0: PR_Lock(gcLock); michael@0: JS_ASSERT(!gcLockOwner); michael@0: #ifdef DEBUG michael@0: gcLockOwner = PR_GetCurrentThread(); michael@0: #endif michael@0: #endif michael@0: } michael@0: michael@0: void unlockGC() { michael@0: #ifdef JS_THREADSAFE michael@0: JS_ASSERT(gcLockOwner == PR_GetCurrentThread()); michael@0: gcLockOwner = nullptr; michael@0: PR_Unlock(gcLock); michael@0: #endif michael@0: } michael@0: michael@0: js::GCHelperThread gcHelperThread; michael@0: michael@0: #if defined(XP_MACOSX) && defined(JS_ION) michael@0: js::AsmJSMachExceptionHandler asmJSMachExceptionHandler; michael@0: #endif michael@0: michael@0: // Whether asm.js signal handlers have been installed and can be used for michael@0: // performing interrupt checks in loops. michael@0: private: michael@0: bool signalHandlersInstalled_; michael@0: public: michael@0: bool signalHandlersInstalled() const { michael@0: return signalHandlersInstalled_; michael@0: } michael@0: michael@0: private: michael@0: js::FreeOp defaultFreeOp_; michael@0: michael@0: public: michael@0: js::FreeOp *defaultFreeOp() { michael@0: return &defaultFreeOp_; michael@0: } michael@0: michael@0: uint32_t debuggerMutations; michael@0: michael@0: const JSSecurityCallbacks *securityCallbacks; michael@0: const js::DOMCallbacks *DOMcallbacks; michael@0: JSDestroyPrincipalsOp destroyPrincipals; michael@0: michael@0: /* Structured data callbacks are runtime-wide. */ michael@0: const JSStructuredCloneCallbacks *structuredCloneCallbacks; michael@0: michael@0: /* Call this to accumulate telemetry data. */ michael@0: JSAccumulateTelemetryDataCallback telemetryCallback; michael@0: michael@0: /* AsmJSCache callbacks are runtime-wide. */ michael@0: JS::AsmJSCacheOps asmJSCacheOps; michael@0: michael@0: /* michael@0: * The propertyRemovals counter is incremented for every JSObject::clear, michael@0: * and for each JSObject::remove method call that frees a slot in the given michael@0: * object. See js_NativeGet and js_NativeSet in jsobj.cpp. michael@0: */ michael@0: uint32_t propertyRemovals; michael@0: michael@0: #if !EXPOSE_INTL_API michael@0: /* Number localization, used by jsnum.cpp. */ michael@0: const char *thousandsSeparator; michael@0: const char *decimalSeparator; michael@0: const char *numGrouping; michael@0: #endif michael@0: michael@0: private: michael@0: js::MathCache *mathCache_; michael@0: js::MathCache *createMathCache(JSContext *cx); michael@0: public: michael@0: js::MathCache *getMathCache(JSContext *cx) { michael@0: return mathCache_ ? mathCache_ : createMathCache(cx); michael@0: } michael@0: js::MathCache *maybeGetMathCache() { michael@0: return mathCache_; michael@0: } michael@0: michael@0: js::GSNCache gsnCache; michael@0: js::ScopeCoordinateNameCache scopeCoordinateNameCache; michael@0: js::NewObjectCache newObjectCache; michael@0: js::NativeIterCache nativeIterCache; michael@0: js::SourceDataCache sourceDataCache; michael@0: js::EvalCache evalCache; michael@0: js::LazyScriptCache lazyScriptCache; michael@0: michael@0: js::DateTimeInfo dateTimeInfo; michael@0: michael@0: js::ConservativeGCData conservativeGC; michael@0: michael@0: // Pool of maps used during parse/emit. This may be modified by threads michael@0: // with an ExclusiveContext and requires a lock. Active compilations michael@0: // prevent the pool from being purged during GCs. michael@0: private: michael@0: js::frontend::ParseMapPool parseMapPool_; michael@0: unsigned activeCompilations_; michael@0: public: michael@0: js::frontend::ParseMapPool &parseMapPool() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: return parseMapPool_; michael@0: } michael@0: bool hasActiveCompilations() { michael@0: return activeCompilations_ != 0; michael@0: } michael@0: void addActiveCompilation() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: activeCompilations_++; michael@0: } michael@0: void removeActiveCompilation() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: activeCompilations_--; michael@0: } michael@0: michael@0: // Count of AutoKeepAtoms instances on the main thread's stack. When any michael@0: // instances exist, atoms in the runtime will not be collected. Threads michael@0: // with an ExclusiveContext do not increment this value, but the presence michael@0: // of any such threads also inhibits collection of atoms. We don't scan the michael@0: // stacks of exclusive threads, so we need to avoid collecting their michael@0: // objects in another way. The only GC thing pointers they have are to michael@0: // their exclusive compartment (which is not collected) or to the atoms michael@0: // compartment. Therefore, we avoid collecting the atoms compartment when michael@0: // exclusive threads are running. michael@0: private: michael@0: unsigned keepAtoms_; michael@0: friend class js::AutoKeepAtoms; michael@0: public: michael@0: bool keepAtoms() { michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(this)); michael@0: return keepAtoms_ != 0 || exclusiveThreadsPresent(); michael@0: } michael@0: michael@0: private: michael@0: const JSPrincipals *trustedPrincipals_; michael@0: public: michael@0: void setTrustedPrincipals(const JSPrincipals *p) { trustedPrincipals_ = p; } michael@0: const JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; } michael@0: michael@0: private: michael@0: bool beingDestroyed_; michael@0: public: michael@0: bool isBeingDestroyed() const { michael@0: return beingDestroyed_; michael@0: } michael@0: michael@0: private: michael@0: // Set of all atoms other than those in permanentAtoms and staticStrings. michael@0: // This may be modified by threads with an ExclusiveContext and requires michael@0: // a lock. michael@0: js::AtomSet *atoms_; michael@0: michael@0: // Compartment and associated zone containing all atoms in the runtime, michael@0: // as well as runtime wide IonCode stubs. The contents of this compartment michael@0: // may be modified by threads with an ExclusiveContext and requires a lock. michael@0: JSCompartment *atomsCompartment_; michael@0: michael@0: public: michael@0: bool initializeAtoms(JSContext *cx); michael@0: void finishAtoms(); michael@0: michael@0: void sweepAtoms(); michael@0: michael@0: js::AtomSet &atoms() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: return *atoms_; michael@0: } michael@0: JSCompartment *atomsCompartment() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: return atomsCompartment_; michael@0: } michael@0: michael@0: bool isAtomsCompartment(JSCompartment *comp) { michael@0: return comp == atomsCompartment_; michael@0: } michael@0: michael@0: // The atoms compartment is the only one in its zone. michael@0: inline bool isAtomsZone(JS::Zone *zone); michael@0: michael@0: bool activeGCInAtomsZone(); michael@0: michael@0: // Permanent atoms are fixed during initialization of the runtime and are michael@0: // not modified or collected until the runtime is destroyed. These may be michael@0: // shared with another, longer living runtime through |parentRuntime| and michael@0: // can be freely accessed with no locking necessary. michael@0: michael@0: // Permanent atoms pre-allocated for general use. michael@0: js::StaticStrings *staticStrings; michael@0: michael@0: // Cached pointers to various permanent property names. michael@0: JSAtomState *commonNames; michael@0: michael@0: // All permanent atoms in the runtime, other than those in staticStrings. michael@0: js::AtomSet *permanentAtoms; michael@0: michael@0: bool transformToPermanentAtoms(); michael@0: michael@0: const JSWrapObjectCallbacks *wrapObjectCallbacks; michael@0: js::PreserveWrapperCallback preserveWrapperCallback; michael@0: michael@0: // Table of bytecode and other data that may be shared across scripts michael@0: // within the runtime. This may be modified by threads with an michael@0: // ExclusiveContext and requires a lock. michael@0: private: michael@0: js::ScriptDataTable scriptDataTable_; michael@0: public: michael@0: js::ScriptDataTable &scriptDataTable() { michael@0: JS_ASSERT(currentThreadHasExclusiveAccess()); michael@0: return scriptDataTable_; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: size_t noGCOrAllocationCheck; michael@0: #endif michael@0: michael@0: bool jitSupportsFloatingPoint; michael@0: michael@0: // Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1) michael@0: // has been noticed by Ion/Baseline. michael@0: void resetJitStackLimit(); michael@0: michael@0: // Cache for jit::GetPcScript(). michael@0: js::jit::PcScriptCache *ionPcScriptCache; michael@0: michael@0: js::ThreadPool threadPool; michael@0: michael@0: js::DefaultJSContextCallback defaultJSContextCallback; michael@0: michael@0: js::CTypesActivityCallback ctypesActivityCallback; michael@0: michael@0: // Non-zero if this is a ForkJoin warmup execution. See michael@0: // js::ForkJoin() for more information. michael@0: uint32_t forkJoinWarmup; michael@0: michael@0: private: michael@0: // In certain cases, we want to optimize certain opcodes to typed instructions, michael@0: // to avoid carrying an extra register to feed into an unbox. Unfortunately, michael@0: // that's not always possible. For example, a GetPropertyCacheT could return a michael@0: // typed double, but if it takes its out-of-line path, it could return an michael@0: // object, and trigger invalidation. The invalidation bailout will consider the michael@0: // return value to be a double, and create a garbage Value. michael@0: // michael@0: // To allow the GetPropertyCacheT optimization, we allow the ability for michael@0: // GetPropertyCache to override the return value at the top of the stack - the michael@0: // value that will be temporarily corrupt. This special override value is set michael@0: // only in callVM() targets that are about to return *and* have invalidated michael@0: // their callee. michael@0: js::Value ionReturnOverride_; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: static mozilla::Atomic liveRuntimesCount; michael@0: #else michael@0: static size_t liveRuntimesCount; michael@0: #endif michael@0: michael@0: public: michael@0: static bool hasLiveRuntimes() { michael@0: return liveRuntimesCount > 0; michael@0: } michael@0: michael@0: bool hasIonReturnOverride() const { michael@0: return !ionReturnOverride_.isMagic(); michael@0: } michael@0: js::Value takeIonReturnOverride() { michael@0: js::Value v = ionReturnOverride_; michael@0: ionReturnOverride_ = js::MagicValue(JS_ARG_POISON); michael@0: return v; michael@0: } michael@0: void setIonReturnOverride(const js::Value &v) { michael@0: JS_ASSERT(!hasIonReturnOverride()); michael@0: ionReturnOverride_ = v; michael@0: } michael@0: michael@0: JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads); michael@0: ~JSRuntime(); michael@0: michael@0: bool init(uint32_t maxbytes); michael@0: michael@0: JSRuntime *thisFromCtor() { return this; } michael@0: michael@0: void setGCMaxMallocBytes(size_t value); michael@0: michael@0: void resetGCMallocBytes() { michael@0: gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); michael@0: gcMallocGCTriggered = false; michael@0: } michael@0: michael@0: /* michael@0: * Call this after allocating memory held by GC things, to update memory michael@0: * pressure counters or report the OOM error if necessary. If oomError and michael@0: * cx is not null the function also reports OOM error. michael@0: * michael@0: * The function must be called outside the GC lock and in case of OOM error michael@0: * the caller must ensure that no deadlock possible during OOM reporting. michael@0: */ michael@0: void updateMallocCounter(size_t nbytes); michael@0: void updateMallocCounter(JS::Zone *zone, size_t nbytes); michael@0: michael@0: void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); } michael@0: michael@0: bool isTooMuchMalloc() const { michael@0: return gcMallocBytes <= 0; michael@0: } michael@0: michael@0: /* michael@0: * The function must be called outside the GC lock. michael@0: */ michael@0: JS_FRIEND_API(void) onTooMuchMalloc(); michael@0: michael@0: /* michael@0: * This should be called after system malloc/realloc returns nullptr to try michael@0: * to recove some memory or to report an error. Failures in malloc and michael@0: * calloc are signaled by p == null and p == reinterpret_cast(1). michael@0: * Other values of p mean a realloc failure. michael@0: * michael@0: * The function must be called outside the GC lock. michael@0: */ michael@0: JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes); michael@0: JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); michael@0: michael@0: // Ways in which the interrupt callback on the runtime can be triggered, michael@0: // varying based on which thread is triggering the callback. michael@0: enum InterruptMode { michael@0: RequestInterruptMainThread, michael@0: RequestInterruptAnyThread, michael@0: RequestInterruptAnyThreadDontStopIon, michael@0: RequestInterruptAnyThreadForkJoin michael@0: }; michael@0: michael@0: void requestInterrupt(InterruptMode mode); michael@0: michael@0: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime); michael@0: michael@0: private: michael@0: JS::RuntimeOptions options_; michael@0: michael@0: JSUseHelperThreads useHelperThreads_; michael@0: michael@0: // Settings for how helper threads can be used. michael@0: bool parallelIonCompilationEnabled_; michael@0: bool parallelParsingEnabled_; michael@0: michael@0: // True iff this is a DOM Worker runtime. michael@0: bool isWorkerRuntime_; michael@0: michael@0: public: michael@0: michael@0: // This controls whether the JSRuntime is allowed to create any helper michael@0: // threads at all. This means both specific threads (background GC thread) michael@0: // and the general JS worker thread pool. michael@0: bool useHelperThreads() const { michael@0: #ifdef JS_THREADSAFE michael@0: return useHelperThreads_ == JS_USE_HELPER_THREADS; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: // Note: these values may be toggled dynamically (in response to about:config michael@0: // prefs changing). michael@0: void setParallelIonCompilationEnabled(bool value) { michael@0: parallelIonCompilationEnabled_ = value; michael@0: } michael@0: bool canUseParallelIonCompilation() const { michael@0: return useHelperThreads() && michael@0: parallelIonCompilationEnabled_; michael@0: } michael@0: void setParallelParsingEnabled(bool value) { michael@0: parallelParsingEnabled_ = value; michael@0: } michael@0: bool canUseParallelParsing() const { michael@0: return useHelperThreads() && michael@0: parallelParsingEnabled_; michael@0: } michael@0: michael@0: void setIsWorkerRuntime() { michael@0: isWorkerRuntime_ = true; michael@0: } michael@0: bool isWorkerRuntime() const { michael@0: return isWorkerRuntime_; michael@0: } michael@0: michael@0: const JS::RuntimeOptions &options() const { michael@0: return options_; michael@0: } michael@0: JS::RuntimeOptions &options() { michael@0: return options_; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: public: michael@0: js::AutoEnterPolicy *enteredPolicy; michael@0: #endif michael@0: michael@0: /* See comment for JS::SetLargeAllocationFailureCallback in jsapi.h. */ michael@0: JS::LargeAllocationFailureCallback largeAllocationFailureCallback; michael@0: /* See comment for JS::SetOutOfMemoryCallback in jsapi.h. */ michael@0: JS::OutOfMemoryCallback oomCallback; michael@0: michael@0: /* michael@0: * These variations of malloc/calloc/realloc will call the michael@0: * large-allocation-failure callback on OOM and retry the allocation. michael@0: */ michael@0: michael@0: static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024; michael@0: michael@0: void *callocCanGC(size_t bytes) { michael@0: void *p = calloc_(bytes); michael@0: if (MOZ_LIKELY(!!p)) michael@0: return p; michael@0: if (!largeAllocationFailureCallback || bytes < LARGE_ALLOCATION) michael@0: return nullptr; michael@0: largeAllocationFailureCallback(); michael@0: return onOutOfMemory(reinterpret_cast(1), bytes); michael@0: } michael@0: michael@0: void *reallocCanGC(void *p, size_t bytes) { michael@0: void *p2 = realloc_(p, bytes); michael@0: if (MOZ_LIKELY(!!p2)) michael@0: return p2; michael@0: if (!largeAllocationFailureCallback || bytes < LARGE_ALLOCATION) michael@0: return nullptr; michael@0: largeAllocationFailureCallback(); michael@0: return onOutOfMemory(p, bytes); michael@0: } michael@0: }; michael@0: michael@0: namespace js { michael@0: michael@0: // When entering JIT code, the calling JSContext* is stored into the thread's michael@0: // PerThreadData. This function retrieves the JSContext with the pre-condition michael@0: // that the caller is JIT code or C++ called directly from JIT code. This michael@0: // function should not be called from arbitrary locations since the JSContext michael@0: // may be the wrong one. michael@0: static inline JSContext * michael@0: GetJSContextFromJitCode() michael@0: { michael@0: JSContext *cx = TlsPerThreadData.get()->jitJSContext; michael@0: JS_ASSERT(cx); michael@0: return cx; michael@0: } michael@0: michael@0: /* michael@0: * Flags accompany script version data so that a) dynamically created scripts michael@0: * can inherit their caller's compile-time properties and b) scripts can be michael@0: * appropriately compared in the eval cache across global option changes. An michael@0: * example of the latter is enabling the top-level-anonymous-function-is-error michael@0: * option: subsequent evals of the same, previously-valid script text may have michael@0: * become invalid. michael@0: */ michael@0: namespace VersionFlags { michael@0: static const unsigned MASK = 0x0FFF; /* see JSVersion in jspubtd.h */ michael@0: } /* namespace VersionFlags */ michael@0: michael@0: static inline JSVersion michael@0: VersionNumber(JSVersion version) michael@0: { michael@0: return JSVersion(uint32_t(version) & VersionFlags::MASK); michael@0: } michael@0: michael@0: static inline JSVersion michael@0: VersionExtractFlags(JSVersion version) michael@0: { michael@0: return JSVersion(uint32_t(version) & ~VersionFlags::MASK); michael@0: } michael@0: michael@0: static inline void michael@0: VersionCopyFlags(JSVersion *version, JSVersion from) michael@0: { michael@0: *version = JSVersion(VersionNumber(*version) | VersionExtractFlags(from)); michael@0: } michael@0: michael@0: static inline bool michael@0: VersionHasFlags(JSVersion version) michael@0: { michael@0: return !!VersionExtractFlags(version); michael@0: } michael@0: michael@0: static inline bool michael@0: VersionIsKnown(JSVersion version) michael@0: { michael@0: return VersionNumber(version) != JSVERSION_UNKNOWN; michael@0: } michael@0: michael@0: inline void michael@0: FreeOp::free_(void *p) michael@0: { michael@0: if (shouldFreeLater()) { michael@0: runtime()->gcHelperThread.freeLater(p); michael@0: return; michael@0: } michael@0: js_free(p); michael@0: } michael@0: michael@0: class AutoLockGC michael@0: { michael@0: public: michael@0: explicit AutoLockGC(JSRuntime *rt = nullptr michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : runtime(rt) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: // Avoid MSVC warning C4390 for non-threadsafe builds. michael@0: if (rt) michael@0: rt->lockGC(); michael@0: } michael@0: michael@0: ~AutoLockGC() michael@0: { michael@0: if (runtime) michael@0: runtime->unlockGC(); michael@0: } michael@0: michael@0: bool locked() const { michael@0: return !!runtime; michael@0: } michael@0: michael@0: void lock(JSRuntime *rt) { michael@0: JS_ASSERT(rt); michael@0: JS_ASSERT(!runtime); michael@0: runtime = rt; michael@0: rt->lockGC(); michael@0: } michael@0: michael@0: private: michael@0: JSRuntime *runtime; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoUnlockGC michael@0: { michael@0: private: michael@0: JSRuntime *rt; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: michael@0: public: michael@0: explicit AutoUnlockGC(JSRuntime *rt michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : rt(rt) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: rt->unlockGC(); michael@0: } michael@0: ~AutoUnlockGC() { rt->lockGC(); } michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS AutoKeepAtoms michael@0: { michael@0: PerThreadData *pt; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: michael@0: public: michael@0: explicit AutoKeepAtoms(PerThreadData *pt michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : pt(pt) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: if (JSRuntime *rt = pt->runtimeIfOnOwnerThread()) { michael@0: rt->keepAtoms_++; michael@0: } else { michael@0: // This should be a thread with an exclusive context, which will michael@0: // always inhibit collection of atoms. michael@0: JS_ASSERT(pt->exclusiveThreadsPresent()); michael@0: } michael@0: } michael@0: ~AutoKeepAtoms() { michael@0: if (JSRuntime *rt = pt->runtimeIfOnOwnerThread()) { michael@0: JS_ASSERT(rt->keepAtoms_); michael@0: rt->keepAtoms_--; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: inline void michael@0: PerThreadData::setJitStackLimit(uintptr_t limit) michael@0: { michael@0: JS_ASSERT(runtime_->currentThreadOwnsInterruptLock()); michael@0: jitStackLimit = limit; michael@0: } michael@0: michael@0: inline JSRuntime * michael@0: PerThreadData::runtimeFromMainThread() michael@0: { michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); michael@0: return runtime_; michael@0: } michael@0: michael@0: inline JSRuntime * michael@0: PerThreadData::runtimeIfOnOwnerThread() michael@0: { michael@0: return CurrentThreadCanAccessRuntime(runtime_) ? runtime_ : nullptr; michael@0: } michael@0: michael@0: inline bool michael@0: PerThreadData::exclusiveThreadsPresent() michael@0: { michael@0: return runtime_->exclusiveThreadsPresent(); michael@0: } michael@0: michael@0: inline void michael@0: PerThreadData::addActiveCompilation() michael@0: { michael@0: activeCompilations++; michael@0: runtime_->addActiveCompilation(); michael@0: } michael@0: michael@0: inline void michael@0: PerThreadData::removeActiveCompilation() michael@0: { michael@0: JS_ASSERT(activeCompilations); michael@0: activeCompilations--; michael@0: runtime_->removeActiveCompilation(); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(Value *vec, size_t len) michael@0: { michael@0: mozilla::PodZero(vec, len); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(Value *beg, Value *end) michael@0: { michael@0: mozilla::PodZero(beg, end - beg); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(jsid *beg, jsid *end) michael@0: { michael@0: for (jsid *id = beg; id != end; ++id) michael@0: *id = INT_TO_JSID(0); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(jsid *vec, size_t len) michael@0: { michael@0: MakeRangeGCSafe(vec, vec + len); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(Shape **beg, Shape **end) michael@0: { michael@0: mozilla::PodZero(beg, end - beg); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: MakeRangeGCSafe(Shape **vec, size_t len) michael@0: { michael@0: mozilla::PodZero(vec, len); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: SetValueRangeToUndefined(Value *beg, Value *end) michael@0: { michael@0: for (Value *v = beg; v != end; ++v) michael@0: v->setUndefined(); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: SetValueRangeToUndefined(Value *vec, size_t len) michael@0: { michael@0: SetValueRangeToUndefined(vec, vec + len); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: SetValueRangeToNull(Value *beg, Value *end) michael@0: { michael@0: for (Value *v = beg; v != end; ++v) michael@0: v->setNull(); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void michael@0: SetValueRangeToNull(Value *vec, size_t len) michael@0: { michael@0: SetValueRangeToNull(vec, vec + len); michael@0: } michael@0: michael@0: /* michael@0: * Allocation policy that uses JSRuntime::malloc_ and friends, so that michael@0: * memory pressure is properly accounted for. This is suitable for michael@0: * long-lived objects owned by the JSRuntime. michael@0: * michael@0: * Since it doesn't hold a JSContext (those may not live long enough), it michael@0: * can't report out-of-memory conditions itself; the caller must check for michael@0: * OOM and take the appropriate action. michael@0: * michael@0: * FIXME bug 647103 - replace these *AllocPolicy names. michael@0: */ michael@0: class RuntimeAllocPolicy michael@0: { michael@0: JSRuntime *const runtime; michael@0: michael@0: public: michael@0: RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {} michael@0: void *malloc_(size_t bytes) { return runtime->malloc_(bytes); } michael@0: void *calloc_(size_t bytes) { return runtime->calloc_(bytes); } michael@0: void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); } michael@0: void free_(void *p) { js_free(p); } michael@0: void reportAllocOverflow() const {} michael@0: }; michael@0: michael@0: extern const JSSecurityCallbacks NullSecurityCallbacks; 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 /* vm_Runtime_h */