| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* JS execution context. */ |
| |
| #ifndef jscntxt_h |
| #define jscntxt_h |
| |
| #include "mozilla/LinkedList.h" |
| #include "mozilla/PodOperations.h" |
| |
| #include <string.h> |
| #include <setjmp.h> |
| |
| #include "jsapi.h" |
| #include "jsfriendapi.h" |
| #include "jsprvtd.h" |
| #include "jsatom.h" |
| #include "jsclist.h" |
| #include "jsgc.h" |
| |
| #include "assembler/jit/ExecutableAllocator.h" |
| #include "ds/FixedSizeHash.h" |
| #include "ds/LifoAlloc.h" |
| #include "frontend/ParseMaps.h" |
| #include "gc/Nursery.h" |
| #include "gc/Statistics.h" |
| #include "gc/StoreBuffer.h" |
| #include "js/HashTable.h" |
| #include "js/Vector.h" |
| #include "vm/DateTime.h" |
| #include "vm/SPSProfiler.h" |
| #include "vm/Stack.h" |
| #include "vm/ThreadPool.h" |
| |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ |
| #pragma warning(push) |
| #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */ |
| #endif |
| |
| struct DtoaState; |
| |
| extern void |
| js_ReportOutOfMemory(JSContext *cx); |
| |
| extern void |
| js_ReportAllocationOverflow(JSContext *cx); |
| |
| namespace js { |
| |
| typedef Rooted<JSLinearString*> RootedLinearString; |
| |
| struct CallsiteCloneKey { |
| /* The original function that we are cloning. */ |
| JSFunction *original; |
| |
| /* The script of the call. */ |
| JSScript *script; |
| |
| /* The offset of the call. */ |
| uint32_t offset; |
| |
| CallsiteCloneKey(JSFunction *f, JSScript *s, uint32_t o) : original(f), script(s), offset(o) {} |
| |
| typedef CallsiteCloneKey Lookup; |
| |
| static inline uint32_t hash(CallsiteCloneKey key) { |
| return uint32_t(size_t(key.script->code + key.offset) ^ size_t(key.original)); |
| } |
| |
| static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) { |
| return a.script == b.script && a.offset == b.offset && a.original == b.original; |
| } |
| }; |
| |
| typedef HashMap<CallsiteCloneKey, |
| ReadBarriered<JSFunction>, |
| CallsiteCloneKey, |
| SystemAllocPolicy> CallsiteCloneTable; |
| |
| JSFunction *CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, |
| HandleScript script, jsbytecode *pc); |
| |
| typedef HashSet<JSObject *> ObjectSet; |
| typedef HashSet<Shape *> ShapeSet; |
| |
| /* Detects cycles when traversing an object graph. */ |
| class AutoCycleDetector |
| { |
| JSContext *cx; |
| RootedObject obj; |
| bool cyclic; |
| uint32_t hashsetGenerationAtInit; |
| ObjectSet::AddPtr hashsetAddPointer; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| |
| public: |
| AutoCycleDetector(JSContext *cx, HandleObject objArg |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : cx(cx), obj(cx, objArg), cyclic(true) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| ~AutoCycleDetector(); |
| |
| bool init(); |
| |
| bool foundCycle() { return cyclic; } |
| }; |
| |
| /* Updates references in the cycle detection set if the GC moves them. */ |
| extern void |
| TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set); |
| |
| class MathCache; |
| |
| namespace jit { |
| class IonActivation; |
| class IonRuntime; |
| struct PcScriptCache; |
| } |
| |
| class AsmJSActivation; |
| class InterpreterFrames; |
| class WeakMapBase; |
| class WorkerThreadState; |
| |
| /* |
| * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a |
| * given pc in a script. We use the script->code pointer to tag the cache, |
| * instead of the script address itself, so that source notes are always found |
| * by offset from the bytecode with which they were generated. |
| */ |
| struct GSNCache { |
| typedef HashMap<jsbytecode *, |
| jssrcnote *, |
| PointerHasher<jsbytecode *, 0>, |
| SystemAllocPolicy> Map; |
| |
| jsbytecode *code; |
| Map map; |
| |
| GSNCache() : code(NULL) { } |
| |
| void purge(); |
| }; |
| |
| typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector; |
| |
| struct ConservativeGCData |
| { |
| /* |
| * The GC scans conservatively between ThreadData::nativeStackBase and |
| * nativeStackTop unless the latter is NULL. |
| */ |
| uintptr_t *nativeStackTop; |
| |
| #if defined(JSGC_ROOT_ANALYSIS) && (JS_STACK_GROWTH_DIRECTION < 0) |
| /* |
| * Record old contents of the native stack from the last time there was a |
| * scan, to reduce the overhead involved in repeatedly rescanning the |
| * native stack during root analysis. oldStackData stores words in reverse |
| * order starting at oldStackEnd. |
| */ |
| uintptr_t *oldStackMin, *oldStackEnd; |
| uintptr_t *oldStackData; |
| size_t oldStackCapacity; // in sizeof(uintptr_t) |
| #endif |
| |
| union { |
| jmp_buf jmpbuf; |
| uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; |
| } registerSnapshot; |
| |
| ConservativeGCData() { |
| mozilla::PodZero(this); |
| } |
| |
| ~ConservativeGCData() { |
| #ifdef JS_THREADSAFE |
| /* |
| * The conservative GC scanner should be disabled when the thread leaves |
| * the last request. |
| */ |
| JS_ASSERT(!hasStackToScan()); |
| #endif |
| } |
| |
| JS_NEVER_INLINE void recordStackTop(); |
| |
| #ifdef JS_THREADSAFE |
| void updateForRequestEnd() { |
| nativeStackTop = NULL; |
| } |
| #endif |
| |
| bool hasStackToScan() const { |
| return !!nativeStackTop; |
| } |
| }; |
| |
| class SourceDataCache |
| { |
| typedef HashMap<ScriptSource *, |
| JSStableString *, |
| DefaultHasher<ScriptSource *>, |
| SystemAllocPolicy> Map; |
| Map *map_; |
| |
| public: |
| SourceDataCache() : map_(NULL) {} |
| JSStableString *lookup(ScriptSource *ss); |
| void put(ScriptSource *ss, JSStableString *); |
| void purge(); |
| }; |
| |
| struct EvalCacheEntry |
| { |
| JSScript *script; |
| JSScript *callerScript; |
| jsbytecode *pc; |
| }; |
| |
| struct EvalCacheLookup |
| { |
| EvalCacheLookup(JSContext *cx) : str(cx), callerScript(cx) {} |
| RootedLinearString str; |
| RootedScript callerScript; |
| JSVersion version; |
| jsbytecode *pc; |
| }; |
| |
| struct EvalCacheHashPolicy |
| { |
| typedef EvalCacheLookup Lookup; |
| |
| static HashNumber hash(const Lookup &l); |
| static bool match(const EvalCacheEntry &entry, const EvalCacheLookup &l); |
| }; |
| |
| typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache; |
| |
| struct LazyScriptHashPolicy |
| { |
| struct Lookup { |
| JSContext *cx; |
| LazyScript *lazy; |
| |
| Lookup(JSContext *cx, LazyScript *lazy) |
| : cx(cx), lazy(lazy) |
| {} |
| }; |
| |
| static const size_t NumHashes = 3; |
| |
| static void hash(const Lookup &lookup, HashNumber hashes[NumHashes]); |
| static bool match(JSScript *script, const Lookup &lookup); |
| |
| // Alternate methods for use when removing scripts from the hash without an |
| // explicit LazyScript lookup. |
| static void hash(JSScript *script, HashNumber hashes[NumHashes]); |
| static bool match(JSScript *script, JSScript *lookup) { return script == lookup; } |
| |
| static void clear(JSScript **pscript) { *pscript = NULL; } |
| static bool isCleared(JSScript *script) { return !script; } |
| }; |
| |
| typedef FixedSizeHashSet<JSScript *, LazyScriptHashPolicy, 769> LazyScriptCache; |
| |
| class PropertyIteratorObject; |
| |
| class NativeIterCache |
| { |
| static const size_t SIZE = size_t(1) << 8; |
| |
| /* Cached native iterators. */ |
| PropertyIteratorObject *data[SIZE]; |
| |
| static size_t getIndex(uint32_t key) { |
| return size_t(key) % SIZE; |
| } |
| |
| public: |
| /* Native iterator most recently started. */ |
| PropertyIteratorObject *last; |
| |
| NativeIterCache() |
| : last(NULL) |
| { |
| mozilla::PodArrayZero(data); |
| } |
| |
| void purge() { |
| last = NULL; |
| mozilla::PodArrayZero(data); |
| } |
| |
| PropertyIteratorObject *get(uint32_t key) const { |
| return data[getIndex(key)]; |
| } |
| |
| void set(uint32_t key, PropertyIteratorObject *iterobj) { |
| data[getIndex(key)] = iterobj; |
| } |
| }; |
| |
| /* |
| * Cache for speeding up repetitive creation of objects in the VM. |
| * When an object is created which matches the criteria in the 'key' section |
| * below, an entry is filled with the resulting object. |
| */ |
| class NewObjectCache |
| { |
| /* Statically asserted to be equal to sizeof(JSObject_Slots16) */ |
| static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value); |
| static inline void staticAsserts(); |
| |
| struct Entry |
| { |
| /* Class of the constructed object. */ |
| Class *clasp; |
| |
| /* |
| * Key with one of three possible values: |
| * |
| * - Global for the object. The object must have a standard class for |
| * which the global's prototype can be determined, and the object's |
| * parent will be the global. |
| * |
| * - Prototype for the object (cannot be global). The object's parent |
| * will be the prototype's parent. |
| * |
| * - Type for the object. The object's parent will be the type's |
| * prototype's parent. |
| */ |
| gc::Cell *key; |
| |
| /* Allocation kind for the constructed object. */ |
| gc::AllocKind kind; |
| |
| /* Number of bytes to copy from the template object. */ |
| uint32_t nbytes; |
| |
| /* |
| * Template object to copy from, with the initial values of fields, |
| * fixed slots (undefined) and private data (NULL). |
| */ |
| char templateObject[MAX_OBJ_SIZE]; |
| }; |
| |
| Entry entries[41]; // TODO: reconsider size |
| |
| public: |
| |
| typedef int EntryIndex; |
| |
| NewObjectCache() { mozilla::PodZero(this); } |
| void purge() { mozilla::PodZero(this); } |
| |
| /* Remove any cached items keyed on moved objects. */ |
| void clearNurseryObjects(JSRuntime *rt); |
| |
| /* |
| * Get the entry index for the given lookup, return whether there was a hit |
| * on an existing entry. |
| */ |
| inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry); |
| inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry); |
| inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry); |
| |
| /* |
| * Return a new object from a cache hit produced by a lookup method, or |
| * NULL if returning the object could possibly trigger GC (does not |
| * indicate failure). |
| */ |
| inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap); |
| |
| /* Fill an entry after a cache miss. */ |
| void fillProto(EntryIndex entry, Class *clasp, js::TaggedProto proto, gc::AllocKind kind, JSObject *obj); |
| inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj); |
| inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj); |
| |
| /* Invalidate any entries which might produce an object with shape/proto. */ |
| void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto); |
| |
| private: |
| inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry); |
| inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj); |
| static inline void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind); |
| }; |
| |
| /* |
| * A FreeOp can do one thing: free memory. For convenience, it has delete_ |
| * convenience methods that also call destructors. |
| * |
| * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not |
| * need to pass a JSContext to those hooks. |
| */ |
| class FreeOp : public JSFreeOp { |
| bool shouldFreeLater_; |
| |
| public: |
| static FreeOp *get(JSFreeOp *fop) { |
| return static_cast<FreeOp *>(fop); |
| } |
| |
| FreeOp(JSRuntime *rt, bool shouldFreeLater) |
| : JSFreeOp(rt), |
| shouldFreeLater_(shouldFreeLater) |
| { |
| } |
| |
| bool shouldFreeLater() const { |
| return shouldFreeLater_; |
| } |
| |
| inline void free_(void *p); |
| |
| template <class T> |
| inline void delete_(T *p) { |
| if (p) { |
| p->~T(); |
| free_(p); |
| } |
| } |
| |
| static void staticAsserts() { |
| /* |
| * Check that JSFreeOp is the first base class for FreeOp and we can |
| * reinterpret a pointer to JSFreeOp as a pointer to FreeOp without |
| * any offset adjustments. JSClass::finalize <-> Class::finalize depends |
| * on this. |
| */ |
| JS_STATIC_ASSERT(offsetof(FreeOp, shouldFreeLater_) == sizeof(JSFreeOp)); |
| } |
| }; |
| |
| } /* namespace js */ |
| |
| namespace JS { |
| struct RuntimeSizes; |
| } |
| |
| /* Various built-in or commonly-used names pinned on first context. */ |
| struct JSAtomState |
| { |
| #define PROPERTYNAME_FIELD(idpart, id, text) js::FixedHeapPtr<js::PropertyName> id; |
| FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD) |
| #undef PROPERTYNAME_FIELD |
| #define PROPERTYNAME_FIELD(name, code, init) js::FixedHeapPtr<js::PropertyName> name; |
| JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD) |
| #undef PROPERTYNAME_FIELD |
| }; |
| |
| #define NAME_OFFSET(name) offsetof(JSAtomState, name) |
| #define OFFSET_TO_NAME(rt,off) (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off))) |
| |
| namespace js { |
| |
| /* |
| * Encapsulates portions of the runtime/context that are tied to a |
| * single active thread. Normally, as most JS is single-threaded, |
| * there is only one instance of this struct, embedded in the |
| * JSRuntime as the field |mainThread|. During Parallel JS sections, |
| * however, there will be one instance per worker thread. |
| */ |
| class PerThreadData : public js::PerThreadDataFriendFields |
| { |
| /* |
| * Backpointer to the full shared JSRuntime* with which this |
| * thread is associated. This is private because accessing the |
| * fields of this runtime can provoke race conditions, so the |
| * intention is that access will be mediated through safe |
| * functions like |associatedWith()| below. |
| */ |
| JSRuntime *runtime_; |
| |
| public: |
| /* |
| * We save all conservative scanned roots in this vector so that |
| * conservative scanning can be "replayed" deterministically. In DEBUG mode, |
| * this allows us to run a non-incremental GC after every incremental GC to |
| * ensure that no objects were missed. |
| */ |
| #ifdef DEBUG |
| struct SavedGCRoot { |
| void *thing; |
| JSGCTraceKind kind; |
| |
| SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {} |
| }; |
| js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots; |
| #endif |
| |
| /* |
| * If Ion code is on the stack, and has called into C++, this will be |
| * aligned to an Ion exit frame. |
| */ |
| uint8_t *ionTop; |
| JSContext *ionJSContext; |
| uintptr_t ionStackLimit; |
| |
| inline void setIonStackLimit(uintptr_t limit); |
| |
| /* |
| * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This |
| * stack is used by JSRuntime::triggerOperationCallback to stop long- |
| * running asm.js without requiring dynamic polling operations in the |
| * generated code. Since triggerOperationCallback may run on a separate |
| * thread than the JSRuntime's owner thread all reads/writes must be |
| * synchronized (by rt->operationCallbackLock). |
| */ |
| private: |
| friend class js::Activation; |
| friend class js::ActivationIterator; |
| friend class js::jit::JitActivation; |
| friend class js::AsmJSActivation; |
| |
| /* |
| * Points to the most recent activation running on the thread. |
| * See Activation comment in vm/Stack.h. |
| */ |
| js::Activation *activation_; |
| |
| /* See AsmJSActivation comment. Protected by rt->operationCallbackLock. */ |
| js::AsmJSActivation *asmJSActivationStack_; |
| |
| public: |
| static unsigned offsetOfActivation() { |
| return offsetof(PerThreadData, activation_); |
| } |
| static unsigned offsetOfAsmJSActivationStackReadOnly() { |
| return offsetof(PerThreadData, asmJSActivationStack_); |
| } |
| |
| js::AsmJSActivation *asmJSActivationStackFromAnyThread() const { |
| return asmJSActivationStack_; |
| } |
| js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const { |
| return asmJSActivationStack_; |
| } |
| |
| js::Activation *activation() const { |
| return activation_; |
| } |
| |
| /* |
| * When this flag is non-zero, any attempt to GC will be skipped. It is used |
| * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in |
| * debugging facilities that cannot tolerate a GC and would rather OOM |
| * immediately, such as utilities exposed to GDB. Setting this flag is |
| * extremely dangerous and should only be used when in an OOM situation or |
| * in non-exposed debugging facilities. |
| */ |
| int32_t suppressGC; |
| |
| PerThreadData(JSRuntime *runtime); |
| |
| bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } |
| }; |
| |
| template<class Client> |
| struct MallocProvider |
| { |
| void *malloc_(size_t bytes) { |
| Client *client = static_cast<Client *>(this); |
| client->updateMallocCounter(bytes); |
| void *p = js_malloc(bytes); |
| return JS_LIKELY(!!p) ? p : client->onOutOfMemory(NULL, bytes); |
| } |
| |
| void *calloc_(size_t bytes) { |
| Client *client = static_cast<Client *>(this); |
| client->updateMallocCounter(bytes); |
| void *p = js_calloc(bytes); |
| return JS_LIKELY(!!p) ? p : client->onOutOfMemory(reinterpret_cast<void *>(1), bytes); |
| } |
| |
| void *realloc_(void *p, size_t oldBytes, size_t newBytes) { |
| Client *client = static_cast<Client *>(this); |
| /* |
| * For compatibility we do not account for realloc that decreases |
| * previously allocated memory. |
| */ |
| if (newBytes > oldBytes) |
| client->updateMallocCounter(newBytes - oldBytes); |
| void *p2 = js_realloc(p, newBytes); |
| return JS_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, newBytes); |
| } |
| |
| void *realloc_(void *p, size_t bytes) { |
| Client *client = static_cast<Client *>(this); |
| /* |
| * For compatibility we do not account for realloc that increases |
| * previously allocated memory. |
| */ |
| if (!p) |
| client->updateMallocCounter(bytes); |
| void *p2 = js_realloc(p, bytes); |
| return JS_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, bytes); |
| } |
| |
| template <class T> |
| T *pod_malloc() { |
| return (T *)malloc_(sizeof(T)); |
| } |
| |
| template <class T> |
| T *pod_calloc() { |
| return (T *)calloc_(sizeof(T)); |
| } |
| |
| template <class T> |
| T *pod_malloc(size_t numElems) { |
| if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) { |
| Client *client = static_cast<Client *>(this); |
| client->reportAllocationOverflow(); |
| return NULL; |
| } |
| return (T *)malloc_(numElems * sizeof(T)); |
| } |
| |
| template <class T> |
| T *pod_calloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) { |
| if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) { |
| Client *client = static_cast<Client *>(this); |
| client->reportAllocationOverflow(); |
| return NULL; |
| } |
| return (T *)calloc_(numElems * sizeof(T)); |
| } |
| |
| JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE) |
| }; |
| |
| namespace gc { |
| class MarkingValidator; |
| } // namespace gc |
| |
| typedef Vector<JS::Zone *, 1, SystemAllocPolicy> ZoneVector; |
| |
| struct SelfHostedClass; |
| |
| } // namespace js |
| |
| struct JSRuntime : public JS::shadow::Runtime, |
| public js::MallocProvider<JSRuntime> |
| { |
| /* |
| * Per-thread data for the main thread that is associated with |
| * this JSRuntime, as opposed to any worker threads used in |
| * parallel sections. See definition of |PerThreadData| struct |
| * above for more details. |
| * |
| * NB: This field is statically asserted to be at offset |
| * sizeof(js::shadow::Runtime). See |
| * PerThreadDataFriendFields::getMainThread. |
| */ |
| js::PerThreadData mainThread; |
| |
| /* |
| * If non-zero, we were been asked to call the operation callback as soon |
| * as possible. |
| */ |
| volatile int32_t interrupt; |
| |
| #ifdef JS_THREADSAFE |
| private: |
| /* |
| * Lock taken when triggering the operation callback from another thread. |
| * Protects all data that is touched in this process. |
| */ |
| PRLock *operationCallbackLock; |
| #ifdef DEBUG |
| PRThread *operationCallbackOwner; |
| #endif |
| public: |
| #endif // JS_THREADSAFE |
| |
| class AutoLockForOperationCallback { |
| #ifdef JS_THREADSAFE |
| JSRuntime *rt; |
| public: |
| AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| PR_Lock(rt->operationCallbackLock); |
| #ifdef DEBUG |
| rt->operationCallbackOwner = PR_GetCurrentThread(); |
| #endif |
| } |
| ~AutoLockForOperationCallback() { |
| JS_ASSERT(rt->operationCallbackOwner == PR_GetCurrentThread()); |
| #ifdef DEBUG |
| rt->operationCallbackOwner = NULL; |
| #endif |
| PR_Unlock(rt->operationCallbackLock); |
| } |
| #else // JS_THREADSAFE |
| public: |
| AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| #endif // JS_THREADSAFE |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| bool currentThreadOwnsOperationCallbackLock() { |
| #if defined(JS_THREADSAFE) && defined(DEBUG) |
| return operationCallbackOwner == PR_GetCurrentThread(); |
| #else |
| return true; |
| #endif |
| } |
| |
| /* Default compartment. */ |
| JSCompartment *atomsCompartment; |
| |
| /* Embedders can use this zone however they wish. */ |
| JS::Zone *systemZone; |
| |
| /* List of compartments and zones (protected by the GC lock). */ |
| js::ZoneVector zones; |
| |
| /* How many compartments there are across all zones. */ |
| size_t numCompartments; |
| |
| /* Locale-specific callbacks for string conversion. */ |
| JSLocaleCallbacks *localeCallbacks; |
| |
| /* Default locale for Internationalization API */ |
| char *defaultLocale; |
| |
| /* Default JSVersion. */ |
| JSVersion defaultVersion_; |
| |
| /* See comment for JS_AbortIfWrongThread in jsapi.h. */ |
| #ifdef JS_THREADSAFE |
| public: |
| void *ownerThread() const { return ownerThread_; } |
| void clearOwnerThread(); |
| void setOwnerThread(); |
| JS_FRIEND_API(void) abortIfWrongThread() const; |
| #ifdef DEBUG |
| JS_FRIEND_API(void) assertValidThread() const; |
| #else |
| void assertValidThread() const {} |
| #endif |
| private: |
| void *ownerThread_; |
| public: |
| #else |
| public: |
| void abortIfWrongThread() const {} |
| void assertValidThread() const {} |
| #endif |
| |
| /* Temporary arena pool used while compiling and decompiling. */ |
| static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; |
| js::LifoAlloc tempLifoAlloc; |
| |
| /* |
| * Free LIFO blocks are transferred to this allocator before being freed on |
| * the background GC thread. |
| */ |
| js::LifoAlloc freeLifoAlloc; |
| |
| private: |
| /* |
| * Both of these allocators are used for regular expression code which is shared at the |
| * thread-data level. |
| */ |
| #if ENABLE_ASSEMBLER |
| JSC::ExecutableAllocator *execAlloc_; |
| #endif |
| WTF::BumpPointerAllocator *bumpAlloc_; |
| js::jit::IonRuntime *ionRuntime_; |
| |
| JSObject *selfHostingGlobal_; |
| js::SelfHostedClass *selfHostedClasses_; |
| |
| /* Space for interpreter frames. */ |
| js::InterpreterStack interpreterStack_; |
| |
| JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); |
| WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); |
| js::jit::IonRuntime *createIonRuntime(JSContext *cx); |
| |
| public: |
| #if ENABLE_ASSEMBLER |
| JSC::ExecutableAllocator *getExecAlloc(JSContext *cx) { |
| return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx); |
| } |
| JSC::ExecutableAllocator &execAlloc() { |
| JS_ASSERT(execAlloc_); |
| return *execAlloc_; |
| } |
| JSC::ExecutableAllocator *maybeExecAlloc() { |
| return execAlloc_; |
| } |
| #endif |
| WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) { |
| return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx); |
| } |
| js::jit::IonRuntime *getIonRuntime(JSContext *cx) { |
| return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx); |
| } |
| js::jit::IonRuntime *ionRuntime() { |
| return ionRuntime_; |
| } |
| bool hasIonRuntime() const { |
| return !!ionRuntime_; |
| } |
| js::InterpreterStack &interpreterStack() { |
| return interpreterStack_; |
| } |
| |
| //------------------------------------------------------------------------- |
| // Self-hosting support |
| //------------------------------------------------------------------------- |
| |
| bool initSelfHosting(JSContext *cx); |
| void finishSelfHosting(); |
| void markSelfHostingGlobal(JSTracer *trc); |
| bool isSelfHostingGlobal(js::HandleObject global) { |
| return global == selfHostingGlobal_; |
| } |
| js::SelfHostedClass *selfHostedClasses() { |
| return selfHostedClasses_; |
| } |
| void addSelfHostedClass(js::SelfHostedClass *shClass); |
| bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle<js::PropertyName*> name, |
| js::Handle<JSFunction*> targetFun); |
| bool cloneSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name, |
| js::MutableHandleValue vp); |
| bool maybeWrappedSelfHostedFunction(JSContext *cx, js::Handle<js::PropertyName*> name, |
| js::MutableHandleValue funVal); |
| |
| //------------------------------------------------------------------------- |
| // Locale information |
| //------------------------------------------------------------------------- |
| |
| /* |
| * Set the default locale for the ECMAScript Internationalization API |
| * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). |
| * Note that the Internationalization API encourages clients to |
| * specify their own locales. |
| * The locale string remains owned by the caller. |
| */ |
| bool setDefaultLocale(const char *locale); |
| |
| /* Reset the default locale to OS defaults. */ |
| void resetDefaultLocale(); |
| |
| /* Gets current default locale. String remains owned by context. */ |
| const char *getDefaultLocale(); |
| |
| JSVersion defaultVersion() { return defaultVersion_; } |
| void setDefaultVersion(JSVersion v) { defaultVersion_ = v; } |
| |
| /* Base address of the native stack for the current thread. */ |
| uintptr_t nativeStackBase; |
| |
| /* The native stack size limit that runtime should not exceed. */ |
| size_t nativeStackQuota; |
| |
| /* |
| * Frames currently running in js::Interpret. See InterpreterFrames for |
| * details. |
| */ |
| js::InterpreterFrames *interpreterFrames; |
| |
| /* Context create/destroy callback. */ |
| JSContextCallback cxCallback; |
| |
| /* Compartment destroy callback. */ |
| JSDestroyCompartmentCallback destroyCompartmentCallback; |
| |
| /* Zone destroy callback. */ |
| JSZoneCallback destroyZoneCallback; |
| |
| /* Zone sweep callback. */ |
| JSZoneCallback sweepZoneCallback; |
| |
| /* Call this to get the name of a compartment. */ |
| JSCompartmentNameCallback compartmentNameCallback; |
| |
| js::ActivityCallback activityCallback; |
| void *activityCallbackArg; |
| |
| #ifdef JS_THREADSAFE |
| /* The request depth for this thread. */ |
| unsigned requestDepth; |
| |
| # ifdef DEBUG |
| unsigned checkRequestDepth; |
| # endif |
| #endif |
| |
| /* Garbage collector state, used by jsgc.c. */ |
| |
| /* |
| * Set of all GC chunks with at least one allocated thing. The |
| * conservative GC uses it to quickly check if a possible GC thing points |
| * into an allocated chunk. |
| */ |
| js::GCChunkSet gcChunkSet; |
| |
| /* |
| * Doubly-linked lists of chunks from user and system compartments. The GC |
| * allocates its arenas from the corresponding list and when all arenas |
| * in the list head are taken, then the chunk is removed from the list. |
| * During the GC when all arenas in a chunk become free, that chunk is |
| * removed from the list and scheduled for release. |
| */ |
| js::gc::Chunk *gcSystemAvailableChunkListHead; |
| js::gc::Chunk *gcUserAvailableChunkListHead; |
| js::gc::ChunkPool gcChunkPool; |
| |
| js::RootedValueMap gcRootsHash; |
| unsigned gcKeepAtoms; |
| volatile size_t gcBytes; |
| size_t gcMaxBytes; |
| size_t gcMaxMallocBytes; |
| |
| /* |
| * Number of the committed arenas in all GC chunks including empty chunks. |
| * The counter is volatile as it is read without the GC lock, see comments |
| * in MaybeGC. |
| */ |
| volatile uint32_t gcNumArenasFreeCommitted; |
| js::GCMarker gcMarker; |
| void *gcVerifyPreData; |
| void *gcVerifyPostData; |
| bool gcChunkAllocationSinceLastGC; |
| int64_t gcNextFullGCTime; |
| int64_t gcLastGCTime; |
| int64_t gcJitReleaseTime; |
| JSGCMode gcMode; |
| size_t gcAllocationThreshold; |
| bool gcHighFrequencyGC; |
| uint64_t gcHighFrequencyTimeThreshold; |
| uint64_t gcHighFrequencyLowLimitBytes; |
| uint64_t gcHighFrequencyHighLimitBytes; |
| double gcHighFrequencyHeapGrowthMax; |
| double gcHighFrequencyHeapGrowthMin; |
| double gcLowFrequencyHeapGrowth; |
| bool gcDynamicHeapGrowth; |
| bool gcDynamicMarkSlice; |
| uint64_t gcDecommitThreshold; |
| |
| /* During shutdown, the GC needs to clean up every possible object. */ |
| bool gcShouldCleanUpEverything; |
| |
| /* |
| * The gray bits can become invalid if UnmarkGray overflows the stack. A |
| * full GC will reset this bit, since it fills in all the gray bits. |
| */ |
| bool gcGrayBitsValid; |
| |
| /* |
| * These flags must be kept separate so that a thread requesting a |
| * compartment GC doesn't cancel another thread's concurrent request for a |
| * full GC. |
| */ |
| volatile uintptr_t gcIsNeeded; |
| |
| js::gcstats::Statistics gcStats; |
| |
| /* Incremented on every GC slice. */ |
| uint64_t gcNumber; |
| |
| /* The gcNumber at the time of the most recent GC's first slice. */ |
| uint64_t gcStartNumber; |
| |
| /* Whether the currently running GC can finish in multiple slices. */ |
| bool gcIsIncremental; |
| |
| /* Whether all compartments are being collected in first GC slice. */ |
| bool gcIsFull; |
| |
| /* The reason that an interrupt-triggered GC should be called. */ |
| JS::gcreason::Reason gcTriggerReason; |
| |
| /* |
| * If this is true, all marked objects must belong to a compartment being |
| * GCed. This is used to look for compartment bugs. |
| */ |
| bool gcStrictCompartmentChecking; |
| |
| #ifdef DEBUG |
| /* |
| * If this is 0, all cross-compartment proxies must be registered in the |
| * wrapper map. This checking must be disabled temporarily while creating |
| * new wrappers. When non-zero, this records the recursion depth of wrapper |
| * creation. |
| */ |
| uintptr_t gcDisableStrictProxyCheckingCount; |
| #else |
| uintptr_t unused1; |
| #endif |
| |
| /* |
| * The current incremental GC phase. This is also used internally in |
| * non-incremental GC. |
| */ |
| js::gc::State gcIncrementalState; |
| |
| /* Indicates that the last incremental slice exhausted the mark stack. */ |
| bool gcLastMarkSlice; |
| |
| /* Whether any sweeping will take place in the separate GC helper thread. */ |
| bool gcSweepOnBackgroundThread; |
| |
| /* Whether any black->gray edges were found during marking. */ |
| bool gcFoundBlackGrayEdges; |
| |
| /* List head of zones to be swept in the background. */ |
| JS::Zone *gcSweepingZones; |
| |
| /* Index of current zone group (for stats). */ |
| unsigned gcZoneGroupIndex; |
| |
| /* |
| * Incremental sweep state. |
| */ |
| JS::Zone *gcZoneGroups; |
| JS::Zone *gcCurrentZoneGroup; |
| int gcSweepPhase; |
| JS::Zone *gcSweepZone; |
| int gcSweepKindIndex; |
| bool gcAbortSweepAfterCurrentGroup; |
| |
| /* |
| * List head of arenas allocated during the sweep phase. |
| */ |
| js::gc::ArenaHeader *gcArenasAllocatedDuringSweep; |
| |
| #ifdef DEBUG |
| js::gc::MarkingValidator *gcMarkingValidator; |
| #endif |
| |
| /* |
| * Indicates that a GC slice has taken place in the middle of an animation |
| * frame, rather than at the beginning. In this case, the next slice will be |
| * delayed so that we don't get back-to-back slices. |
| */ |
| volatile uintptr_t gcInterFrameGC; |
| |
| /* Default budget for incremental GC slice. See SliceBudget in jsgc.h. */ |
| int64_t gcSliceBudget; |
| |
| /* |
| * We disable incremental GC if we encounter a js::Class with a trace hook |
| * that does not implement write barriers. |
| */ |
| bool gcIncrementalEnabled; |
| |
| /* |
| * GGC can be enabled from the command line while testing. |
| */ |
| bool gcGenerationalEnabled; |
| |
| /* |
| * This is true if we are in the middle of a brain transplant (e.g., |
| * JS_TransplantObject) or some other operation that can manipulate |
| * dead zones. |
| */ |
| bool gcManipulatingDeadZones; |
| |
| /* |
| * This field is incremented each time we mark an object inside a |
| * zone with no incoming cross-compartment pointers. Typically if |
| * this happens it signals that an incremental GC is marking too much |
| * stuff. At various times we check this counter and, if it has changed, we |
| * run an immediate, non-incremental GC to clean up the dead |
| * zones. This should happen very rarely. |
| */ |
| unsigned gcObjectsMarkedInDeadZones; |
| |
| bool gcPoke; |
| |
| volatile js::HeapState heapState; |
| |
| bool isHeapBusy() { return heapState != js::Idle; } |
| bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; } |
| bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; } |
| bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); } |
| |
| #ifdef JSGC_GENERATIONAL |
| js::Nursery gcNursery; |
| js::gc::StoreBuffer gcStoreBuffer; |
| #endif |
| |
| /* |
| * These options control the zealousness of the GC. The fundamental values |
| * are gcNextScheduled and gcDebugCompartmentGC. At every allocation, |
| * gcNextScheduled is decremented. When it reaches zero, we do either a |
| * full or a compartmental GC, based on gcDebugCompartmentGC. |
| * |
| * At this point, if gcZeal_ is one of the types that trigger periodic |
| * collection, then gcNextScheduled is reset to the value of |
| * gcZealFrequency. Otherwise, no additional GCs take place. |
| * |
| * You can control these values in several ways: |
| * - Pass the -Z flag to the shell (see the usage info for details) |
| * - Call gczeal() or schedulegc() from inside shell-executed JS code |
| * (see the help for details) |
| * |
| * If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and |
| * whenever a GC poke happens). This option is mainly useful to embedders. |
| * |
| * We use gcZeal_ == 4 to enable write barrier verification. See the comment |
| * in jsgc.cpp for more information about this. |
| * |
| * gcZeal_ values from 8 to 10 periodically run different types of |
| * incremental GC. |
| */ |
| #ifdef JS_GC_ZEAL |
| int gcZeal_; |
| int gcZealFrequency; |
| int gcNextScheduled; |
| bool gcDeterministicOnly; |
| int gcIncrementalLimit; |
| |
| js::Vector<JSObject *, 0, js::SystemAllocPolicy> gcSelectedForMarking; |
| |
| int gcZeal() { return gcZeal_; } |
| |
| bool needZealousGC() { |
| if (gcNextScheduled > 0 && --gcNextScheduled == 0) { |
| if (gcZeal() == js::gc::ZealAllocValue || |
| gcZeal() == js::gc::ZealPurgeAnalysisValue || |
| (gcZeal() >= js::gc::ZealIncrementalRootsThenFinish && |
| gcZeal() <= js::gc::ZealIncrementalMultipleSlices)) |
| { |
| gcNextScheduled = gcZealFrequency; |
| } |
| return true; |
| } |
| return false; |
| } |
| #else |
| int gcZeal() { return 0; } |
| bool needZealousGC() { return false; } |
| #endif |
| |
| bool gcValidate; |
| bool gcFullCompartmentChecks; |
| |
| JSGCCallback gcCallback; |
| JS::GCSliceCallback gcSliceCallback; |
| JSFinalizeCallback gcFinalizeCallback; |
| |
| js::AnalysisPurgeCallback analysisPurgeCallback; |
| uint64_t analysisPurgeTriggerBytes; |
| |
| private: |
| /* |
| * Malloc counter to measure memory pressure for GC scheduling. It runs |
| * from gcMaxMallocBytes down to zero. |
| */ |
| volatile ptrdiff_t gcMallocBytes; |
| |
| public: |
| void setNeedsBarrier(bool needs) { |
| needsBarrier_ = needs; |
| } |
| |
| bool needsBarrier() const { |
| return needsBarrier_; |
| } |
| |
| struct ExtraTracer { |
| JSTraceDataOp op; |
| void *data; |
| |
| ExtraTracer() |
| : op(NULL), data(NULL) |
| {} |
| ExtraTracer(JSTraceDataOp op, void *data) |
| : op(op), data(data) |
| {} |
| }; |
| |
| /* |
| * The trace operations to trace embedding-specific GC roots. One is for |
| * tracing through black roots and the other is for tracing through gray |
| * roots. The black/gray distinction is only relevant to the cycle |
| * collector. |
| */ |
| typedef js::Vector<ExtraTracer, 4, js::SystemAllocPolicy> ExtraTracerVector; |
| ExtraTracerVector gcBlackRootTracers; |
| ExtraTracer gcGrayRootTracer; |
| |
| /* Stack of thread-stack-allocated GC roots. */ |
| js::AutoGCRooter *autoGCRooters; |
| |
| /* |
| * The GC can only safely decommit memory when the page size of the |
| * running process matches the compiled arena size. |
| */ |
| size_t gcSystemPageSize; |
| |
| /* The OS allocation granularity may not match the page size. */ |
| size_t gcSystemAllocGranularity; |
| |
| /* Strong references on scripts held for PCCount profiling API. */ |
| js::ScriptAndCountsVector *scriptAndCountsVector; |
| |
| /* Well-known numbers held for use by this runtime's contexts. */ |
| js::Value NaNValue; |
| js::Value negativeInfinityValue; |
| js::Value positiveInfinityValue; |
| |
| js::PropertyName *emptyString; |
| |
| /* List of active contexts sharing this runtime. */ |
| mozilla::LinkedList<JSContext> contextList; |
| |
| bool hasContexts() const { |
| return !contextList.isEmpty(); |
| } |
| |
| JS_SourceHook sourceHook; |
| |
| /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ |
| JSDebugHooks debugHooks; |
| |
| /* If true, new compartments are initially in debug mode. */ |
| bool debugMode; |
| |
| /* SPS profiling metadata */ |
| js::SPSProfiler spsProfiler; |
| |
| /* If true, new scripts must be created with PC counter information. */ |
| bool profilingScripts; |
| |
| /* Always preserve JIT code during GCs, for testing. */ |
| bool alwaysPreserveCode; |
| |
| /* Had an out-of-memory error which did not populate an exception. */ |
| bool hadOutOfMemory; |
| |
| /* Linked list of all Debugger objects in the runtime. */ |
| mozilla::LinkedList<js::Debugger> debuggerList; |
| |
| /* |
| * Head of circular list of all enabled Debuggers that have |
| * onNewGlobalObject handler methods established. |
| */ |
| JSCList onNewGlobalObjectWatchers; |
| |
| /* Client opaque pointers */ |
| void *data; |
| |
| /* Synchronize GC heap access between main thread and GCHelperThread. */ |
| PRLock *gcLock; |
| |
| js::GCHelperThread gcHelperThread; |
| |
| #ifdef XP_MACOSX |
| js::AsmJSMachExceptionHandler asmJSMachExceptionHandler; |
| #endif |
| |
| #ifdef JS_THREADSAFE |
| # ifdef JS_ION |
| js::WorkerThreadState *workerThreadState; |
| # endif |
| |
| js::SourceCompressorThread sourceCompressorThread; |
| #endif |
| |
| private: |
| js::FreeOp defaultFreeOp_; |
| |
| public: |
| js::FreeOp *defaultFreeOp() { |
| return &defaultFreeOp_; |
| } |
| |
| uint32_t debuggerMutations; |
| |
| const JSSecurityCallbacks *securityCallbacks; |
| const js::DOMCallbacks *DOMcallbacks; |
| JSDestroyPrincipalsOp destroyPrincipals; |
| |
| /* Structured data callbacks are runtime-wide. */ |
| const JSStructuredCloneCallbacks *structuredCloneCallbacks; |
| |
| /* Call this to accumulate telemetry data. */ |
| JSAccumulateTelemetryDataCallback telemetryCallback; |
| |
| /* |
| * The propertyRemovals counter is incremented for every JSObject::clear, |
| * and for each JSObject::remove method call that frees a slot in the given |
| * object. See js_NativeGet and js_NativeSet in jsobj.cpp. |
| */ |
| uint32_t propertyRemovals; |
| |
| #if !ENABLE_INTL_API |
| /* Number localization, used by jsnum.cpp. */ |
| const char *thousandsSeparator; |
| const char *decimalSeparator; |
| const char *numGrouping; |
| #endif |
| |
| private: |
| js::MathCache *mathCache_; |
| js::MathCache *createMathCache(JSContext *cx); |
| public: |
| js::MathCache *getMathCache(JSContext *cx) { |
| return mathCache_ ? mathCache_ : createMathCache(cx); |
| } |
| |
| js::GSNCache gsnCache; |
| js::NewObjectCache newObjectCache; |
| js::NativeIterCache nativeIterCache; |
| js::SourceDataCache sourceDataCache; |
| js::EvalCache evalCache; |
| js::LazyScriptCache lazyScriptCache; |
| |
| /* State used by jsdtoa.cpp. */ |
| DtoaState *dtoaState; |
| |
| js::DateTimeInfo dateTimeInfo; |
| |
| js::ConservativeGCData conservativeGC; |
| |
| /* Pool of maps used during parse/emit. */ |
| js::frontend::ParseMapPool parseMapPool; |
| |
| /* |
| * Count of currently active compilations. |
| * When there are compilations active for the context, the GC must not |
| * purge the ParseMapPool. |
| */ |
| unsigned activeCompilations; |
| |
| private: |
| JSPrincipals *trustedPrincipals_; |
| public: |
| void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; } |
| JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; } |
| |
| /* Set of all currently-living atoms. */ |
| js::AtomSet atoms; |
| |
| union { |
| /* |
| * Cached pointers to various interned property names, initialized in |
| * order from first to last via the other union arm. |
| */ |
| JSAtomState atomState; |
| |
| js::FixedHeapPtr<js::PropertyName> firstCachedName; |
| }; |
| |
| /* Tables of strings that are pre-allocated in the atomsCompartment. */ |
| js::StaticStrings staticStrings; |
| |
| JSWrapObjectCallback wrapObjectCallback; |
| JSSameCompartmentWrapObjectCallback sameCompartmentWrapObjectCallback; |
| JSPreWrapCallback preWrapObjectCallback; |
| js::PreserveWrapperCallback preserveWrapperCallback; |
| |
| js::ScriptDataTable scriptDataTable; |
| |
| #ifdef DEBUG |
| size_t noGCOrAllocationCheck; |
| #endif |
| |
| bool jitHardening; |
| |
| bool jitSupportsFloatingPoint; |
| |
| // Used to reset stack limit after a signaled interrupt (i.e. ionStackLimit_ = -1) |
| // has been noticed by Ion/Baseline. |
| void resetIonStackLimit() { |
| AutoLockForOperationCallback lock(this); |
| mainThread.setIonStackLimit(mainThread.nativeStackLimit); |
| } |
| |
| // Cache for jit::GetPcScript(). |
| js::jit::PcScriptCache *ionPcScriptCache; |
| |
| js::ThreadPool threadPool; |
| |
| js::CTypesActivityCallback ctypesActivityCallback; |
| |
| // Non-zero if this is a parallel warmup execution. See |
| // js::parallel::Do() for more information. |
| uint32_t parallelWarmup; |
| |
| private: |
| // In certain cases, we want to optimize certain opcodes to typed instructions, |
| // to avoid carrying an extra register to feed into an unbox. Unfortunately, |
| // that's not always possible. For example, a GetPropertyCacheT could return a |
| // typed double, but if it takes its out-of-line path, it could return an |
| // object, and trigger invalidation. The invalidation bailout will consider the |
| // return value to be a double, and create a garbage Value. |
| // |
| // To allow the GetPropertyCacheT optimization, we allow the ability for |
| // GetPropertyCache to override the return value at the top of the stack - the |
| // value that will be temporarily corrupt. This special override value is set |
| // only in callVM() targets that are about to return *and* have invalidated |
| // their callee. |
| js::Value ionReturnOverride_; |
| |
| public: |
| bool hasIonReturnOverride() const { |
| return !ionReturnOverride_.isMagic(); |
| } |
| js::Value takeIonReturnOverride() { |
| js::Value v = ionReturnOverride_; |
| ionReturnOverride_ = js::MagicValue(JS_ARG_POISON); |
| return v; |
| } |
| void setIonReturnOverride(const js::Value &v) { |
| JS_ASSERT(!hasIonReturnOverride()); |
| ionReturnOverride_ = v; |
| } |
| |
| JSRuntime(JSUseHelperThreads useHelperThreads); |
| ~JSRuntime(); |
| |
| bool init(uint32_t maxbytes); |
| |
| JSRuntime *thisFromCtor() { return this; } |
| |
| void setGCMaxMallocBytes(size_t value); |
| |
| void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); } |
| |
| /* |
| * Call this after allocating memory held by GC things, to update memory |
| * pressure counters or report the OOM error if necessary. If oomError and |
| * cx is not null the function also reports OOM error. |
| * |
| * The function must be called outside the GC lock and in case of OOM error |
| * the caller must ensure that no deadlock possible during OOM reporting. |
| */ |
| void updateMallocCounter(size_t nbytes); |
| void updateMallocCounter(JS::Zone *zone, size_t nbytes); |
| |
| void reportAllocationOverflow() { js_ReportAllocationOverflow(NULL); } |
| |
| bool isTooMuchMalloc() const { |
| return gcMallocBytes <= 0; |
| } |
| |
| /* |
| * The function must be called outside the GC lock. |
| */ |
| JS_FRIEND_API(void) onTooMuchMalloc(); |
| |
| /* |
| * This should be called after system malloc/realloc returns NULL to try |
| * to recove some memory or to report an error. Failures in malloc and |
| * calloc are signaled by p == null and p == reinterpret_cast<void *>(1). |
| * Other values of p mean a realloc failure. |
| * |
| * The function must be called outside the GC lock. |
| */ |
| JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes); |
| JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); |
| |
| void triggerOperationCallback(); |
| |
| void setJitHardening(bool enabled); |
| bool getJitHardening() const { |
| return jitHardening; |
| } |
| |
| void sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, JS::RuntimeSizes *runtime); |
| |
| private: |
| |
| JSUseHelperThreads useHelperThreads_; |
| int32_t requestedHelperThreadCount; |
| |
| public: |
| |
| bool useHelperThreads() const { |
| #ifdef JS_THREADSAFE |
| return useHelperThreads_ == JS_USE_HELPER_THREADS; |
| #else |
| return false; |
| #endif |
| } |
| |
| void requestHelperThreadCount(size_t count) { |
| requestedHelperThreadCount = count; |
| } |
| |
| /* Number of helper threads which should be created for this runtime. */ |
| size_t helperThreadCount() const { |
| #ifdef JS_THREADSAFE |
| if (requestedHelperThreadCount < 0) |
| return js::GetCPUCount() - 1; |
| return requestedHelperThreadCount; |
| #else |
| return 0; |
| #endif |
| } |
| |
| #ifdef DEBUG |
| public: |
| js::AutoEnterPolicy *enteredPolicy; |
| #endif |
| }; |
| |
| /* Common macros to access thread-local caches in JSRuntime. */ |
| #define JS_KEEP_ATOMS(rt) (rt)->gcKeepAtoms++; |
| #define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--; |
| |
| namespace js { |
| |
| struct AutoResolving; |
| |
| /* |
| * Flags accompany script version data so that a) dynamically created scripts |
| * can inherit their caller's compile-time properties and b) scripts can be |
| * appropriately compared in the eval cache across global option changes. An |
| * example of the latter is enabling the top-level-anonymous-function-is-error |
| * option: subsequent evals of the same, previously-valid script text may have |
| * become invalid. |
| */ |
| namespace VersionFlags { |
| static const unsigned MASK = 0x0FFF; /* see JSVersion in jspubtd.h */ |
| } /* namespace VersionFlags */ |
| |
| static inline JSVersion |
| VersionNumber(JSVersion version) |
| { |
| return JSVersion(uint32_t(version) & VersionFlags::MASK); |
| } |
| |
| static inline JSVersion |
| VersionExtractFlags(JSVersion version) |
| { |
| return JSVersion(uint32_t(version) & ~VersionFlags::MASK); |
| } |
| |
| static inline void |
| VersionCopyFlags(JSVersion *version, JSVersion from) |
| { |
| *version = JSVersion(VersionNumber(*version) | VersionExtractFlags(from)); |
| } |
| |
| static inline bool |
| VersionHasFlags(JSVersion version) |
| { |
| return !!VersionExtractFlags(version); |
| } |
| |
| static inline bool |
| VersionIsKnown(JSVersion version) |
| { |
| return VersionNumber(version) != JSVERSION_UNKNOWN; |
| } |
| |
| inline void |
| FreeOp::free_(void *p) |
| { |
| if (shouldFreeLater()) { |
| runtime()->gcHelperThread.freeLater(p); |
| return; |
| } |
| js_free(p); |
| } |
| |
| class ForkJoinSlice; |
| |
| /* |
| * ThreadSafeContext is the base class for both JSContext, the "normal" |
| * sequential context, and ForkJoinSlice, the per-thread parallel context used |
| * in PJS. |
| * |
| * When cast to a ThreadSafeContext, the only usable operations are casting |
| * back to the context from which it came, and generic allocation |
| * operations. These generic versions branch internally based on whether the |
| * underneath context is really a JSContext or a ForkJoinSlice, and are in |
| * general more expensive than using the context directly. |
| * |
| * Thus, ThreadSafeContext should only be used for VM functions that may be |
| * called in both sequential and parallel execution. The most likely class of |
| * VM functions that do these are those that allocate commonly used data |
| * structures, such as concatenating strings and extending elements. |
| */ |
| struct ThreadSafeContext : js::ContextFriendFields, |
| public MallocProvider<ThreadSafeContext> |
| { |
| public: |
| enum ContextKind { |
| Context_JS, |
| Context_ForkJoin |
| }; |
| |
| private: |
| ContextKind contextKind_; |
| |
| public: |
| PerThreadData *perThreadData; |
| |
| explicit ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind); |
| |
| bool isJSContext() const; |
| JSContext *asJSContext(); |
| |
| bool isForkJoinSlice() const; |
| ForkJoinSlice *asForkJoinSlice(); |
| |
| #ifdef JSGC_GENERATIONAL |
| inline bool hasNursery() const { |
| return isJSContext(); |
| } |
| |
| inline js::Nursery &nursery() { |
| JS_ASSERT(hasNursery()); |
| return runtime_->gcNursery; |
| } |
| #endif |
| |
| /* |
| * Allocator used when allocating GCThings on this context. If we are a |
| * JSContext, this is the Zone allocator of the JSContext's zone. If we |
| * are the per-thread data of a ForkJoinSlice, this is a per-thread |
| * allocator. |
| * |
| * This does not live in PerThreadData because the notion of an allocator |
| * is only per-thread in PJS. The runtime (and the main thread) can have |
| * more than one zone, each with its own allocator, and it's up to the |
| * context to specify what compartment and zone we are operating in. |
| */ |
| protected: |
| Allocator *allocator_; |
| |
| public: |
| static size_t offsetOfAllocator() { return offsetof(ThreadSafeContext, allocator_); } |
| |
| inline Allocator *const allocator(); |
| |
| /* GC support. */ |
| inline AllowGC allowGC() const; |
| |
| template <typename T> |
| inline bool isInsideCurrentZone(T thing) const; |
| |
| void *onOutOfMemory(void *p, size_t nbytes) { |
| return runtime_->onOutOfMemory(p, nbytes, isJSContext() ? asJSContext() : NULL); |
| } |
| inline void updateMallocCounter(size_t nbytes) { |
| /* Note: this is racy. */ |
| runtime_->updateMallocCounter(zone_, nbytes); |
| } |
| void reportAllocationOverflow() { |
| js_ReportAllocationOverflow(isJSContext() ? asJSContext() : NULL); |
| } |
| }; |
| |
| } /* namespace js */ |
| |
| struct JSContext : js::ThreadSafeContext, |
| public mozilla::LinkedListElement<JSContext> |
| { |
| explicit JSContext(JSRuntime *rt); |
| JSContext *thisDuringConstruction() { return this; } |
| ~JSContext(); |
| |
| JSRuntime *runtime() const { return runtime_; } |
| JSCompartment *compartment() const { return compartment_; } |
| |
| inline JS::Zone *zone() const { |
| JS_ASSERT_IF(!compartment(), !zone_); |
| JS_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); |
| return zone_; |
| } |
| js::PerThreadData &mainThread() const { return runtime()->mainThread; } |
| |
| private: |
| /* Exception state -- the exception member is a GC root by definition. */ |
| bool throwing; /* is there a pending exception? */ |
| js::Value exception; /* most-recently-thrown exception */ |
| |
| /* Per-context options. */ |
| unsigned options_; /* see jsapi.h for JSOPTION_* */ |
| |
| public: |
| int32_t reportGranularity; /* see vm/Probes.h */ |
| |
| js::AutoResolving *resolvingList; |
| |
| /* True if generating an error, to prevent runaway recursion. */ |
| bool generatingError; |
| |
| inline void setCompartment(JSCompartment *comp); |
| |
| /* |
| * "Entering" a compartment changes cx->compartment (which changes |
| * cx->global). Note that this does not push any StackFrame which means |
| * that it is possible for cx->fp()->compartment() != cx->compartment. |
| * This is not a problem since, in general, most places in the VM cannot |
| * know that they were called from script (e.g., they may have been called |
| * through the JSAPI via JS_CallFunction) and thus cannot expect fp. |
| * |
| * Compartments should be entered/left in a LIFO fasion. The depth of this |
| * enter/leave stack is maintained by enterCompartmentDepth_ and queried by |
| * hasEnteredCompartment. |
| * |
| * To enter a compartment, code should prefer using AutoCompartment over |
| * manually calling cx->enterCompartment/leaveCompartment. |
| */ |
| private: |
| unsigned enterCompartmentDepth_; |
| public: |
| bool hasEnteredCompartment() const { |
| return enterCompartmentDepth_ > 0; |
| } |
| #ifdef DEBUG |
| unsigned getEnterCompartmentDepth() const { |
| return enterCompartmentDepth_; |
| } |
| #endif |
| |
| inline void enterCompartment(JSCompartment *c); |
| inline void leaveCompartment(JSCompartment *oldCompartment); |
| |
| /* See JS_SaveFrameChain/JS_RestoreFrameChain. */ |
| private: |
| struct SavedFrameChain { |
| SavedFrameChain(JSCompartment *comp, unsigned count) |
| : compartment(comp), enterCompartmentCount(count) {} |
| JSCompartment *compartment; |
| unsigned enterCompartmentCount; |
| }; |
| typedef js::Vector<SavedFrameChain, 1, js::SystemAllocPolicy> SaveStack; |
| SaveStack savedFrameChains_; |
| public: |
| bool saveFrameChain(); |
| void restoreFrameChain(); |
| |
| /* |
| * When no compartments have been explicitly entered, the context's |
| * compartment will be set to the compartment of the "default compartment |
| * object". |
| */ |
| private: |
| JSObject *defaultCompartmentObject_; |
| public: |
| inline void setDefaultCompartmentObject(JSObject *obj); |
| inline void setDefaultCompartmentObjectIfUnset(JSObject *obj); |
| JSObject *maybeDefaultCompartmentObject() const { return defaultCompartmentObject_; } |
| |
| /* |
| * Current global. This is only safe to use within the scope of the |
| * AutoCompartment from which it's called. |
| */ |
| inline js::Handle<js::GlobalObject*> global() const; |
| |
| /* Wrap cx->exception for the current compartment. */ |
| void wrapPendingException(); |
| |
| /* State for object and array toSource conversion. */ |
| js::ObjectSet cycleDetectorSet; |
| |
| /* Per-context optional error reporter. */ |
| JSErrorReporter errorReporter; |
| |
| /* Branch callback. */ |
| JSOperationCallback operationCallback; |
| |
| /* Client opaque pointers. */ |
| void *data; |
| void *data2; |
| |
| inline js::RegExpStatics *regExpStatics(); |
| |
| public: |
| |
| /* |
| * Return: |
| * - The newest scripted frame's version, if there is such a frame. |
| * - The version from the compartment. |
| * - The default version. |
| * |
| * Note: if this ever shows up in a profile, just add caching! |
| */ |
| JSVersion findVersion() const; |
| |
| void setOptions(unsigned opts) { |
| JS_ASSERT((opts & JSOPTION_MASK) == opts); |
| options_ = opts; |
| } |
| |
| unsigned options() const { return options_; } |
| |
| bool hasOption(unsigned opt) const { |
| JS_ASSERT((opt & JSOPTION_MASK) == opt); |
| return !!(options_ & opt); |
| } |
| |
| bool hasExtraWarningsOption() const { return hasOption(JSOPTION_EXTRA_WARNINGS); } |
| bool hasWErrorOption() const { return hasOption(JSOPTION_WERROR); } |
| |
| js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; } |
| inline js::LifoAlloc &analysisLifoAlloc(); |
| inline js::LifoAlloc &typeLifoAlloc(); |
| |
| inline js::PropertyTree &propertyTree(); |
| |
| #ifdef JS_THREADSAFE |
| unsigned outstandingRequests;/* number of JS_BeginRequest calls |
| without the corresponding |
| JS_EndRequest. */ |
| #endif |
| |
| /* Stored here to avoid passing it around as a parameter. */ |
| unsigned resolveFlags; |
| |
| /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ |
| js::Value iterValue; |
| |
| bool jitIsBroken; |
| |
| inline bool typeInferenceEnabled() const; |
| |
| void updateJITEnabled(); |
| |
| /* Whether this context has JS frames on the stack. */ |
| bool currentlyRunning() const; |
| |
| bool currentlyRunningInInterpreter() const { |
| return mainThread().activation()->isInterpreter(); |
| } |
| bool currentlyRunningInJit() const { |
| return mainThread().activation()->isJit(); |
| } |
| js::StackFrame *interpreterFrame() const { |
| return mainThread().activation()->asInterpreter()->current(); |
| } |
| js::FrameRegs &interpreterRegs() const { |
| return mainThread().activation()->asInterpreter()->regs(); |
| } |
| |
| /* |
| * Get the topmost script and optional pc on the stack. By default, this |
| * function only returns a JSScript in the current compartment, returning |
| * NULL if the current script is in a different compartment. This behavior |
| * can be overridden by passing ALLOW_CROSS_COMPARTMENT. |
| */ |
| enum MaybeAllowCrossCompartment { |
| DONT_ALLOW_CROSS_COMPARTMENT = false, |
| ALLOW_CROSS_COMPARTMENT = true |
| }; |
| inline JSScript *currentScript(jsbytecode **pc = NULL, |
| MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const; |
| |
| #ifdef MOZ_TRACE_JSCALLS |
| /* Function entry/exit debugging callback. */ |
| JSFunctionCallback functionCallback; |
| |
| void doFunctionCallback(const JSFunction *fun, |
| const JSScript *scr, |
| int entering) const |
| { |
| if (functionCallback) |
| functionCallback(fun, scr, this, entering); |
| } |
| #endif |
| |
| private: |
| /* Innermost-executing generator or null if no generator are executing. */ |
| JSGenerator *innermostGenerator_; |
| public: |
| JSGenerator *innermostGenerator() const { return innermostGenerator_; } |
| void enterGenerator(JSGenerator *gen); |
| void leaveGenerator(JSGenerator *gen); |
| |
| void *onOutOfMemory(void *p, size_t nbytes) { |
| return runtime()->onOutOfMemory(p, nbytes, this); |
| } |
| void updateMallocCounter(size_t nbytes) { |
| runtime()->updateMallocCounter(zone(), nbytes); |
| } |
| void reportAllocationOverflow() { |
| js_ReportAllocationOverflow(this); |
| } |
| |
| bool isExceptionPending() { |
| return throwing; |
| } |
| |
| js::Value getPendingException() { |
| JS_ASSERT(throwing); |
| return exception; |
| } |
| |
| void setPendingException(js::Value v); |
| |
| void clearPendingException() { |
| throwing = false; |
| exception.setUndefined(); |
| } |
| |
| JSAtomState & names() { return runtime()->atomState; } |
| |
| #ifdef DEBUG |
| /* |
| * Controls whether a quadratic-complexity assertion is performed during |
| * stack iteration; defaults to true. |
| */ |
| bool stackIterAssertionEnabled; |
| #endif |
| |
| /* |
| * See JS_SetTrustedPrincipals in jsapi.h. |
| * Note: !cx->compartment is treated as trusted. |
| */ |
| bool runningWithTrustedPrincipals() const; |
| |
| JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; |
| |
| void mark(JSTracer *trc); |
| |
| private: |
| /* |
| * The allocation code calls the function to indicate either OOM failure |
| * when p is null or that a memory pressure counter has reached some |
| * threshold when p is not null. The function takes the pointer and not |
| * a boolean flag to minimize the amount of code in its inlined callers. |
| */ |
| JS_FRIEND_API(void) checkMallocGCPressure(void *p); |
| }; /* struct JSContext */ |
| |
| namespace js { |
| |
| struct AutoResolving { |
| public: |
| enum Kind { |
| LOOKUP, |
| WATCH |
| }; |
| |
| AutoResolving(JSContext *cx, HandleObject obj, HandleId id, Kind kind = LOOKUP |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| JS_ASSERT(obj); |
| cx->resolvingList = this; |
| } |
| |
| ~AutoResolving() { |
| JS_ASSERT(context->resolvingList == this); |
| context->resolvingList = link; |
| } |
| |
| bool alreadyStarted() const { |
| return link && alreadyStartedSlow(); |
| } |
| |
| private: |
| bool alreadyStartedSlow() const; |
| |
| JSContext *const context; |
| HandleObject object; |
| HandleId id; |
| Kind const kind; |
| AutoResolving *const link; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| #ifdef JS_THREADSAFE |
| # define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock) |
| # define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock) |
| #else |
| # define JS_LOCK_GC(rt) do { } while (0) |
| # define JS_UNLOCK_GC(rt) do { } while (0) |
| #endif |
| |
| class AutoLockGC |
| { |
| public: |
| explicit AutoLockGC(JSRuntime *rt = NULL |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : runtime(rt) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| // Avoid MSVC warning C4390 for non-threadsafe builds. |
| if (rt) |
| JS_LOCK_GC(rt); |
| } |
| |
| ~AutoLockGC() |
| { |
| if (runtime) |
| JS_UNLOCK_GC(runtime); |
| } |
| |
| bool locked() const { |
| return !!runtime; |
| } |
| |
| void lock(JSRuntime *rt) { |
| JS_ASSERT(rt); |
| JS_ASSERT(!runtime); |
| runtime = rt; |
| JS_LOCK_GC(rt); |
| } |
| |
| private: |
| JSRuntime *runtime; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoUnlockGC |
| { |
| private: |
| #ifdef JS_THREADSAFE |
| JSRuntime *rt; |
| #endif |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| |
| public: |
| explicit AutoUnlockGC(JSRuntime *rt |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| #ifdef JS_THREADSAFE |
| : rt(rt) |
| #endif |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| JS_UNLOCK_GC(rt); |
| } |
| ~AutoUnlockGC() { JS_LOCK_GC(rt); } |
| }; |
| |
| class MOZ_STACK_CLASS AutoKeepAtoms |
| { |
| JSRuntime *rt; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| |
| public: |
| explicit AutoKeepAtoms(JSRuntime *rt |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : rt(rt) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| JS_KEEP_ATOMS(rt); |
| } |
| ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } |
| }; |
| |
| // Maximum supported value of arguments.length. This bounds the maximum |
| // number of arguments that can be supplied to Function.prototype.apply. |
| // This value also bounds the number of elements parsed in an array |
| // initialiser. |
| static const unsigned ARGS_LENGTH_MAX = 500 * 1000; |
| |
| } /* namespace js */ |
| |
| class JSAutoResolveFlags |
| { |
| public: |
| JSAutoResolveFlags(JSContext *cx, unsigned flags |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : mContext(cx), mSaved(cx->resolveFlags) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| cx->resolveFlags = flags; |
| } |
| |
| ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } |
| |
| private: |
| JSContext *mContext; |
| unsigned mSaved; |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| namespace js { |
| |
| /* |
| * Enumerate all contexts in a runtime. |
| */ |
| class ContextIter { |
| JSContext *iter; |
| |
| public: |
| explicit ContextIter(JSRuntime *rt) { |
| iter = rt->contextList.getFirst(); |
| } |
| |
| bool done() const { |
| return !iter; |
| } |
| |
| void next() { |
| JS_ASSERT(!done()); |
| iter = iter->getNext(); |
| } |
| |
| JSContext *get() const { |
| JS_ASSERT(!done()); |
| return iter; |
| } |
| |
| operator JSContext *() const { |
| return get(); |
| } |
| |
| JSContext *operator ->() const { |
| return get(); |
| } |
| }; |
| |
| /* |
| * Create and destroy functions for JSContext, which is manually allocated |
| * and exclusively owned. |
| */ |
| extern JSContext * |
| NewContext(JSRuntime *rt, size_t stackChunkSize); |
| |
| enum DestroyContextMode { |
| DCM_NO_GC, |
| DCM_FORCE_GC, |
| DCM_NEW_FAILED |
| }; |
| |
| extern void |
| DestroyContext(JSContext *cx, DestroyContextMode mode); |
| |
| enum ErrorArgumentsType { |
| ArgumentsAreUnicode, |
| ArgumentsAreASCII |
| }; |
| |
| inline void |
| PerThreadData::setIonStackLimit(uintptr_t limit) |
| { |
| JS_ASSERT(runtime_->currentThreadOwnsOperationCallbackLock()); |
| ionStackLimit = limit; |
| } |
| |
| } /* namespace js */ |
| |
| #ifdef va_start |
| extern JSBool |
| js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap); |
| |
| extern JSBool |
| js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback, |
| void *userRef, const unsigned errorNumber, |
| js::ErrorArgumentsType argumentsType, va_list ap); |
| |
| extern bool |
| js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback, |
| void *userRef, const unsigned errorNumber, |
| const jschar **args); |
| |
| extern JSBool |
| js_ExpandErrorArgumentsVA(JSContext *cx, JSErrorCallback callback, |
| void *userRef, const unsigned errorNumber, |
| char **message, JSErrorReport *reportp, |
| js::ErrorArgumentsType argumentsType, va_list ap); |
| #endif |
| |
| namespace js { |
| |
| /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ |
| extern void |
| ReportUsageError(JSContext *cx, HandleObject callee, const char *msg); |
| |
| /* |
| * Prints a full report and returns true if the given report is non-NULL and |
| * the report doesn't have the JSREPORT_WARNING flag set or reportWarnings is |
| * true. |
| * Returns false otherwise, printing just the message if the report is NULL. |
| */ |
| extern bool |
| PrintError(JSContext *cx, FILE *file, const char *message, JSErrorReport *report, |
| bool reportWarnings); |
| } /* namespace js */ |
| |
| /* |
| * Report an exception using a previously composed JSErrorReport. |
| * XXXbe remove from "friend" API |
| */ |
| extern JS_FRIEND_API(void) |
| js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report); |
| |
| extern void |
| js_ReportIsNotDefined(JSContext *cx, const char *name); |
| |
| /* |
| * Report an attempt to access the property of a null or undefined value (v). |
| */ |
| extern JSBool |
| js_ReportIsNullOrUndefined(JSContext *cx, int spindex, js::HandleValue v, |
| js::HandleString fallback); |
| |
| extern void |
| js_ReportMissingArg(JSContext *cx, js::HandleValue v, unsigned arg); |
| |
| /* |
| * Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as |
| * the first argument for the error message. If the error message has less |
| * then 3 arguments, use null for arg1 or arg2. |
| */ |
| extern JSBool |
| js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber, |
| int spindex, js::HandleValue v, js::HandleString fallback, |
| const char *arg1, const char *arg2); |
| |
| #define js_ReportValueError(cx,errorNumber,spindex,v,fallback) \ |
| ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, NULL, NULL)) |
| |
| #define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \ |
| ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, arg1, NULL)) |
| |
| #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ |
| ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ |
| spindex, v, fallback, arg1, arg2)) |
| |
| extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; |
| |
| char * |
| js_strdup(JSContext *cx, const char *s); |
| |
| #ifdef JS_THREADSAFE |
| # define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime()->requestDepth >= 1) |
| #else |
| # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) |
| #endif |
| |
| /* |
| * Invoke the operation callback and return false if the current execution |
| * is to be terminated. |
| */ |
| extern JSBool |
| js_InvokeOperationCallback(JSContext *cx); |
| |
| extern JSBool |
| js_HandleExecutionInterrupt(JSContext *cx); |
| |
| /* |
| * If the operation callback flag was set, call the operation callback. |
| * This macro can run the full GC. Return true if it is OK to continue and |
| * false otherwise. |
| */ |
| static MOZ_ALWAYS_INLINE bool |
| JS_CHECK_OPERATION_LIMIT(JSContext *cx) |
| { |
| JS_ASSERT_REQUEST_DEPTH(cx); |
| return !cx->runtime()->interrupt || js_InvokeOperationCallback(cx); |
| } |
| |
| namespace js { |
| |
| /************************************************************************/ |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(Value *vec, size_t len) |
| { |
| mozilla::PodZero(vec, len); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(Value *beg, Value *end) |
| { |
| mozilla::PodZero(beg, end - beg); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(jsid *beg, jsid *end) |
| { |
| for (jsid *id = beg; id != end; ++id) |
| *id = INT_TO_JSID(0); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(jsid *vec, size_t len) |
| { |
| MakeRangeGCSafe(vec, vec + len); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(Shape **beg, Shape **end) |
| { |
| mozilla::PodZero(beg, end - beg); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MakeRangeGCSafe(Shape **vec, size_t len) |
| { |
| mozilla::PodZero(vec, len); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| SetValueRangeToUndefined(Value *beg, Value *end) |
| { |
| for (Value *v = beg; v != end; ++v) |
| v->setUndefined(); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| SetValueRangeToUndefined(Value *vec, size_t len) |
| { |
| SetValueRangeToUndefined(vec, vec + len); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| SetValueRangeToNull(Value *beg, Value *end) |
| { |
| for (Value *v = beg; v != end; ++v) |
| v->setNull(); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| SetValueRangeToNull(Value *vec, size_t len) |
| { |
| SetValueRangeToNull(vec, vec + len); |
| } |
| |
| class AutoStringVector : public AutoVectorRooter<JSString *> |
| { |
| public: |
| explicit AutoStringVector(JSContext *cx |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoVectorRooter<JSString *>(cx, STRINGVECTOR) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoShapeVector : public AutoVectorRooter<Shape *> |
| { |
| public: |
| explicit AutoShapeVector(JSContext *cx |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoVectorRooter<Shape *>(cx, SHAPEVECTOR) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoValueArray : public AutoGCRooter |
| { |
| Value *start_; |
| unsigned length_; |
| SkipRoot skip; |
| |
| public: |
| AutoValueArray(JSContext *cx, Value *start, unsigned length |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoGCRooter(cx, VALARRAY), start_(start), length_(length), skip(cx, start, length) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| Value *start() { return start_; } |
| unsigned length() const { return length_; } |
| |
| MutableHandleValue handleAt(unsigned i) |
| { |
| JS_ASSERT(i < length_); |
| return MutableHandleValue::fromMarkedLocation(&start_[i]); |
| } |
| HandleValue handleAt(unsigned i) const |
| { |
| JS_ASSERT(i < length_); |
| return HandleValue::fromMarkedLocation(&start_[i]); |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoObjectObjectHashMap : public AutoHashMapRooter<JSObject *, JSObject *> |
| { |
| public: |
| explicit AutoObjectObjectHashMap(JSContext *cx |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoHashMapRooter<JSObject *, JSObject *>(cx, OBJOBJHASHMAP) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoObjectUnsigned32HashMap : public AutoHashMapRooter<JSObject *, uint32_t> |
| { |
| public: |
| explicit AutoObjectUnsigned32HashMap(JSContext *cx |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoHashMapRooter<JSObject *, uint32_t>(cx, OBJU32HASHMAP) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoObjectHashSet : public AutoHashSetRooter<JSObject *> |
| { |
| public: |
| explicit AutoObjectHashSet(JSContext *cx |
| MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : AutoHashSetRooter<JSObject *>(cx, OBJHASHSET) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| class AutoAssertNoException |
| { |
| #ifdef DEBUG |
| JSContext *cx; |
| bool hadException; |
| #endif |
| |
| public: |
| AutoAssertNoException(JSContext *cx) |
| #ifdef DEBUG |
| : cx(cx), |
| hadException(cx->isExceptionPending()) |
| #endif |
| { |
| } |
| |
| ~AutoAssertNoException() |
| { |
| JS_ASSERT_IF(!hadException, !cx->isExceptionPending()); |
| } |
| }; |
| |
| /* |
| * Allocation policy that uses JSRuntime::malloc_ and friends, so that |
| * memory pressure is properly accounted for. This is suitable for |
| * long-lived objects owned by the JSRuntime. |
| * |
| * Since it doesn't hold a JSContext (those may not live long enough), it |
| * can't report out-of-memory conditions itself; the caller must check for |
| * OOM and take the appropriate action. |
| * |
| * FIXME bug 647103 - replace these *AllocPolicy names. |
| */ |
| class RuntimeAllocPolicy |
| { |
| JSRuntime *const runtime; |
| |
| public: |
| RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {} |
| RuntimeAllocPolicy(JSContext *cx) : runtime(cx->runtime()) {} |
| void *malloc_(size_t bytes) { return runtime->malloc_(bytes); } |
| void *calloc_(size_t bytes) { return runtime->calloc_(bytes); } |
| void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); } |
| void free_(void *p) { js_free(p); } |
| void reportAllocOverflow() const {} |
| }; |
| |
| /* |
| * FIXME bug 647103 - replace these *AllocPolicy names. |
| */ |
| class ContextAllocPolicy |
| { |
| JSContext *const cx_; |
| |
| public: |
| ContextAllocPolicy(JSContext *cx) : cx_(cx) {} |
| JSContext *context() const { return cx_; } |
| void *malloc_(size_t bytes) { return cx_->malloc_(bytes); } |
| void *calloc_(size_t bytes) { return cx_->calloc_(bytes); } |
| void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); } |
| void free_(void *p) { js_free(p); } |
| void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); } |
| }; |
| |
| /* Exposed intrinsics so that Ion may inline them. */ |
| JSBool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); |
| |
| JSBool intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_NewObjectWithClassPrototype(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp); |
| |
| JSBool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); |
| JSBool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp); |
| |
| #ifdef DEBUG |
| JSBool intrinsic_Dump(JSContext *cx, unsigned argc, Value *vp); |
| #endif |
| |
| } /* namespace js */ |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #pragma warning(pop) |
| #endif |
| |
| #endif /* jscntxt_h */ |