/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jscntxt_h___ #define jscntxt_h___ /* * JS execution context. */ #include #include "jsprvtd.h" #include "jsarena.h" #include "jsclist.h" #include "jslong.h" #include "jsatom.h" #include "jsdhash.h" #include "jsdtoa.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcchunk.h" #include "jshashtable.h" #include "jsinterp.h" #include "jsmath.h" #include "jsobj.h" #include "jspropertycache.h" #include "jspropertytree.h" #include "jsstaticcheck.h" #include "jsutil.h" #include "jsarray.h" #include "jsvector.h" #include "prmjtime.h" #include "vm/Stack.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 /* Forward declarations of nanojit types. */ namespace nanojit { class Assembler; class CodeAlloc; class Fragment; template struct DefaultHash; template class HashMap; template class Seq; } /* namespace nanojit */ namespace js { /* Tracer constants. */ static const size_t MONITOR_N_GLOBAL_STATES = 4; static const size_t FRAGMENT_TABLE_SIZE = 512; static const size_t MAX_GLOBAL_SLOTS = 4096; static const size_t GLOBAL_SLOTS_BUFFER_SIZE = MAX_GLOBAL_SLOTS + 1; /* Forward declarations of tracer types. */ class VMAllocator; class FrameInfoCache; struct FrameInfo; struct VMSideExit; struct TreeFragment; struct TracerState; template class Queue; typedef Queue SlotList; class TypeMap; class LoopProfile; #if defined(JS_JIT_SPEW) || defined(DEBUG) struct FragPI; typedef nanojit::HashMap > FragStatsMap; #endif namespace mjit { class JaegerCompartment; } /* * 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, SystemAllocPolicy> Map; jsbytecode *code; Map map; #ifdef JS_GSNMETER struct Stats { uint32 hits; uint32 misses; uint32 fills; uint32 purges; Stats() : hits(0), misses(0), fills(0), purges(0) { } }; Stats stats; #endif GSNCache() : code(NULL) { } void purge(); }; inline GSNCache * GetGSNCache(JSContext *cx); struct PendingProxyOperation { PendingProxyOperation *next; JSObject *object; }; struct ThreadData { /* * If non-zero, we were been asked to call the operation callback as soon * as possible. If the thread has an active request, this contributes * towards rt->interruptCounter. */ volatile int32 interruptFlags; #ifdef JS_THREADSAFE /* The request depth for this thread. */ unsigned requestDepth; #endif #ifdef JS_TRACER /* * During trace execution (or during trace recording or * profiling), these fields point to the compartment doing the * execution on this thread. At other times, they are NULL. If a * thread tries to execute/record/profile one trace while another * is still running, the initial one will abort. Therefore, we * only need to track one at a time. */ JSCompartment *onTraceCompartment; JSCompartment *recordingCompartment; JSCompartment *profilingCompartment; /* Maximum size of the tracer's code cache before we start flushing. */ uint32 maxCodeCacheBytes; static const uint32 DEFAULT_JIT_CACHE_SIZE = 16 * 1024 * 1024; #endif /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; /* * Flag indicating that we are waiving any soft limits on the GC heap * because we want allocations to be infallible (except when we hit OOM). */ bool waiveGCQuota; /* * The GSN cache is per thread since even multi-cx-per-thread embeddings * do not interleave js_GetSrcNote calls. */ GSNCache gsnCache; /* Property cache for faster call/get/set invocation. */ PropertyCache propertyCache; /* State used by dtoa.c. */ DtoaState *dtoaState; /* Base address of the native stack for the current thread. */ jsuword *nativeStackBase; /* List of currently pending operations on proxies. */ PendingProxyOperation *pendingProxyOperation; ConservativeGCThreadData conservativeGC; ThreadData(); ~ThreadData(); bool init(); void mark(JSTracer *trc) { stackSpace.mark(trc); } void purge(JSContext *cx) { gsnCache.purge(); /* FIXME: bug 506341. */ propertyCache.purge(cx); } /* This must be called with the GC lock held. */ void triggerOperationCallback(JSRuntime *rt); }; } /* namespace js */ #ifdef JS_THREADSAFE /* * Structure uniquely representing a thread. It holds thread-private data * that can be accessed without a global lock. */ struct JSThread { typedef js::HashMap, js::SystemAllocPolicy> Map; /* Linked list of all contexts in use on this thread. */ JSCList contextList; /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ void *id; /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */ unsigned suspendCount; # ifdef DEBUG unsigned checkRequestDepth; # endif /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ js::ThreadData data; JSThread(void *id) : id(id), suspendCount(0) # ifdef DEBUG , checkRequestDepth(0) # endif { JS_INIT_CLIST(&contextList); } ~JSThread() { /* The thread must have zero contexts. */ JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList)); } bool init() { return data.init(); } }; #define JS_THREAD_DATA(cx) (&(cx)->thread()->data) extern JSThread * js_CurrentThread(JSRuntime *rt); /* * The function takes the GC lock and does not release in successful return. * On error (out of memory) the function releases the lock but delegates * the error reporting to the caller. */ extern JSBool js_InitContextThread(JSContext *cx); /* * On entrance the GC lock must be held and it will be held on exit. */ extern void js_ClearContextThread(JSContext *cx); #endif /* JS_THREADSAFE */ #ifdef DEBUG # define FUNCTION_KIND_METER_LIST(_) \ _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \ _(flat), _(badfunarg), \ _(joinedsetmethod), _(joinedinitmethod), \ _(joinedreplace), _(joinedsort), _(joinedmodulepat), \ _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \ _(unjoined), _(indynamicscope) # define identity(x) x struct JSFunctionMeter { int32 FUNCTION_KIND_METER_LIST(identity); }; # undef identity # define JS_FUNCTION_METER(cx,x) JS_RUNTIME_METER((cx)->runtime, functionMeter.x) #else # define JS_FUNCTION_METER(cx,x) ((void)0) #endif typedef enum JSDestroyContextMode { JSDCM_NO_GC, JSDCM_MAYBE_GC, JSDCM_FORCE_GC, JSDCM_NEW_FAILED } JSDestroyContextMode; typedef enum JSRuntimeState { JSRTS_DOWN, JSRTS_LAUNCHING, JSRTS_UP, JSRTS_LANDING } JSRuntimeState; typedef struct JSPropertyTreeEntry { JSDHashEntryHdr hdr; js::Shape *child; } JSPropertyTreeEntry; typedef void (* JSActivityCallback)(void *arg, JSBool active); namespace js { typedef js::Vector CompartmentVector; } struct JSRuntime { /* Default compartment. */ JSCompartment *atomsCompartment; #ifdef JS_THREADSAFE bool atomsCompartmentIsLocked; #endif /* List of compartments (protected by the GC lock). */ js::CompartmentVector compartments; /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ JSRuntimeState state; /* Context create/destroy callback. */ JSContextCallback cxCallback; /* Compartment create/destroy callback. */ JSCompartmentCallback compartmentCallback; /* * Sets a callback that is run whenever the runtime goes idle - the * last active request ceases - and begins activity - when it was * idle and a request begins. Note: The callback is called under the * GC lock. */ void setActivityCallback(JSActivityCallback cb, void *arg) { activityCallback = cb; activityCallbackArg = arg; } JSActivityCallback activityCallback; void *activityCallbackArg; /* * Shape regenerated whenever a prototype implicated by an "add property" * property cache fill and induced trace guard has a readonly property or a * setter defined on it. This number proxies for the shapes of all objects * along the prototype chain of all objects in the runtime on which such an * add-property result has been cached/traced. * * See bug 492355 for more details. * * This comes early in JSRuntime to minimize the immediate format used by * trace-JITted code that reads it. */ uint32 protoHazardShape; /* Garbage collector state, used by jsgc.c. */ js::GCChunkSet gcChunkSet; js::RootedValueMap gcRootsHash; js::GCLocks gcLocksHash; jsrefcount gcKeepAtoms; uint32 gcBytes; uint32 gcTriggerBytes; size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; int64 gcJitReleaseTime; JSGCMode gcMode; volatile bool gcIsNeeded; JSObject *gcWeakMapList; /* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */ void *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE / sizeof(void *)]; void *gcMarkStackXMLs[js::XML_MARK_STACK_SIZE / sizeof(void *)]; void *gcMarkStackLarges[js::LARGE_MARK_STACK_SIZE / sizeof(void *)]; /* * Compartment that triggered GC. If more than one Compatment need GC, * gcTriggerCompartment is reset to NULL and a global GC is performed. */ JSCompartment *gcTriggerCompartment; /* Compartment that is currently involved in per-compartment GC */ JSCompartment *gcCurrentCompartment; /* * We can pack these flags as only the GC thread writes to them. Atomic * updates to packed bytes are not guaranteed, so stores issued by one * thread may be lost due to unsynchronized read-modify-write cycles on * other threads. */ bool gcPoke; bool gcMarkAndSweep; bool gcRunning; bool gcRegenShapes; #ifdef JS_GC_ZEAL jsrefcount gcZeal; #endif JSGCCallback gcCallback; private: /* * Malloc counter to measure memory pressure for GC scheduling. It runs * from gcMaxMallocBytes down to zero. */ volatile ptrdiff_t gcMallocBytes; public: js::GCChunkAllocator *gcChunkAllocator; void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) { JS_ASSERT(allocator); JS_ASSERT(state == JSRTS_DOWN); gcChunkAllocator = allocator; } /* * The trace operation and its data argument to trace embedding-specific * GC roots. */ JSTraceDataOp gcExtraRootsTraceOp; void *gcExtraRootsData; /* Well-known numbers held for use by this runtime's contexts. */ js::Value NaNValue; js::Value negativeInfinityValue; js::Value positiveInfinityValue; JSFlatString *emptyString; /* List of active contexts sharing this runtime; protected by gcLock. */ JSCList contextList; /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; /* * Right now, we only support runtime-wide debugging. */ JSBool debugMode; #ifdef JS_TRACER /* True if any debug hooks not supported by the JIT are enabled. */ bool debuggerInhibitsJIT() const { return (globalDebugHooks.interruptHook || globalDebugHooks.callHook); } #endif /* More debugging state, see jsdbgapi.c. */ JSCList trapList; JSCList watchPointList; /* Client opaque pointers */ void *data; #ifdef JS_THREADSAFE /* These combine to interlock the GC and new requests. */ PRLock *gcLock; PRCondVar *gcDone; PRCondVar *requestDone; uint32 requestCount; JSThread *gcThread; js::GCHelperThread gcHelperThread; /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ PRLock *rtLock; #ifdef DEBUG void * rtLockOwner; #endif /* Used to synchronize down/up state change; protected by gcLock. */ PRCondVar *stateChange; /* * Lock serializing trapList and watchPointList accesses, and count of all * mutations to trapList and watchPointList made by debugger threads. To * keep the code simple, we define debuggerMutations for the thread-unsafe * case too. */ PRLock *debuggerLock; JSThread::Map threads; #endif /* JS_THREADSAFE */ uint32 debuggerMutations; /* * Security callbacks set on the runtime are used by each context unless * an override is set on the context. */ JSSecurityCallbacks *securityCallbacks; /* Structured data callbacks are runtime-wide. */ const JSStructuredCloneCallbacks *structuredCloneCallbacks; /* * 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. */ int32 propertyRemovals; /* Script filename table. */ struct JSHashTable *scriptFilenameTable; #ifdef JS_THREADSAFE PRLock *scriptFilenameTableLock; #endif /* Number localization, used by jsnum.c */ const char *thousandsSeparator; const char *decimalSeparator; const char *numGrouping; /* * Weak references to lazily-created, well-known XML singletons. * * NB: Singleton objects must be carefully disconnected from the rest of * the object graph usually associated with a JSContext's global object, * including the set of standard class objects. See jsxml.c for details. */ JSObject *anynameObject; JSObject *functionNamespaceObject; #ifdef JS_THREADSAFE /* Number of threads with active requests and unhandled interrupts. */ volatile int32 interruptCounter; #else js::ThreadData threadData; #define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData) #endif /* * Object shape (property cache structural type) identifier generator. * * Type 0 stands for the empty scope, and must not be regenerated due to * uint32 wrap-around. Since js_GenerateShape (in jsinterp.cpp) uses * atomic pre-increment, the initial value for the first typed non-empty * scope will be 1. * * If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the * cache is disabled, to avoid aliasing two different types. It stays * disabled until a triggered GC at some later moment compresses live * types, minimizing rt->shapeGen in the process. */ volatile uint32 shapeGen; /* Literal table maintained by jsatom.c functions. */ JSAtomState atomState; /* * Various metering fields are defined at the end of JSRuntime. In this * way there is no need to recompile all the code that refers to other * fields of JSRuntime after enabling the corresponding metering macro. */ #ifdef JS_DUMP_ENUM_CACHE_STATS int32 nativeEnumProbes; int32 nativeEnumMisses; # define ENUM_CACHE_METER(name) JS_ATOMIC_INCREMENT(&cx->runtime->name) #else # define ENUM_CACHE_METER(name) ((void) 0) #endif #ifdef DEBUG /* Function invocation metering. */ jsrefcount inlineCalls; jsrefcount nativeCalls; jsrefcount nonInlineCalls; jsrefcount constructs; /* * NB: emptyShapes (in JSCompartment) is init'ed iff at least one * of these envars is set: * * JS_PROPTREE_STATFILE statistics on the property tree forest * JS_PROPTREE_DUMPFILE all paths in the property tree forest */ const char *propTreeStatFilename; const char *propTreeDumpFilename; bool meterEmptyShapes() const { return propTreeStatFilename || propTreeDumpFilename; } /* String instrumentation. */ jsrefcount liveStrings; jsrefcount totalStrings; jsrefcount liveDependentStrings; jsrefcount totalDependentStrings; jsrefcount badUndependStrings; double lengthSum; double lengthSquaredSum; double strdepLengthSum; double strdepLengthSquaredSum; /* Script instrumentation. */ jsrefcount liveScripts; jsrefcount totalScripts; jsrefcount liveEmptyScripts; jsrefcount totalEmptyScripts; jsrefcount highWaterLiveScripts; #endif /* DEBUG */ #ifdef JS_SCOPE_DEPTH_METER /* * Stats on runtime prototype chain lookups and scope chain depths, i.e., * counts of objects traversed on a chain until the wanted id is found. */ JSBasicStats protoLookupDepthStats; JSBasicStats scopeSearchDepthStats; /* * Stats on compile-time host environment and lexical scope chain lengths * (maximum depths). */ JSBasicStats hostenvScopeDepthStats; JSBasicStats lexicalScopeDepthStats; #endif #ifdef JS_GCMETER js::gc::JSGCStats gcStats; js::gc::JSGCArenaStats globalArenaStats[js::gc::FINALIZE_LIMIT]; #endif #ifdef DEBUG /* * If functionMeterFilename, set from an envariable in JSRuntime's ctor, is * null, the remaining members in this ifdef'ed group are not initialized. */ const char *functionMeterFilename; JSFunctionMeter functionMeter; char lastScriptFilename[1024]; typedef js::HashMap, js::SystemAllocPolicy> FunctionCountMap; FunctionCountMap methodReadBarrierCountMap; FunctionCountMap unjoinedFunctionCountMap; #endif JSWrapObjectCallback wrapObjectCallback; JSPreWrapCallback preWrapObjectCallback; #ifdef JS_METHODJIT /* This measures the size of JITScripts, native maps and IC structs. */ size_t mjitDataSize; #endif /* * To ensure that cx->malloc does not cause a GC, we set this flag during * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while * reporting the OOM, we ignore it. */ bool inOOMReport; #if defined(MOZ_GCTIMER) || defined(JSGC_TESTPILOT) struct GCData { /* * Timestamp of the first GCTimer -- application runtime is determined * relative to this value. */ uint64 firstEnter; bool firstEnterValid; void setFirstEnter(uint64 v) { JS_ASSERT(!firstEnterValid); firstEnter = v; firstEnterValid = true; } #ifdef JSGC_TESTPILOT bool infoEnabled; bool isTimerEnabled() { return infoEnabled; } /* * Circular buffer with GC data. * count may grow >= INFO_LIMIT, which would indicate data loss. */ static const size_t INFO_LIMIT = 64; JSGCInfo info[INFO_LIMIT]; size_t start; size_t count; #else /* defined(MOZ_GCTIMER) */ bool isTimerEnabled() { return true; } #endif } gcData; #endif JSRuntime(); ~JSRuntime(); bool init(uint32 maxbytes); void setGCLastBytes(size_t lastBytes); void reduceGCTriggerBytes(uint32 amount); /* * Call the system malloc while checking for GC memory pressure and * reporting OOM error when cx is not null. We will not GC from here. */ void* malloc_(size_t bytes, JSContext *cx = NULL) { updateMallocCounter(bytes); void *p = ::js_malloc(bytes); return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx); } /* * Call the system calloc while checking for GC memory pressure and * reporting OOM error when cx is not null. We will not GC from here. */ void* calloc_(size_t bytes, JSContext *cx = NULL) { updateMallocCounter(bytes); void *p = ::js_calloc(bytes); return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast(1), bytes, cx); } void* realloc_(void* p, size_t oldBytes, size_t newBytes, JSContext *cx = NULL) { JS_ASSERT(oldBytes < newBytes); updateMallocCounter(newBytes - oldBytes); void *p2 = ::js_realloc(p, newBytes); return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx); } void* realloc_(void* p, size_t bytes, JSContext *cx = NULL) { /* * For compatibility we do not account for realloc that increases * previously allocated memory. */ if (!p) updateMallocCounter(bytes); void *p2 = ::js_realloc(p, bytes); return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx); } inline void free_(void* p) { /* FIXME: Making this free in the background is buggy. Can it work? */ js::Foreground::free_(p); } JS_DECLARE_NEW_METHODS(malloc_, JS_ALWAYS_INLINE) JS_DECLARE_DELETE_METHODS(free_, JS_ALWAYS_INLINE) bool isGCMallocLimitReached() const { return gcMallocBytes <= 0; } void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); } void setGCMaxMallocBytes(size_t value) { /* * For compatibility treat any value that exceeds PTRDIFF_T_MAX to * mean that value. */ gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; resetGCMallocBytes(); } /* * 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) { /* We tolerate any thread races when updating gcMallocBytes. */ ptrdiff_t newCount = gcMallocBytes - ptrdiff_t(nbytes); gcMallocBytes = newCount; if (JS_UNLIKELY(newCount <= 0)) onTooMuchMalloc(); } /* * 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(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, JSContext *cx); }; /* Common macros to access thread-local caches in JSThread or JSRuntime. */ #define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) #ifdef DEBUG # define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) # define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) #else # define JS_RUNTIME_METER(rt, which) /* nothing */ # define JS_RUNTIME_UNMETER(rt, which) /* nothing */ #endif #define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); #define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to * formatter functions. Elements are sorted in non-increasing format string * length order. */ struct JSArgumentFormatMap { const char *format; size_t length; JSArgumentFormatter formatter; JSArgumentFormatMap *next; }; #endif extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */ namespace js { class AutoGCRooter; struct AutoResolving; static inline bool OptionsHasXML(uint32 options) { return !!(options & JSOPTION_XML); } static inline bool OptionsHasAnonFunFix(uint32 options) { return !!(options & JSOPTION_ANONFUNFIX); } static inline bool OptionsSameVersionFlags(uint32 self, uint32 other) { static const uint32 mask = JSOPTION_XML | JSOPTION_ANONFUNFIX; return !((self & mask) ^ (other & mask)); } /* * 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 uintN MASK = 0x0FFF; /* see JSVersion in jspubtd.h */ static const uintN HAS_XML = 0x1000; /* flag induced by XML option */ static const uintN ANONFUNFIX = 0x2000; /* see jsapi.h comment on JSOPTION_ANONFUNFIX */ static const uintN FULL_MASK = 0x3FFF; } static inline JSVersion VersionNumber(JSVersion version) { return JSVersion(uint32(version) & VersionFlags::MASK); } static inline bool VersionHasXML(JSVersion version) { return !!(version & VersionFlags::HAS_XML); } /* @warning This is a distinct condition from having the XML flag set. */ static inline bool VersionShouldParseXML(JSVersion version) { return VersionHasXML(version) || VersionNumber(version) >= JSVERSION_1_6; } static inline bool VersionHasAnonFunFix(JSVersion version) { return !!(version & VersionFlags::ANONFUNFIX); } static inline void VersionSetXML(JSVersion *version, bool enable) { if (enable) *version = JSVersion(uint32(*version) | VersionFlags::HAS_XML); else *version = JSVersion(uint32(*version) & ~VersionFlags::HAS_XML); } static inline void VersionSetAnonFunFix(JSVersion *version, bool enable) { if (enable) *version = JSVersion(uint32(*version) | VersionFlags::ANONFUNFIX); else *version = JSVersion(uint32(*version) & ~VersionFlags::ANONFUNFIX); } static inline JSVersion VersionExtractFlags(JSVersion version) { return JSVersion(uint32(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 uintN VersionFlagsToOptions(JSVersion version) { uintN copts = (VersionHasXML(version) ? JSOPTION_XML : 0) | (VersionHasAnonFunFix(version) ? JSOPTION_ANONFUNFIX : 0); JS_ASSERT((copts & JSCOMPILEOPTION_MASK) == copts); return copts; } static inline JSVersion OptionFlagsToVersion(uintN options, JSVersion version) { VersionSetXML(&version, OptionsHasXML(options)); VersionSetAnonFunFix(&version, OptionsHasAnonFunFix(options)); return version; } static inline bool VersionIsKnown(JSVersion version) { return VersionNumber(version) != JSVERSION_UNKNOWN; } typedef HashSet, SystemAllocPolicy> BusyArraysSet; } /* namespace js */ struct JSContext { explicit JSContext(JSRuntime *rt); JSContext *thisDuringConstruction() { return this; } ~JSContext(); /* JSRuntime contextList linkage. */ JSCList link; private: /* See JSContext::findVersion. */ JSVersion defaultVersion; /* script compilation version */ JSVersion versionOverride; /* supercedes defaultVersion when valid */ bool hasVersionOverride; /* Exception state -- the exception member is a GC root by definition. */ JSBool throwing; /* is there a pending exception? */ js::Value exception; /* most-recently-thrown exception */ /* Per-context run options. */ uintN runOptions; /* see jsapi.h for JSOPTION_* */ public: /* Locale specific callbacks for string conversion. */ JSLocaleCallbacks *localeCallbacks; js::AutoResolving *resolvingList; /* * True if generating an error, to prevent runaway recursion. * NB: generatingError packs with throwing below. */ JSPackedBool generatingError; /* Limit pointer for checking native stack consumption during recursion. */ jsuword stackLimit; /* Quota on the size of arenas used to compile and execute scripts. */ size_t scriptStackQuota; /* Data shared by threads in an address space. */ JSRuntime *const runtime; /* GC heap compartment. */ JSCompartment *compartment; /* Current execution stack. */ js::ContextStack stack; /* ContextStack convenience functions */ bool running() const { return stack.running(); } js::StackFrame* fp() const { return stack.fp(); } js::StackFrame* maybefp() const { return stack.maybefp(); } js::FrameRegs& regs() const { return stack.regs(); } js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); } /* Set cx->compartment based on the current scope chain. */ void resetCompartment(); /* Wrap cx->exception for the current compartment. */ void wrapPendingException(); /* Temporary arena pool used while compiling and decompiling. */ JSArenaPool tempPool; /* Temporary arena pool used while evaluate regular expressions. */ JSArenaPool regExpPool; /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; /* State for object and array toSource conversion. */ JSSharpObjectMap sharpObjectMap; js::BusyArraysSet busyArrays; /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ JSArgumentFormatMap *argumentFormatMap; /* Last message string and log file for debugging. */ char *lastMessage; #ifdef DEBUG void *logfp; jsbytecode *logPrevPc; #endif /* Per-context optional error reporter. */ JSErrorReporter errorReporter; /* Branch callback. */ JSOperationCallback operationCallback; /* Client opaque pointers. */ void *data; void *data2; inline js::RegExpStatics *regExpStatics(); public: /* * The default script compilation version can be set iff there is no code running. * This typically occurs via the JSAPI right after a context is constructed. */ bool canSetDefaultVersion() const { return !stack.running() && !hasVersionOverride; } /* Force a version for future script compilation. */ void overrideVersion(JSVersion newVersion) { JS_ASSERT(!canSetDefaultVersion()); versionOverride = newVersion; hasVersionOverride = true; } /* Set the default script compilation version. */ void setDefaultVersion(JSVersion version) { defaultVersion = version; } void clearVersionOverride() { hasVersionOverride = false; } JSVersion getDefaultVersion() const { return defaultVersion; } bool isVersionOverridden() const { return hasVersionOverride; } JSVersion getVersionOverride() const { JS_ASSERT(isVersionOverridden()); return versionOverride; } /* * Set the default version if possible; otherwise, force the version. * Return whether an override occurred. */ bool maybeOverrideVersion(JSVersion newVersion) { if (canSetDefaultVersion()) { setDefaultVersion(newVersion); return false; } overrideVersion(newVersion); return true; } /* * If there is no code on the stack, turn the override version into the * default version. */ void maybeMigrateVersionOverride() { if (JS_LIKELY(!isVersionOverridden() && stack.empty())) return; defaultVersion = versionOverride; clearVersionOverride(); } /* * Return: * - The override version, if there is an override version. * - The newest scripted frame's version, if there is such a frame. * - The default verion. * * Note: if this ever shows up in a profile, just add caching! */ JSVersion findVersion() const { if (hasVersionOverride) return versionOverride; if (stack.running()) { /* There may be a scripted function somewhere on the stack! */ js::StackFrame *f = fp(); while (f && !f->isScriptFrame()) f = f->prev(); if (f) return f->script()->getVersion(); } return defaultVersion; } void setRunOptions(uintN ropts) { JS_ASSERT((ropts & JSRUNOPTION_MASK) == ropts); runOptions = ropts; } /* Note: may override the version. */ void setCompileOptions(uintN newcopts) { JS_ASSERT((newcopts & JSCOMPILEOPTION_MASK) == newcopts); if (JS_LIKELY(getCompileOptions() == newcopts)) return; JSVersion version = findVersion(); JSVersion newVersion = js::OptionFlagsToVersion(newcopts, version); maybeOverrideVersion(newVersion); } uintN getRunOptions() const { return runOptions; } uintN getCompileOptions() const { return js::VersionFlagsToOptions(findVersion()); } uintN allOptions() const { return getRunOptions() | getCompileOptions(); } bool hasRunOption(uintN ropt) const { JS_ASSERT((ropt & JSRUNOPTION_MASK) == ropt); return !!(runOptions & ropt); } bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); } bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); } bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); } #ifdef JS_THREADSAFE private: JSThread *thread_; public: JSThread *thread() const { return thread_; } void setThread(JSThread *thread); static const size_t threadOffset() { return offsetof(JSContext, thread_); } unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ JSCList threadLinks; /* JSThread contextList linkage */ #define CX_FROM_THREAD_LINKS(tl) \ ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif /* Stack of thread-stack-allocated GC roots. */ js::AutoGCRooter *autoGCRooters; /* Debug hooks associated with the current context. */ const JSDebugHooks *debugHooks; /* Security callbacks that override any defined on the runtime. */ JSSecurityCallbacks *securityCallbacks; /* Stored here to avoid passing it around as a parameter. */ uintN resolveFlags; /* Random number generator state, used by jsmath.cpp. */ int64 rngSeed; /* Location to stash the iteration value between JSOP_MOREITER and JSOP_FOR*. */ js::Value iterValue; #ifdef JS_TRACER /* * True if traces may be executed. Invariant: The value of traceJitenabled * is always equal to the expression in updateJITEnabled below. * * This flag and the fields accessed by updateJITEnabled are written only * in runtime->gcLock, to avoid race conditions that would leave the wrong * value in traceJitEnabled. (But the interpreter reads this without * locking. That can race against another thread setting debug hooks, but * we always read cx->debugHooks without locking anyway.) */ bool traceJitEnabled; #endif #ifdef JS_METHODJIT bool methodJitEnabled; bool profilingEnabled; inline js::mjit::JaegerCompartment *jaegerCompartment(); #endif /* Caller must be holding runtime->gcLock. */ void updateJITEnabled(); #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 DSTOffsetCache dstOffsetCache; /* List of currently active non-escaping enumerators (for-in). */ JSObject *enumerators; private: /* * To go from a live generator frame (on the stack) to its generator object * (see comment js_FloatingFrameIfGenerator), we maintain a stack of active * generators, pushing and popping when entering and leaving generator * frames, respectively. */ js::Vector genStack; public: /* Return the generator object for the given generator frame. */ JSGenerator *generatorFor(js::StackFrame *fp) const; /* Early OOM-check. */ inline bool ensureGeneratorStackSpace(); bool enterGenerator(JSGenerator *gen) { return genStack.append(gen); } void leaveGenerator(JSGenerator *gen) { JS_ASSERT(genStack.back() == gen); genStack.popBack(); } #ifdef JS_THREADSAFE /* * When non-null JSContext::free_ delegates the job to the background * thread. */ js::GCHelperThread *gcBackgroundFree; #endif inline void* malloc_(size_t bytes) { return runtime->malloc_(bytes, this); } inline void* mallocNoReport(size_t bytes) { JS_ASSERT(bytes != 0); return runtime->malloc_(bytes, NULL); } inline void* calloc_(size_t bytes) { JS_ASSERT(bytes != 0); return runtime->calloc_(bytes, this); } inline void* realloc_(void* p, size_t bytes) { return runtime->realloc_(p, bytes, this); } inline void* realloc_(void* p, size_t oldBytes, size_t newBytes) { return runtime->realloc_(p, oldBytes, newBytes, this); } inline void free_(void* p) { #ifdef JS_THREADSAFE if (gcBackgroundFree) { gcBackgroundFree->freeLater(p); return; } #endif runtime->free_(p); } JS_DECLARE_NEW_METHODS(malloc_, inline) JS_DECLARE_DELETE_METHODS(free_, inline) void purge(); #ifdef DEBUG void assertValidStackDepth(uintN depth) { JS_ASSERT(0 <= regs().sp - fp()->base()); JS_ASSERT(depth <= uintptr_t(regs().sp - fp()->base())); } #else void assertValidStackDepth(uintN /*depth*/) {} #endif bool isExceptionPending() { return throwing; } js::Value getPendingException() { JS_ASSERT(throwing); return exception; } void setPendingException(js::Value v); void clearPendingException() { this->throwing = false; this->exception.setUndefined(); } 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 { #ifdef JS_THREADSAFE # define JS_THREAD_ID(cx) ((cx)->thread() ? (cx)->thread()->id : 0) #endif #if defined JS_THREADSAFE && defined DEBUG class AutoCheckRequestDepth { JSContext *cx; public: AutoCheckRequestDepth(JSContext *cx) : cx(cx) { cx->thread()->checkRequestDepth++; } ~AutoCheckRequestDepth() { JS_ASSERT(cx->thread()->checkRequestDepth != 0); cx->thread()->checkRequestDepth--; } }; # define CHECK_REQUEST(cx) \ JS_ASSERT((cx)->thread()); \ JS_ASSERT((cx)->thread()->data.requestDepth || (cx)->thread() == (cx)->runtime->gcThread); \ AutoCheckRequestDepth _autoCheckRequestDepth(cx); #else # define CHECK_REQUEST(cx) ((void) 0) # define CHECK_REQUEST_THREAD(cx) ((void) 0) #endif static inline uintN FramePCOffset(JSContext *cx, js::StackFrame* fp) { jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); return uintN(pc - fp->script()->code); } static inline JSAtom ** FrameAtomBase(JSContext *cx, js::StackFrame *fp) { return fp->hasImacropc() ? cx->runtime->atomState.commonAtomsStart() : fp->script()->atomMap.vector; } struct AutoResolving { public: enum Kind { LOOKUP, WATCH }; AutoResolving(JSContext *cx, JSObject *obj, jsid id, Kind kind = LOOKUP JS_GUARD_OBJECT_NOTIFIER_PARAM) : context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList) { JS_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; JSObject *const object; jsid const id; Kind const kind; AutoResolving *const link; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoGCRooter { public: AutoGCRooter(JSContext *cx, ptrdiff_t tag) : down(cx->autoGCRooters), tag(tag), context(cx) { JS_ASSERT(this != cx->autoGCRooters); CHECK_REQUEST(cx); cx->autoGCRooters = this; } ~AutoGCRooter() { JS_ASSERT(this == context->autoGCRooters); CHECK_REQUEST(context); context->autoGCRooters = down; } /* Implemented in jsgc.cpp. */ inline void trace(JSTracer *trc); #ifdef __GNUC__ # pragma GCC visibility push(default) #endif friend JS_FRIEND_API(void) MarkContext(JSTracer *trc, JSContext *acx); friend void MarkRuntime(JSTracer *trc); #ifdef __GNUC__ # pragma GCC visibility pop #endif protected: AutoGCRooter * const down; /* * Discriminates actual subclass of this being used. If non-negative, the * subclass roots an array of values of the length stored in this field. * If negative, meaning is indicated by the corresponding value in the enum * below. Any other negative value indicates some deeper problem such as * memory corruption. */ ptrdiff_t tag; JSContext * const context; enum { JSVAL = -1, /* js::AutoValueRooter */ SHAPE = -2, /* js::AutoShapeRooter */ PARSER = -3, /* js::Parser */ SCRIPT = -4, /* js::AutoScriptRooter */ ENUMERATOR = -5, /* js::AutoEnumStateRooter */ IDARRAY = -6, /* js::AutoIdArray */ DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */ NAMESPACES = -8, /* js::AutoNamespaceArray */ XML = -9, /* js::AutoXMLRooter */ OBJECT = -10, /* js::AutoObjectRooter */ ID = -11, /* js::AutoIdRooter */ VALVECTOR = -12, /* js::AutoValueVector */ DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ STRING = -14, /* js::AutoStringRooter */ IDVECTOR = -15, /* js::AutoIdVector */ BINDINGS = -16, /* js::Bindings */ SHAPEVECTOR = -17 /* js::AutoShapeVector */ }; private: /* No copy or assignment semantics. */ AutoGCRooter(AutoGCRooter &ida); void operator=(AutoGCRooter &ida); }; /* FIXME(bug 332648): Move this into a public header. */ class AutoValueRooter : private AutoGCRooter { public: explicit AutoValueRooter(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, JSVAL), val(js::NullValue()) { JS_GUARD_OBJECT_NOTIFIER_INIT; } AutoValueRooter(JSContext *cx, const Value &v JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, JSVAL), val(v) { JS_GUARD_OBJECT_NOTIFIER_INIT; } AutoValueRooter(JSContext *cx, jsval v JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, JSVAL), val(js::Valueify(v)) { JS_GUARD_OBJECT_NOTIFIER_INIT; } /* * If you are looking for Object* overloads, use AutoObjectRooter instead; * rooting Object*s as a js::Value requires discerning whether or not it is * a function object. Also, AutoObjectRooter is smaller. */ void set(Value v) { JS_ASSERT(tag == JSVAL); val = v; } void set(jsval v) { JS_ASSERT(tag == JSVAL); val = js::Valueify(v); } const Value &value() const { JS_ASSERT(tag == JSVAL); return val; } Value *addr() { JS_ASSERT(tag == JSVAL); return &val; } const jsval &jsval_value() const { JS_ASSERT(tag == JSVAL); return Jsvalify(val); } jsval *jsval_addr() { JS_ASSERT(tag == JSVAL); return Jsvalify(&val); } friend void AutoGCRooter::trace(JSTracer *trc); friend void MarkRuntime(JSTracer *trc); private: Value val; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoObjectRooter : private AutoGCRooter { public: AutoObjectRooter(JSContext *cx, JSObject *obj = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, OBJECT), obj(obj) { JS_GUARD_OBJECT_NOTIFIER_INIT; } void setObject(JSObject *obj) { this->obj = obj; } JSObject * object() const { return obj; } JSObject ** addr() { return &obj; } friend void AutoGCRooter::trace(JSTracer *trc); friend void MarkRuntime(JSTracer *trc); private: JSObject *obj; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoStringRooter : private AutoGCRooter { public: AutoStringRooter(JSContext *cx, JSString *str = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, STRING), str(str) { JS_GUARD_OBJECT_NOTIFIER_INIT; } void setString(JSString *str) { this->str = str; } JSString * string() const { return str; } JSString ** addr() { return &str; } friend void AutoGCRooter::trace(JSTracer *trc); private: JSString *str; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoArrayRooter : private AutoGCRooter { public: AutoArrayRooter(JSContext *cx, size_t len, Value *vec JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, len), array(vec) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_ASSERT(tag >= 0); } AutoArrayRooter(JSContext *cx, size_t len, jsval *vec JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, len), array(Valueify(vec)) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_ASSERT(tag >= 0); } void changeLength(size_t newLength) { tag = ptrdiff_t(newLength); JS_ASSERT(tag >= 0); } void changeArray(Value *newArray, size_t newLength) { changeLength(newLength); array = newArray; } Value *array; friend void AutoGCRooter::trace(JSTracer *trc); private: JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoShapeRooter : private AutoGCRooter { public: AutoShapeRooter(JSContext *cx, const js::Shape *shape JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, SHAPE), shape(shape) { JS_GUARD_OBJECT_NOTIFIER_INIT; } friend void AutoGCRooter::trace(JSTracer *trc); friend void MarkRuntime(JSTracer *trc); private: const js::Shape * const shape; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoScriptRooter : private AutoGCRooter { public: AutoScriptRooter(JSContext *cx, JSScript *script JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, SCRIPT), script(script) { JS_GUARD_OBJECT_NOTIFIER_INIT; } void setScript(JSScript *script) { this->script = script; } friend void AutoGCRooter::trace(JSTracer *trc); private: JSScript *script; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoIdRooter : private AutoGCRooter { public: explicit AutoIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0) JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, ID), id_(id) { JS_GUARD_OBJECT_NOTIFIER_INIT; } jsid id() { return id_; } jsid * addr() { return &id_; } friend void AutoGCRooter::trace(JSTracer *trc); friend void MarkRuntime(JSTracer *trc); private: jsid id_; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoIdArray : private AutoGCRooter { public: AutoIdArray(JSContext *cx, JSIdArray *ida JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, IDARRAY), idArray(ida) { JS_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoIdArray() { if (idArray) JS_DestroyIdArray(context, idArray); } bool operator!() { return idArray == NULL; } jsid operator[](size_t i) const { JS_ASSERT(idArray); JS_ASSERT(i < size_t(idArray->length)); return idArray->vector[i]; } size_t length() const { return idArray->length; } friend void AutoGCRooter::trace(JSTracer *trc); JSIdArray *steal() { JSIdArray *copy = idArray; idArray = NULL; return copy; } protected: inline void trace(JSTracer *trc); private: JSIdArray * idArray; JS_DECL_USE_GUARD_OBJECT_NOTIFIER /* No copy or assignment semantics. */ AutoIdArray(AutoIdArray &ida); void operator=(AutoIdArray &ida); }; /* The auto-root for enumeration object and its state. */ class AutoEnumStateRooter : private AutoGCRooter { public: AutoEnumStateRooter(JSContext *cx, JSObject *obj JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, ENUMERATOR), obj(obj), stateValue() { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_ASSERT(obj); } ~AutoEnumStateRooter() { if (!stateValue.isNull()) { DebugOnly ok = obj->enumerate(context, JSENUMERATE_DESTROY, &stateValue, 0); JS_ASSERT(ok); } } friend void AutoGCRooter::trace(JSTracer *trc); const Value &state() const { return stateValue; } Value *addr() { return &stateValue; } protected: void trace(JSTracer *trc); JSObject * const obj; private: Value stateValue; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; #ifdef JS_HAS_XML_SUPPORT class AutoXMLRooter : private AutoGCRooter { public: AutoXMLRooter(JSContext *cx, JSXML *xml JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, XML), xml(xml) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_ASSERT(xml); } friend void AutoGCRooter::trace(JSTracer *trc); friend void MarkRuntime(JSTracer *trc); private: JSXML * const xml; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; #endif /* JS_HAS_XML_SUPPORT */ class AutoBindingsRooter : private AutoGCRooter { public: AutoBindingsRooter(JSContext *cx, Bindings &bindings JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, BINDINGS), bindings(bindings) { JS_GUARD_OBJECT_NOTIFIER_INIT; } friend void AutoGCRooter::trace(JSTracer *trc); private: Bindings &bindings; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoLockGC { public: explicit AutoLockGC(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_LOCK_GC(rt); } ~AutoLockGC() { JS_UNLOCK_GC(rt); } private: JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoUnlockGC { private: JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoUnlockGC(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_UNLOCK_GC(rt); } ~AutoUnlockGC() { JS_LOCK_GC(rt); } }; class AutoLockAtomsCompartment { private: JSContext *cx; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: AutoLockAtomsCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_LOCK(cx, &cx->runtime->atomState.lock); #ifdef JS_THREADSAFE cx->runtime->atomsCompartmentIsLocked = true; #endif } ~AutoLockAtomsCompartment() { #ifdef JS_THREADSAFE cx->runtime->atomsCompartmentIsLocked = false; #endif JS_UNLOCK(cx, &cx->runtime->atomState.lock); } }; class AutoUnlockAtomsCompartment { JSContext *cx; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: AutoUnlockAtomsCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; #ifdef JS_THREADSAFE cx->runtime->atomsCompartmentIsLocked = false; #endif JS_UNLOCK(cx, &cx->runtime->atomState.lock); } ~AutoUnlockAtomsCompartment() { JS_LOCK(cx, &cx->runtime->atomState.lock); #ifdef JS_THREADSAFE cx->runtime->atomsCompartmentIsLocked = true; #endif } }; class AutoKeepAtoms { JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoKeepAtoms(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_KEEP_ATOMS(rt); } ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } }; class AutoArenaAllocator { JSArenaPool *pool; void *mark; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoArenaAllocator(JSArenaPool *pool JS_GUARD_OBJECT_NOTIFIER_PARAM) : pool(pool), mark(JS_ARENA_MARK(pool)) { JS_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoArenaAllocator() { JS_ARENA_RELEASE(pool, mark); } template T *alloc(size_t elems) { void *ptr; JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T)); return static_cast(ptr); } }; class AutoReleasePtr { JSContext *cx; void *ptr; JS_DECL_USE_GUARD_OBJECT_NOTIFIER AutoReleasePtr operator=(const AutoReleasePtr &other); public: explicit AutoReleasePtr(JSContext *cx, void *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx), ptr(ptr) { JS_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoReleasePtr() { cx->free_(ptr); } }; /* * FIXME: bug 602774: cleaner API for AutoReleaseNullablePtr */ class AutoReleaseNullablePtr { JSContext *cx; void *ptr; JS_DECL_USE_GUARD_OBJECT_NOTIFIER AutoReleaseNullablePtr operator=(const AutoReleaseNullablePtr &other); public: explicit AutoReleaseNullablePtr(JSContext *cx, void *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx), ptr(ptr) { JS_GUARD_OBJECT_NOTIFIER_INIT; } void reset(void *ptr2) { if (ptr) cx->free_(ptr); ptr = ptr2; } ~AutoReleaseNullablePtr() { if (ptr) cx->free_(ptr); } }; class AutoLocalNameArray { public: explicit AutoLocalNameArray(JSContext *cx, JSFunction *fun JS_GUARD_OBJECT_NOTIFIER_PARAM) : context(cx), mark(JS_ARENA_MARK(&cx->tempPool)), names(fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool)), count(fun->script()->bindings.countLocalNames()) { JS_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoLocalNameArray() { JS_ARENA_RELEASE(&context->tempPool, mark); } operator bool() const { return !!names; } uint32 length() const { return count; } const jsuword &operator [](unsigned i) const { return names[i]; } private: JSContext *context; void *mark; jsuword *names; uint32 count; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; template class AlreadyIncRefed { typedef RefCountable *****ConvertibleToBool; RefCountable *obj; public: explicit AlreadyIncRefed(RefCountable *obj) : obj(obj) {} bool null() const { return obj == NULL; } operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } RefCountable *get() const { return obj; } }; template class NeedsIncRef { typedef RefCountable *****ConvertibleToBool; RefCountable *obj; public: explicit NeedsIncRef(RefCountable *obj) : obj(obj) {} bool null() const { return obj == NULL; } operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } RefCountable *get() const { return obj; } }; template class AutoRefCount { typedef RefCountable *****ConvertibleToBool; JSContext *const cx; RefCountable *obj; AutoRefCount(const AutoRefCount &); void operator=(const AutoRefCount &); public: explicit AutoRefCount(JSContext *cx) : cx(cx), obj(NULL) {} AutoRefCount(JSContext *cx, NeedsIncRef aobj) : cx(cx), obj(aobj.get()) { if (obj) obj->incref(cx); } AutoRefCount(JSContext *cx, AlreadyIncRefed aobj) : cx(cx), obj(aobj.get()) {} ~AutoRefCount() { if (obj) obj->decref(cx); } void reset(NeedsIncRef aobj) { if (obj) obj->decref(cx); obj = aobj.get(); if (obj) obj->incref(cx); } void reset(AlreadyIncRefed aobj) { if (obj) obj->decref(cx); obj = aobj.get(); } bool null() const { return obj == NULL; } operator ConvertibleToBool() const { return (ConvertibleToBool)obj; } RefCountable *operator->() const { JS_ASSERT(!null()); return obj; } RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; } RefCountable *get() const { return obj; } }; } /* namespace js */ class JSAutoResolveFlags { public: JSAutoResolveFlags(JSContext *cx, uintN flags JS_GUARD_OBJECT_NOTIFIER_PARAM) : mContext(cx), mSaved(cx->resolveFlags) { JS_GUARD_OBJECT_NOTIFIER_INIT; cx->resolveFlags = flags; } ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } private: JSContext *mContext; uintN mSaved; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; extern js::ThreadData * js_CurrentThreadData(JSRuntime *rt); extern JSBool js_InitThreads(JSRuntime *rt); extern void js_FinishThreads(JSRuntime *rt); extern void js_PurgeThreads(JSContext *cx); namespace js { #ifdef JS_THREADSAFE /* Iterator over ThreadData from all JSThread instances. */ class ThreadDataIter : public JSThread::Map::Range { public: ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {} ThreadData *threadData() const { return &front().value->data; } }; #else /* !JS_THREADSAFE */ class ThreadDataIter { JSRuntime *runtime; bool done; public: ThreadDataIter(JSRuntime *rt) : runtime(rt), done(false) {} bool empty() const { return done; } void popFront() { JS_ASSERT(!done); done = true; } ThreadData *threadData() const { JS_ASSERT(!done); return &runtime->threadData; } }; #endif /* !JS_THREADSAFE */ } /* namespace js */ /* * Create and destroy functions for JSContext, which is manually allocated * and exclusively owned. */ extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); extern void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); static JS_INLINE JSContext * js_ContextFromLinkField(JSCList *link) { JS_ASSERT(link); return (JSContext *) ((uint8 *) link - offsetof(JSContext, link)); } /* * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. */ extern JSContext * js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); /* * Iterate through contexts with active requests. The caller must be holding * rt->gcLock in case of a thread-safe build, or otherwise guarantee that the * context list is not alternated asynchroniously. */ extern JS_FRIEND_API(JSContext *) js_NextActiveContext(JSRuntime *, JSContext *); /* * Report an exception, which is currently realized as a printf-style format * string and its arguments. */ typedef enum JSErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "js.msg" #undef MSG_DEF JSErr_Limit } JSErrNum; extern JS_FRIEND_API(const JSErrorFormatString *) js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); #ifdef va_start extern JSBool js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); extern JSBool js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, void *userRef, const uintN errorNumber, JSBool charArgs, va_list ap); extern JSBool js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, void *userRef, const uintN errorNumber, char **message, JSErrorReport *reportp, bool charArgs, va_list ap); #endif extern void js_ReportOutOfMemory(JSContext *cx); /* * Report that cx->scriptStackQuota is exhausted. */ void js_ReportOutOfScriptQuota(JSContext *maybecx); /* JS_CHECK_RECURSION is used outside JS, so JS_FRIEND_API. */ JS_FRIEND_API(void) js_ReportOverRecursed(JSContext *maybecx); extern JS_FRIEND_API(void) js_ReportAllocationOverflow(JSContext *cx); #define JS_CHECK_RECURSION(cx, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ \ if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy_)) { \ js_ReportOverRecursed(cx); \ onerror; \ } \ JS_END_MACRO /* * 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, intN spindex, const js::Value &v, JSString *fallback); extern void js_ReportMissingArg(JSContext *cx, const js::Value &v, uintN 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, uintN flags, const uintN errorNumber, intN spindex, const js::Value &v, JSString *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 JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; #ifdef JS_THREADSAFE # define JS_ASSERT_REQUEST_DEPTH(cx) (JS_ASSERT((cx)->thread()), \ JS_ASSERT((cx)->thread()->data.requestDepth >= 1)) #else # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) #endif /* * 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. */ #define JS_CHECK_OPERATION_LIMIT(cx) \ (JS_ASSERT_REQUEST_DEPTH(cx), \ (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx))) /* * 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); namespace js { /* These must be called with GC lock taken. */ JS_FRIEND_API(void) TriggerOperationCallback(JSContext *cx); void TriggerAllOperationCallbacks(JSRuntime *rt); } /* namespace js */ extern js::StackFrame * js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp); extern jsbytecode* js_GetCurrentBytecodePC(JSContext* cx); extern bool js_CurrentPCIsInImacro(JSContext *cx); namespace js { class RegExpStatics; extern JS_FORCES_STACK JS_FRIEND_API(void) LeaveTrace(JSContext *cx); } /* namespace js */ /* * Get the current frame, first lazily instantiating stack frames if needed. * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.) * * Defined in jstracer.cpp if JS_TRACER is defined. */ static JS_FORCES_STACK JS_INLINE js::StackFrame * js_GetTopStackFrame(JSContext *cx) { js::LeaveTrace(cx); return cx->maybefp(); } static JS_INLINE JSBool js_IsPropertyCacheDisabled(JSContext *cx) { return cx->runtime->shapeGen >= js::SHAPE_OVERFLOW_BIT; } static JS_INLINE uint32 js_RegenerateShapeForGC(JSRuntime *rt) { JS_ASSERT(rt->gcRunning); JS_ASSERT(rt->gcRegenShapes); /* * Under the GC, compared with js_GenerateShape, we don't need to use * atomic increments but we still must make sure that after an overflow * the shape stays such. */ uint32 shape = rt->shapeGen; shape = (shape + 1) | (shape & js::SHAPE_OVERFLOW_BIT); rt->shapeGen = shape; return shape; } namespace js { template class AutoVectorRooter : protected AutoGCRooter { public: explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, tag), vector(cx) { JS_GUARD_OBJECT_NOTIFIER_INIT; } size_t length() const { return vector.length(); } bool append(const T &v) { return vector.append(v); } /* For use when space has already been reserved. */ void infallibleAppend(const T &v) { vector.infallibleAppend(v); } void popBack() { vector.popBack(); } T popCopy() { return vector.popCopy(); } bool growBy(size_t inc) { size_t oldLength = vector.length(); if (!vector.growByUninitialized(inc)) return false; MakeRangeGCSafe(vector.begin() + oldLength, vector.end()); return true; } bool resize(size_t newLength) { size_t oldLength = vector.length(); if (newLength <= oldLength) { vector.shrinkBy(oldLength - newLength); return true; } if (!vector.growByUninitialized(newLength - oldLength)) return false; MakeRangeGCSafe(vector.begin() + oldLength, vector.end()); return true; } bool reserve(size_t newLength) { return vector.reserve(newLength); } T &operator[](size_t i) { return vector[i]; } const T &operator[](size_t i) const { return vector[i]; } const T *begin() const { return vector.begin(); } T *begin() { return vector.begin(); } const T *end() const { return vector.end(); } T *end() { return vector.end(); } const T &back() const { return vector.back(); } friend void AutoGCRooter::trace(JSTracer *trc); private: typedef Vector VectorImpl; VectorImpl vector; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoValueVector : public AutoVectorRooter { public: explicit AutoValueVector(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, VALVECTOR) { JS_GUARD_OBJECT_NOTIFIER_INIT; } const jsval *jsval_begin() const { return Jsvalify(begin()); } jsval *jsval_begin() { return Jsvalify(begin()); } const jsval *jsval_end() const { return Jsvalify(end()); } jsval *jsval_end() { return Jsvalify(end()); } JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoIdVector : public AutoVectorRooter { public: explicit AutoIdVector(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, IDVECTOR) { JS_GUARD_OBJECT_NOTIFIER_INIT; } JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoShapeVector : public AutoVectorRooter { public: explicit AutoShapeVector(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoVectorRooter(cx, SHAPEVECTOR) { JS_GUARD_OBJECT_NOTIFIER_INIT; } JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; JSIdArray * NewIdArray(JSContext *cx, jsint length); } /* namespace js */ #ifdef _MSC_VER #pragma warning(pop) #pragma warning(pop) #endif #endif /* jscntxt_h___ */