michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jscompartment_h michael@0: #define jscompartment_h michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "gc/Zone.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/PIC.h" michael@0: #include "vm/SavedStacks.h" michael@0: michael@0: namespace js { michael@0: michael@0: namespace jit { michael@0: class JitCompartment; michael@0: } michael@0: michael@0: namespace gc { michael@0: template class ComponentFinder; michael@0: } michael@0: michael@0: struct NativeIterator; michael@0: michael@0: /* michael@0: * A single-entry cache for some base-10 double-to-string conversions. This michael@0: * helps date-format-xparb.js. It also avoids skewing the results for michael@0: * v8-splay.js when measured by the SunSpider harness, where the splay tree michael@0: * initialization (which includes many repeated double-to-string conversions) michael@0: * is erroneously included in the measurement; see bug 562553. michael@0: */ michael@0: class DtoaCache { michael@0: double d; michael@0: int base; michael@0: JSFlatString *s; // if s==nullptr, d and base are not valid michael@0: michael@0: public: michael@0: DtoaCache() : s(nullptr) {} michael@0: void purge() { s = nullptr; } michael@0: michael@0: JSFlatString *lookup(int base, double d) { michael@0: return this->s && base == this->base && d == this->d ? this->s : nullptr; michael@0: } michael@0: michael@0: void cache(int base, double d, JSFlatString *s) { michael@0: this->base = base; michael@0: this->d = d; michael@0: this->s = s; michael@0: } michael@0: }; michael@0: michael@0: /* If HashNumber grows, need to change WrapperHasher. */ michael@0: JS_STATIC_ASSERT(sizeof(HashNumber) == 4); michael@0: michael@0: struct CrossCompartmentKey michael@0: { michael@0: enum Kind { michael@0: ObjectWrapper, michael@0: StringWrapper, michael@0: DebuggerScript, michael@0: DebuggerSource, michael@0: DebuggerObject, michael@0: DebuggerEnvironment michael@0: }; michael@0: michael@0: Kind kind; michael@0: JSObject *debugger; michael@0: js::gc::Cell *wrapped; michael@0: michael@0: CrossCompartmentKey() michael@0: : kind(ObjectWrapper), debugger(nullptr), wrapped(nullptr) {} michael@0: CrossCompartmentKey(JSObject *wrapped) michael@0: : kind(ObjectWrapper), debugger(nullptr), wrapped(wrapped) {} michael@0: CrossCompartmentKey(JSString *wrapped) michael@0: : kind(StringWrapper), debugger(nullptr), wrapped(wrapped) {} michael@0: CrossCompartmentKey(Value wrapped) michael@0: : kind(wrapped.isString() ? StringWrapper : ObjectWrapper), michael@0: debugger(nullptr), michael@0: wrapped((js::gc::Cell *)wrapped.toGCThing()) {} michael@0: CrossCompartmentKey(const RootedValue &wrapped) michael@0: : kind(wrapped.get().isString() ? StringWrapper : ObjectWrapper), michael@0: debugger(nullptr), michael@0: wrapped((js::gc::Cell *)wrapped.get().toGCThing()) {} michael@0: CrossCompartmentKey(Kind kind, JSObject *dbg, js::gc::Cell *wrapped) michael@0: : kind(kind), debugger(dbg), wrapped(wrapped) {} michael@0: }; michael@0: michael@0: struct WrapperHasher : public DefaultHasher michael@0: { michael@0: static HashNumber hash(const CrossCompartmentKey &key) { michael@0: JS_ASSERT(!IsPoisonedPtr(key.wrapped)); michael@0: return uint32_t(uintptr_t(key.wrapped)) | uint32_t(key.kind); michael@0: } michael@0: michael@0: static bool match(const CrossCompartmentKey &l, const CrossCompartmentKey &k) { michael@0: return l.kind == k.kind && l.debugger == k.debugger && l.wrapped == k.wrapped; michael@0: } michael@0: }; michael@0: michael@0: typedef HashMap WrapperMap; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: namespace JS { michael@0: struct TypeInferenceSizes; michael@0: } michael@0: michael@0: namespace js { michael@0: class AutoDebugModeInvalidation; michael@0: class DebugScopes; michael@0: class WeakMapBase; michael@0: } michael@0: michael@0: struct JSCompartment michael@0: { michael@0: JS::CompartmentOptions options_; michael@0: michael@0: private: michael@0: JS::Zone *zone_; michael@0: JSRuntime *runtime_; michael@0: michael@0: public: michael@0: JSPrincipals *principals; michael@0: bool isSystem; michael@0: bool isSelfHosting; michael@0: bool marked; michael@0: michael@0: #ifdef DEBUG michael@0: bool firedOnNewGlobalObject; michael@0: #endif michael@0: michael@0: void mark() { marked = true; } michael@0: michael@0: private: michael@0: friend struct JSRuntime; michael@0: friend struct JSContext; michael@0: friend class js::ExclusiveContext; michael@0: js::ReadBarriered global_; michael@0: michael@0: unsigned enterCompartmentDepth; michael@0: michael@0: public: michael@0: void enter() { enterCompartmentDepth++; } michael@0: void leave() { enterCompartmentDepth--; } michael@0: bool hasBeenEntered() { return !!enterCompartmentDepth; } michael@0: michael@0: JS::Zone *zone() { return zone_; } michael@0: const JS::Zone *zone() const { return zone_; } michael@0: JS::CompartmentOptions &options() { return options_; } michael@0: const JS::CompartmentOptions &options() const { return options_; } michael@0: michael@0: JSRuntime *runtimeFromMainThread() { michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); michael@0: return runtime_; michael@0: } michael@0: michael@0: // Note: Unrestricted access to the zone's runtime from an arbitrary michael@0: // thread can easily lead to races. Use this method very carefully. michael@0: JSRuntime *runtimeFromAnyThread() const { michael@0: return runtime_; michael@0: } michael@0: michael@0: /* michael@0: * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or michael@0: * (b) the compartment's global has been collected. The latter can happen michael@0: * if e.g. a string in a compartment is rooted but no object is, and thus michael@0: * the global isn't rooted, and thus the global can be finalized while the michael@0: * compartment lives on. michael@0: * michael@0: * In contrast, JSObject::global() is infallible because marking a JSObject michael@0: * always marks its global as well. michael@0: * TODO: add infallible JSScript::global() michael@0: */ michael@0: inline js::GlobalObject *maybeGlobal() const; michael@0: michael@0: inline void initGlobal(js::GlobalObject &global); michael@0: michael@0: public: michael@0: /* michael@0: * Moves all data from the allocator |workerAllocator|, which was michael@0: * in use by a parallel worker, into the compartment's main michael@0: * allocator. This is used at the end of a parallel section. michael@0: */ michael@0: void adoptWorkerAllocator(js::Allocator *workerAllocator); michael@0: michael@0: bool activeAnalysis; michael@0: michael@0: /* Type information about the scripts and objects in this compartment. */ michael@0: js::types::TypeCompartment types; michael@0: michael@0: void *data; michael@0: michael@0: private: michael@0: js::ObjectMetadataCallback objectMetadataCallback; michael@0: michael@0: js::SavedStacks savedStacks_; michael@0: michael@0: js::WrapperMap crossCompartmentWrappers; michael@0: michael@0: public: michael@0: /* Last time at which an animation was played for a global in this compartment. */ michael@0: int64_t lastAnimationTime; michael@0: michael@0: js::RegExpCompartment regExps; michael@0: michael@0: /* michael@0: * For generational GC, record whether a write barrier has added this michael@0: * compartment's global to the store buffer since the last minor GC. michael@0: * michael@0: * This is used to avoid adding it to the store buffer on every write, which michael@0: * can quickly fill the buffer and also cause performance problems. michael@0: */ michael@0: bool globalWriteBarriered; michael@0: michael@0: public: michael@0: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, michael@0: size_t *tiAllocationSiteTables, michael@0: size_t *tiArrayTypeTables, michael@0: size_t *tiObjectTypeTables, michael@0: size_t *compartmentObject, michael@0: size_t *shapesCompartmentTables, michael@0: size_t *crossCompartmentWrappers, michael@0: size_t *regexpCompartment, michael@0: size_t *debuggeesSet, michael@0: size_t *savedStacksSet); michael@0: michael@0: /* michael@0: * Shared scope property tree, and arena-pool for allocating its nodes. michael@0: */ michael@0: js::PropertyTree propertyTree; michael@0: michael@0: /* Set of all unowned base shapes in the compartment. */ michael@0: js::BaseShapeSet baseShapes; michael@0: void sweepBaseShapeTable(); michael@0: michael@0: /* Set of initial shapes in the compartment. */ michael@0: js::InitialShapeSet initialShapes; michael@0: void sweepInitialShapeTable(); michael@0: michael@0: /* Set of default 'new' or lazy types in the compartment. */ michael@0: js::types::TypeObjectWithNewScriptSet newTypeObjects; michael@0: js::types::TypeObjectWithNewScriptSet lazyTypeObjects; michael@0: void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table); michael@0: #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) michael@0: void checkNewTypeObjectTableAfterMovingGC(); michael@0: void checkInitialShapesTableAfterMovingGC(); michael@0: void checkWrapperMapAfterMovingGC(); michael@0: #endif michael@0: michael@0: /* michael@0: * Hash table of all manually call site-cloned functions from within michael@0: * self-hosted code. Cloning according to call site provides extra michael@0: * sensitivity for type specialization and inlining. michael@0: */ michael@0: js::CallsiteCloneTable callsiteClones; michael@0: void sweepCallsiteClones(); michael@0: michael@0: /* michael@0: * Lazily initialized script source object to use for scripts cloned michael@0: * from the self-hosting global. michael@0: */ michael@0: js::ReadBarriered selfHostingScriptSource; michael@0: michael@0: /* During GC, stores the index of this compartment in rt->compartments. */ michael@0: unsigned gcIndex; michael@0: michael@0: /* michael@0: * During GC, stores the head of a list of incoming pointers from gray cells. michael@0: * michael@0: * The objects in the list are either cross-compartment wrappers, or michael@0: * debugger wrapper objects. The list link is either in the second extra michael@0: * slot for the former, or a special slot for the latter. michael@0: */ michael@0: JSObject *gcIncomingGrayPointers; michael@0: michael@0: /* During GC, list of live array buffers with >1 view accumulated during tracing. */ michael@0: js::ArrayBufferVector gcLiveArrayBuffers; michael@0: michael@0: /* Linked list of live weakmaps in this compartment. */ michael@0: js::WeakMapBase *gcWeakMapList; michael@0: michael@0: private: michael@0: enum { michael@0: DebugFromC = 1 << 0, michael@0: DebugFromJS = 1 << 1, michael@0: DebugNeedDelazification = 1 << 2 michael@0: }; michael@0: michael@0: static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS; michael@0: michael@0: unsigned debugModeBits; // see debugMode() below michael@0: michael@0: public: michael@0: JSCompartment(JS::Zone *zone, const JS::CompartmentOptions &options); michael@0: ~JSCompartment(); michael@0: michael@0: bool init(JSContext *cx); michael@0: michael@0: /* Mark cross-compartment wrappers. */ michael@0: void markCrossCompartmentWrappers(JSTracer *trc); michael@0: michael@0: inline bool wrap(JSContext *cx, JS::MutableHandleValue vp, michael@0: JS::HandleObject existing = js::NullPtr()); michael@0: michael@0: bool wrap(JSContext *cx, JSString **strp); michael@0: bool wrap(JSContext *cx, js::HeapPtrString *strp); michael@0: bool wrap(JSContext *cx, JS::MutableHandleObject obj, michael@0: JS::HandleObject existingArg = js::NullPtr()); michael@0: bool wrapId(JSContext *cx, jsid *idp); michael@0: bool wrap(JSContext *cx, js::PropertyOp *op); michael@0: bool wrap(JSContext *cx, js::StrictPropertyOp *op); michael@0: bool wrap(JSContext *cx, JS::MutableHandle desc); michael@0: bool wrap(JSContext *cx, js::AutoIdVector &props); michael@0: michael@0: bool putWrapper(JSContext *cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper); michael@0: michael@0: js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) { michael@0: return crossCompartmentWrappers.lookup(wrapped); michael@0: } michael@0: michael@0: void removeWrapper(js::WrapperMap::Ptr p) { michael@0: crossCompartmentWrappers.remove(p); michael@0: } michael@0: michael@0: struct WrapperEnum : public js::WrapperMap::Enum { michael@0: WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {} michael@0: }; michael@0: michael@0: void trace(JSTracer *trc); michael@0: void markRoots(JSTracer *trc); michael@0: bool isDiscardingJitCode(JSTracer *trc); michael@0: void sweep(js::FreeOp *fop, bool releaseTypes); michael@0: void sweepCrossCompartmentWrappers(); michael@0: void purge(); michael@0: void clearTables(); michael@0: michael@0: bool hasObjectMetadataCallback() const { return objectMetadataCallback; } michael@0: void setObjectMetadataCallback(js::ObjectMetadataCallback callback); michael@0: bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const { michael@0: return objectMetadataCallback(cx, obj); michael@0: } michael@0: michael@0: js::SavedStacks &savedStacks() { return savedStacks_; } michael@0: michael@0: void findOutgoingEdges(js::gc::ComponentFinder &finder); michael@0: michael@0: js::DtoaCache dtoaCache; michael@0: michael@0: /* Random number generator state, used by jsmath.cpp. */ michael@0: uint64_t rngState; michael@0: michael@0: private: michael@0: /* michael@0: * Weak reference to each global in this compartment that is a debuggee. michael@0: * Each global has its own list of debuggers. michael@0: */ michael@0: js::GlobalObjectSet debuggees; michael@0: michael@0: private: michael@0: JSCompartment *thisForCtor() { return this; } michael@0: michael@0: public: michael@0: /* michael@0: * There are dueling APIs for debug mode. It can be enabled or disabled via michael@0: * JS_SetDebugModeForCompartment. It is automatically enabled and disabled michael@0: * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set michael@0: * if the C API wants debug mode and the DebugFromJS bit set if debuggees michael@0: * is non-empty. michael@0: * michael@0: * When toggling on, DebugNeedDelazification is set to signal that michael@0: * Debugger methods which depend on seeing all scripts (like findScripts) michael@0: * need to delazify the scripts in the compartment first. michael@0: */ michael@0: bool debugMode() const { michael@0: return !!(debugModeBits & DebugModeFromMask); michael@0: } michael@0: michael@0: /* True if any scripts from this compartment are on the JS stack. */ michael@0: bool hasScriptsOnStack(); michael@0: michael@0: /* michael@0: * Schedule the compartment to be delazified. Called from michael@0: * LazyScript::Create. michael@0: */ michael@0: void scheduleDelazificationForDebugMode() { michael@0: debugModeBits |= DebugNeedDelazification; michael@0: } michael@0: michael@0: /* michael@0: * If we scheduled delazification for turning on debug mode, delazify all michael@0: * scripts. michael@0: */ michael@0: bool ensureDelazifyScriptsForDebugMode(JSContext *cx); michael@0: michael@0: private: michael@0: michael@0: /* This is called only when debugMode() has just toggled. */ michael@0: bool updateJITForDebugMode(JSContext *maybecx, js::AutoDebugModeInvalidation &invalidate); michael@0: michael@0: public: michael@0: js::GlobalObjectSet &getDebuggees() { return debuggees; } michael@0: bool addDebuggee(JSContext *cx, js::GlobalObject *global); michael@0: bool addDebuggee(JSContext *cx, js::GlobalObject *global, michael@0: js::AutoDebugModeInvalidation &invalidate); michael@0: bool removeDebuggee(JSContext *cx, js::GlobalObject *global, michael@0: js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); michael@0: bool removeDebuggee(JSContext *cx, js::GlobalObject *global, michael@0: js::AutoDebugModeInvalidation &invalidate, michael@0: js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); michael@0: void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, michael@0: js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); michael@0: void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, michael@0: js::AutoDebugModeInvalidation &invalidate, michael@0: js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); michael@0: bool setDebugModeFromC(JSContext *cx, bool b, michael@0: js::AutoDebugModeInvalidation &invalidate); michael@0: michael@0: void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JS::HandleObject handler); michael@0: void clearTraps(js::FreeOp *fop); michael@0: michael@0: private: michael@0: void sweepBreakpoints(js::FreeOp *fop); michael@0: michael@0: public: michael@0: js::WatchpointMap *watchpointMap; michael@0: michael@0: js::ScriptCountsMap *scriptCountsMap; michael@0: michael@0: js::DebugScriptMap *debugScriptMap; michael@0: michael@0: /* Bookkeeping information for debug scope objects. */ michael@0: js::DebugScopes *debugScopes; michael@0: michael@0: /* michael@0: * List of potentially active iterators that may need deleted property michael@0: * suppression. michael@0: */ michael@0: js::NativeIterator *enumerators; michael@0: michael@0: /* Used by memory reporters and invalid otherwise. */ michael@0: void *compartmentStats; michael@0: michael@0: #ifdef JS_ION michael@0: private: michael@0: js::jit::JitCompartment *jitCompartment_; michael@0: michael@0: public: michael@0: bool ensureJitCompartmentExists(JSContext *cx); michael@0: js::jit::JitCompartment *jitCompartment() { michael@0: return jitCompartment_; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: inline bool michael@0: JSRuntime::isAtomsZone(JS::Zone *zone) michael@0: { michael@0: return zone == atomsCompartment_->zone(); michael@0: } michael@0: michael@0: // For use when changing the debug mode flag on one or more compartments. michael@0: // Invalidate and discard JIT code since debug mode breaks JIT assumptions. michael@0: // michael@0: // AutoDebugModeInvalidation has two modes: compartment or zone michael@0: // invalidation. While it is correct to always use compartment invalidation, michael@0: // if you know ahead of time you need to invalidate a whole zone, it is faster michael@0: // to invalidate the zone. michael@0: // michael@0: // Compartment invalidation only invalidates scripts belonging to that michael@0: // compartment. michael@0: // michael@0: // Zone invalidation invalidates all scripts belonging to non-special michael@0: // (i.e. those with principals) compartments of the zone. michael@0: // michael@0: // FIXME: Remove entirely once bug 716647 lands. michael@0: // michael@0: class js::AutoDebugModeInvalidation michael@0: { michael@0: JSCompartment *comp_; michael@0: JS::Zone *zone_; michael@0: michael@0: enum { michael@0: NoNeed = 0, michael@0: ToggledOn = 1, michael@0: ToggledOff = 2 michael@0: } needInvalidation_; michael@0: michael@0: public: michael@0: explicit AutoDebugModeInvalidation(JSCompartment *comp) michael@0: : comp_(comp), zone_(nullptr), needInvalidation_(NoNeed) michael@0: { } michael@0: michael@0: explicit AutoDebugModeInvalidation(JS::Zone *zone) michael@0: : comp_(nullptr), zone_(zone), needInvalidation_(NoNeed) michael@0: { } michael@0: michael@0: #ifdef JS_ION michael@0: ~AutoDebugModeInvalidation(); michael@0: #else michael@0: ~AutoDebugModeInvalidation() { } michael@0: #endif michael@0: michael@0: bool isFor(JSCompartment *comp) { michael@0: if (comp_) michael@0: return comp == comp_; michael@0: return comp->zone() == zone_; michael@0: } michael@0: michael@0: void scheduleInvalidation(bool debugMode) { michael@0: // If we are scheduling invalidation for multiple compartments, they michael@0: // must all agree on the toggle. This is so we can decide if we need michael@0: // to invalidate on-stack scripts. michael@0: MOZ_ASSERT_IF(needInvalidation_ != NoNeed, michael@0: needInvalidation_ == (debugMode ? ToggledOn : ToggledOff)); michael@0: needInvalidation_ = debugMode ? ToggledOn : ToggledOff; michael@0: } michael@0: }; michael@0: michael@0: namespace js { michael@0: michael@0: inline js::Handle michael@0: ExclusiveContext::global() const michael@0: { michael@0: /* michael@0: * It's safe to use |unsafeGet()| here because any compartment that is michael@0: * on-stack will be marked automatically, so there's no need for a read michael@0: * barrier on it. Once the compartment is popped, the handle is no longer michael@0: * safe to use. michael@0: */ michael@0: MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first"); michael@0: return Handle::fromMarkedLocation(compartment_->global_.unsafeGet()); michael@0: } michael@0: michael@0: class AssertCompartmentUnchanged michael@0: { michael@0: public: michael@0: AssertCompartmentUnchanged(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : cx(cx), oldCompartment(cx->compartment()) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: ~AssertCompartmentUnchanged() { michael@0: JS_ASSERT(cx->compartment() == oldCompartment); michael@0: } michael@0: michael@0: protected: michael@0: JSContext * const cx; michael@0: JSCompartment * const oldCompartment; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoCompartment michael@0: { michael@0: ExclusiveContext * const cx_; michael@0: JSCompartment * const origin_; michael@0: michael@0: public: michael@0: inline AutoCompartment(ExclusiveContext *cx, JSObject *target); michael@0: inline AutoCompartment(ExclusiveContext *cx, JSCompartment *target); michael@0: inline ~AutoCompartment(); michael@0: michael@0: ExclusiveContext *context() const { return cx_; } michael@0: JSCompartment *origin() const { return origin_; } michael@0: michael@0: private: michael@0: AutoCompartment(const AutoCompartment &) MOZ_DELETE; michael@0: AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE; michael@0: }; michael@0: michael@0: /* michael@0: * Use this to change the behavior of an AutoCompartment slightly on error. If michael@0: * the exception happens to be an Error object, copy it to the origin compartment michael@0: * instead of wrapping it. michael@0: */ michael@0: class ErrorCopier michael@0: { michael@0: mozilla::Maybe ∾ michael@0: RootedObject scope; michael@0: michael@0: public: michael@0: ErrorCopier(mozilla::Maybe &ac, JSObject *scope) michael@0: : ac(ac), scope(ac.ref().context(), scope) {} michael@0: ~ErrorCopier(); michael@0: }; michael@0: michael@0: /* michael@0: * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that michael@0: * are obtained from the cross-compartment map. However, these classes should michael@0: * not be used if the wrapper will escape. For example, it should not be stored michael@0: * in the heap. michael@0: * michael@0: * The AutoWrapper rooters are different from other autorooters because their michael@0: * wrappers are marked on every GC slice rather than just the first one. If michael@0: * there's some wrapper that we want to use temporarily without causing it to be michael@0: * marked, we can use these AutoWrapper classes. If we get unlucky and a GC michael@0: * slice runs during the code using the wrapper, the GC will mark the wrapper so michael@0: * that it doesn't get swept out from under us. Otherwise, the wrapper needn't michael@0: * be marked. This is useful in functions like JS_TransplantObject that michael@0: * manipulate wrappers in compartments that may no longer be alive. michael@0: */ michael@0: michael@0: /* michael@0: * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It michael@0: * should not be used in any other situations. michael@0: */ michael@0: struct WrapperValue michael@0: { michael@0: /* michael@0: * We use unsafeGet() in the constructors to avoid invoking a read barrier michael@0: * on the wrapper, which may be dead (see the comment about bug 803376 in michael@0: * jsgc.cpp regarding this). If there is an incremental GC while the wrapper michael@0: * is in use, the AutoWrapper rooter will ensure the wrapper gets marked. michael@0: */ michael@0: explicit WrapperValue(const WrapperMap::Ptr &ptr) michael@0: : value(*ptr->value().unsafeGet()) michael@0: {} michael@0: michael@0: explicit WrapperValue(const WrapperMap::Enum &e) michael@0: : value(*e.front().value().unsafeGet()) michael@0: {} michael@0: michael@0: Value &get() { return value; } michael@0: Value get() const { return value; } michael@0: operator const Value &() const { return value; } michael@0: JSObject &toObject() const { return value.toObject(); } michael@0: michael@0: private: michael@0: Value value; michael@0: }; michael@0: michael@0: class AutoWrapperVector : public AutoVectorRooter michael@0: { michael@0: public: michael@0: explicit AutoWrapperVector(JSContext *cx michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoVectorRooter(cx, WRAPVECTOR) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: class AutoWrapperRooter : private AutoGCRooter { michael@0: public: michael@0: AutoWrapperRooter(JSContext *cx, WrapperValue v michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : AutoGCRooter(cx, WRAPPER), value(v) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: operator JSObject *() const { michael@0: return value.get().toObjectOrNull(); michael@0: } michael@0: michael@0: friend void AutoGCRooter::trace(JSTracer *trc); michael@0: michael@0: private: michael@0: WrapperValue value; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* jscompartment_h */