1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jscompartment.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,674 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jscompartment_h 1.11 +#define jscompartment_h 1.12 + 1.13 +#include "mozilla/MemoryReporting.h" 1.14 + 1.15 +#include "builtin/TypedObject.h" 1.16 +#include "gc/Zone.h" 1.17 +#include "vm/GlobalObject.h" 1.18 +#include "vm/PIC.h" 1.19 +#include "vm/SavedStacks.h" 1.20 + 1.21 +namespace js { 1.22 + 1.23 +namespace jit { 1.24 +class JitCompartment; 1.25 +} 1.26 + 1.27 +namespace gc { 1.28 +template<class Node> class ComponentFinder; 1.29 +} 1.30 + 1.31 +struct NativeIterator; 1.32 + 1.33 +/* 1.34 + * A single-entry cache for some base-10 double-to-string conversions. This 1.35 + * helps date-format-xparb.js. It also avoids skewing the results for 1.36 + * v8-splay.js when measured by the SunSpider harness, where the splay tree 1.37 + * initialization (which includes many repeated double-to-string conversions) 1.38 + * is erroneously included in the measurement; see bug 562553. 1.39 + */ 1.40 +class DtoaCache { 1.41 + double d; 1.42 + int base; 1.43 + JSFlatString *s; // if s==nullptr, d and base are not valid 1.44 + 1.45 + public: 1.46 + DtoaCache() : s(nullptr) {} 1.47 + void purge() { s = nullptr; } 1.48 + 1.49 + JSFlatString *lookup(int base, double d) { 1.50 + return this->s && base == this->base && d == this->d ? this->s : nullptr; 1.51 + } 1.52 + 1.53 + void cache(int base, double d, JSFlatString *s) { 1.54 + this->base = base; 1.55 + this->d = d; 1.56 + this->s = s; 1.57 + } 1.58 +}; 1.59 + 1.60 +/* If HashNumber grows, need to change WrapperHasher. */ 1.61 +JS_STATIC_ASSERT(sizeof(HashNumber) == 4); 1.62 + 1.63 +struct CrossCompartmentKey 1.64 +{ 1.65 + enum Kind { 1.66 + ObjectWrapper, 1.67 + StringWrapper, 1.68 + DebuggerScript, 1.69 + DebuggerSource, 1.70 + DebuggerObject, 1.71 + DebuggerEnvironment 1.72 + }; 1.73 + 1.74 + Kind kind; 1.75 + JSObject *debugger; 1.76 + js::gc::Cell *wrapped; 1.77 + 1.78 + CrossCompartmentKey() 1.79 + : kind(ObjectWrapper), debugger(nullptr), wrapped(nullptr) {} 1.80 + CrossCompartmentKey(JSObject *wrapped) 1.81 + : kind(ObjectWrapper), debugger(nullptr), wrapped(wrapped) {} 1.82 + CrossCompartmentKey(JSString *wrapped) 1.83 + : kind(StringWrapper), debugger(nullptr), wrapped(wrapped) {} 1.84 + CrossCompartmentKey(Value wrapped) 1.85 + : kind(wrapped.isString() ? StringWrapper : ObjectWrapper), 1.86 + debugger(nullptr), 1.87 + wrapped((js::gc::Cell *)wrapped.toGCThing()) {} 1.88 + CrossCompartmentKey(const RootedValue &wrapped) 1.89 + : kind(wrapped.get().isString() ? StringWrapper : ObjectWrapper), 1.90 + debugger(nullptr), 1.91 + wrapped((js::gc::Cell *)wrapped.get().toGCThing()) {} 1.92 + CrossCompartmentKey(Kind kind, JSObject *dbg, js::gc::Cell *wrapped) 1.93 + : kind(kind), debugger(dbg), wrapped(wrapped) {} 1.94 +}; 1.95 + 1.96 +struct WrapperHasher : public DefaultHasher<CrossCompartmentKey> 1.97 +{ 1.98 + static HashNumber hash(const CrossCompartmentKey &key) { 1.99 + JS_ASSERT(!IsPoisonedPtr(key.wrapped)); 1.100 + return uint32_t(uintptr_t(key.wrapped)) | uint32_t(key.kind); 1.101 + } 1.102 + 1.103 + static bool match(const CrossCompartmentKey &l, const CrossCompartmentKey &k) { 1.104 + return l.kind == k.kind && l.debugger == k.debugger && l.wrapped == k.wrapped; 1.105 + } 1.106 +}; 1.107 + 1.108 +typedef HashMap<CrossCompartmentKey, ReadBarrieredValue, 1.109 + WrapperHasher, SystemAllocPolicy> WrapperMap; 1.110 + 1.111 +} /* namespace js */ 1.112 + 1.113 +namespace JS { 1.114 +struct TypeInferenceSizes; 1.115 +} 1.116 + 1.117 +namespace js { 1.118 +class AutoDebugModeInvalidation; 1.119 +class DebugScopes; 1.120 +class WeakMapBase; 1.121 +} 1.122 + 1.123 +struct JSCompartment 1.124 +{ 1.125 + JS::CompartmentOptions options_; 1.126 + 1.127 + private: 1.128 + JS::Zone *zone_; 1.129 + JSRuntime *runtime_; 1.130 + 1.131 + public: 1.132 + JSPrincipals *principals; 1.133 + bool isSystem; 1.134 + bool isSelfHosting; 1.135 + bool marked; 1.136 + 1.137 +#ifdef DEBUG 1.138 + bool firedOnNewGlobalObject; 1.139 +#endif 1.140 + 1.141 + void mark() { marked = true; } 1.142 + 1.143 + private: 1.144 + friend struct JSRuntime; 1.145 + friend struct JSContext; 1.146 + friend class js::ExclusiveContext; 1.147 + js::ReadBarriered<js::GlobalObject> global_; 1.148 + 1.149 + unsigned enterCompartmentDepth; 1.150 + 1.151 + public: 1.152 + void enter() { enterCompartmentDepth++; } 1.153 + void leave() { enterCompartmentDepth--; } 1.154 + bool hasBeenEntered() { return !!enterCompartmentDepth; } 1.155 + 1.156 + JS::Zone *zone() { return zone_; } 1.157 + const JS::Zone *zone() const { return zone_; } 1.158 + JS::CompartmentOptions &options() { return options_; } 1.159 + const JS::CompartmentOptions &options() const { return options_; } 1.160 + 1.161 + JSRuntime *runtimeFromMainThread() { 1.162 + JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); 1.163 + return runtime_; 1.164 + } 1.165 + 1.166 + // Note: Unrestricted access to the zone's runtime from an arbitrary 1.167 + // thread can easily lead to races. Use this method very carefully. 1.168 + JSRuntime *runtimeFromAnyThread() const { 1.169 + return runtime_; 1.170 + } 1.171 + 1.172 + /* 1.173 + * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or 1.174 + * (b) the compartment's global has been collected. The latter can happen 1.175 + * if e.g. a string in a compartment is rooted but no object is, and thus 1.176 + * the global isn't rooted, and thus the global can be finalized while the 1.177 + * compartment lives on. 1.178 + * 1.179 + * In contrast, JSObject::global() is infallible because marking a JSObject 1.180 + * always marks its global as well. 1.181 + * TODO: add infallible JSScript::global() 1.182 + */ 1.183 + inline js::GlobalObject *maybeGlobal() const; 1.184 + 1.185 + inline void initGlobal(js::GlobalObject &global); 1.186 + 1.187 + public: 1.188 + /* 1.189 + * Moves all data from the allocator |workerAllocator|, which was 1.190 + * in use by a parallel worker, into the compartment's main 1.191 + * allocator. This is used at the end of a parallel section. 1.192 + */ 1.193 + void adoptWorkerAllocator(js::Allocator *workerAllocator); 1.194 + 1.195 + bool activeAnalysis; 1.196 + 1.197 + /* Type information about the scripts and objects in this compartment. */ 1.198 + js::types::TypeCompartment types; 1.199 + 1.200 + void *data; 1.201 + 1.202 + private: 1.203 + js::ObjectMetadataCallback objectMetadataCallback; 1.204 + 1.205 + js::SavedStacks savedStacks_; 1.206 + 1.207 + js::WrapperMap crossCompartmentWrappers; 1.208 + 1.209 + public: 1.210 + /* Last time at which an animation was played for a global in this compartment. */ 1.211 + int64_t lastAnimationTime; 1.212 + 1.213 + js::RegExpCompartment regExps; 1.214 + 1.215 + /* 1.216 + * For generational GC, record whether a write barrier has added this 1.217 + * compartment's global to the store buffer since the last minor GC. 1.218 + * 1.219 + * This is used to avoid adding it to the store buffer on every write, which 1.220 + * can quickly fill the buffer and also cause performance problems. 1.221 + */ 1.222 + bool globalWriteBarriered; 1.223 + 1.224 + public: 1.225 + void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.226 + size_t *tiAllocationSiteTables, 1.227 + size_t *tiArrayTypeTables, 1.228 + size_t *tiObjectTypeTables, 1.229 + size_t *compartmentObject, 1.230 + size_t *shapesCompartmentTables, 1.231 + size_t *crossCompartmentWrappers, 1.232 + size_t *regexpCompartment, 1.233 + size_t *debuggeesSet, 1.234 + size_t *savedStacksSet); 1.235 + 1.236 + /* 1.237 + * Shared scope property tree, and arena-pool for allocating its nodes. 1.238 + */ 1.239 + js::PropertyTree propertyTree; 1.240 + 1.241 + /* Set of all unowned base shapes in the compartment. */ 1.242 + js::BaseShapeSet baseShapes; 1.243 + void sweepBaseShapeTable(); 1.244 + 1.245 + /* Set of initial shapes in the compartment. */ 1.246 + js::InitialShapeSet initialShapes; 1.247 + void sweepInitialShapeTable(); 1.248 + 1.249 + /* Set of default 'new' or lazy types in the compartment. */ 1.250 + js::types::TypeObjectWithNewScriptSet newTypeObjects; 1.251 + js::types::TypeObjectWithNewScriptSet lazyTypeObjects; 1.252 + void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table); 1.253 +#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) 1.254 + void checkNewTypeObjectTableAfterMovingGC(); 1.255 + void checkInitialShapesTableAfterMovingGC(); 1.256 + void checkWrapperMapAfterMovingGC(); 1.257 +#endif 1.258 + 1.259 + /* 1.260 + * Hash table of all manually call site-cloned functions from within 1.261 + * self-hosted code. Cloning according to call site provides extra 1.262 + * sensitivity for type specialization and inlining. 1.263 + */ 1.264 + js::CallsiteCloneTable callsiteClones; 1.265 + void sweepCallsiteClones(); 1.266 + 1.267 + /* 1.268 + * Lazily initialized script source object to use for scripts cloned 1.269 + * from the self-hosting global. 1.270 + */ 1.271 + js::ReadBarriered<js::ScriptSourceObject> selfHostingScriptSource; 1.272 + 1.273 + /* During GC, stores the index of this compartment in rt->compartments. */ 1.274 + unsigned gcIndex; 1.275 + 1.276 + /* 1.277 + * During GC, stores the head of a list of incoming pointers from gray cells. 1.278 + * 1.279 + * The objects in the list are either cross-compartment wrappers, or 1.280 + * debugger wrapper objects. The list link is either in the second extra 1.281 + * slot for the former, or a special slot for the latter. 1.282 + */ 1.283 + JSObject *gcIncomingGrayPointers; 1.284 + 1.285 + /* During GC, list of live array buffers with >1 view accumulated during tracing. */ 1.286 + js::ArrayBufferVector gcLiveArrayBuffers; 1.287 + 1.288 + /* Linked list of live weakmaps in this compartment. */ 1.289 + js::WeakMapBase *gcWeakMapList; 1.290 + 1.291 + private: 1.292 + enum { 1.293 + DebugFromC = 1 << 0, 1.294 + DebugFromJS = 1 << 1, 1.295 + DebugNeedDelazification = 1 << 2 1.296 + }; 1.297 + 1.298 + static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS; 1.299 + 1.300 + unsigned debugModeBits; // see debugMode() below 1.301 + 1.302 + public: 1.303 + JSCompartment(JS::Zone *zone, const JS::CompartmentOptions &options); 1.304 + ~JSCompartment(); 1.305 + 1.306 + bool init(JSContext *cx); 1.307 + 1.308 + /* Mark cross-compartment wrappers. */ 1.309 + void markCrossCompartmentWrappers(JSTracer *trc); 1.310 + 1.311 + inline bool wrap(JSContext *cx, JS::MutableHandleValue vp, 1.312 + JS::HandleObject existing = js::NullPtr()); 1.313 + 1.314 + bool wrap(JSContext *cx, JSString **strp); 1.315 + bool wrap(JSContext *cx, js::HeapPtrString *strp); 1.316 + bool wrap(JSContext *cx, JS::MutableHandleObject obj, 1.317 + JS::HandleObject existingArg = js::NullPtr()); 1.318 + bool wrapId(JSContext *cx, jsid *idp); 1.319 + bool wrap(JSContext *cx, js::PropertyOp *op); 1.320 + bool wrap(JSContext *cx, js::StrictPropertyOp *op); 1.321 + bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc); 1.322 + bool wrap(JSContext *cx, js::AutoIdVector &props); 1.323 + 1.324 + bool putWrapper(JSContext *cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper); 1.325 + 1.326 + js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) { 1.327 + return crossCompartmentWrappers.lookup(wrapped); 1.328 + } 1.329 + 1.330 + void removeWrapper(js::WrapperMap::Ptr p) { 1.331 + crossCompartmentWrappers.remove(p); 1.332 + } 1.333 + 1.334 + struct WrapperEnum : public js::WrapperMap::Enum { 1.335 + WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {} 1.336 + }; 1.337 + 1.338 + void trace(JSTracer *trc); 1.339 + void markRoots(JSTracer *trc); 1.340 + bool isDiscardingJitCode(JSTracer *trc); 1.341 + void sweep(js::FreeOp *fop, bool releaseTypes); 1.342 + void sweepCrossCompartmentWrappers(); 1.343 + void purge(); 1.344 + void clearTables(); 1.345 + 1.346 + bool hasObjectMetadataCallback() const { return objectMetadataCallback; } 1.347 + void setObjectMetadataCallback(js::ObjectMetadataCallback callback); 1.348 + bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const { 1.349 + return objectMetadataCallback(cx, obj); 1.350 + } 1.351 + 1.352 + js::SavedStacks &savedStacks() { return savedStacks_; } 1.353 + 1.354 + void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder); 1.355 + 1.356 + js::DtoaCache dtoaCache; 1.357 + 1.358 + /* Random number generator state, used by jsmath.cpp. */ 1.359 + uint64_t rngState; 1.360 + 1.361 + private: 1.362 + /* 1.363 + * Weak reference to each global in this compartment that is a debuggee. 1.364 + * Each global has its own list of debuggers. 1.365 + */ 1.366 + js::GlobalObjectSet debuggees; 1.367 + 1.368 + private: 1.369 + JSCompartment *thisForCtor() { return this; } 1.370 + 1.371 + public: 1.372 + /* 1.373 + * There are dueling APIs for debug mode. It can be enabled or disabled via 1.374 + * JS_SetDebugModeForCompartment. It is automatically enabled and disabled 1.375 + * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set 1.376 + * if the C API wants debug mode and the DebugFromJS bit set if debuggees 1.377 + * is non-empty. 1.378 + * 1.379 + * When toggling on, DebugNeedDelazification is set to signal that 1.380 + * Debugger methods which depend on seeing all scripts (like findScripts) 1.381 + * need to delazify the scripts in the compartment first. 1.382 + */ 1.383 + bool debugMode() const { 1.384 + return !!(debugModeBits & DebugModeFromMask); 1.385 + } 1.386 + 1.387 + /* True if any scripts from this compartment are on the JS stack. */ 1.388 + bool hasScriptsOnStack(); 1.389 + 1.390 + /* 1.391 + * Schedule the compartment to be delazified. Called from 1.392 + * LazyScript::Create. 1.393 + */ 1.394 + void scheduleDelazificationForDebugMode() { 1.395 + debugModeBits |= DebugNeedDelazification; 1.396 + } 1.397 + 1.398 + /* 1.399 + * If we scheduled delazification for turning on debug mode, delazify all 1.400 + * scripts. 1.401 + */ 1.402 + bool ensureDelazifyScriptsForDebugMode(JSContext *cx); 1.403 + 1.404 + private: 1.405 + 1.406 + /* This is called only when debugMode() has just toggled. */ 1.407 + bool updateJITForDebugMode(JSContext *maybecx, js::AutoDebugModeInvalidation &invalidate); 1.408 + 1.409 + public: 1.410 + js::GlobalObjectSet &getDebuggees() { return debuggees; } 1.411 + bool addDebuggee(JSContext *cx, js::GlobalObject *global); 1.412 + bool addDebuggee(JSContext *cx, js::GlobalObject *global, 1.413 + js::AutoDebugModeInvalidation &invalidate); 1.414 + bool removeDebuggee(JSContext *cx, js::GlobalObject *global, 1.415 + js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); 1.416 + bool removeDebuggee(JSContext *cx, js::GlobalObject *global, 1.417 + js::AutoDebugModeInvalidation &invalidate, 1.418 + js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); 1.419 + void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, 1.420 + js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); 1.421 + void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, 1.422 + js::AutoDebugModeInvalidation &invalidate, 1.423 + js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); 1.424 + bool setDebugModeFromC(JSContext *cx, bool b, 1.425 + js::AutoDebugModeInvalidation &invalidate); 1.426 + 1.427 + void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JS::HandleObject handler); 1.428 + void clearTraps(js::FreeOp *fop); 1.429 + 1.430 + private: 1.431 + void sweepBreakpoints(js::FreeOp *fop); 1.432 + 1.433 + public: 1.434 + js::WatchpointMap *watchpointMap; 1.435 + 1.436 + js::ScriptCountsMap *scriptCountsMap; 1.437 + 1.438 + js::DebugScriptMap *debugScriptMap; 1.439 + 1.440 + /* Bookkeeping information for debug scope objects. */ 1.441 + js::DebugScopes *debugScopes; 1.442 + 1.443 + /* 1.444 + * List of potentially active iterators that may need deleted property 1.445 + * suppression. 1.446 + */ 1.447 + js::NativeIterator *enumerators; 1.448 + 1.449 + /* Used by memory reporters and invalid otherwise. */ 1.450 + void *compartmentStats; 1.451 + 1.452 +#ifdef JS_ION 1.453 + private: 1.454 + js::jit::JitCompartment *jitCompartment_; 1.455 + 1.456 + public: 1.457 + bool ensureJitCompartmentExists(JSContext *cx); 1.458 + js::jit::JitCompartment *jitCompartment() { 1.459 + return jitCompartment_; 1.460 + } 1.461 +#endif 1.462 +}; 1.463 + 1.464 +inline bool 1.465 +JSRuntime::isAtomsZone(JS::Zone *zone) 1.466 +{ 1.467 + return zone == atomsCompartment_->zone(); 1.468 +} 1.469 + 1.470 +// For use when changing the debug mode flag on one or more compartments. 1.471 +// Invalidate and discard JIT code since debug mode breaks JIT assumptions. 1.472 +// 1.473 +// AutoDebugModeInvalidation has two modes: compartment or zone 1.474 +// invalidation. While it is correct to always use compartment invalidation, 1.475 +// if you know ahead of time you need to invalidate a whole zone, it is faster 1.476 +// to invalidate the zone. 1.477 +// 1.478 +// Compartment invalidation only invalidates scripts belonging to that 1.479 +// compartment. 1.480 +// 1.481 +// Zone invalidation invalidates all scripts belonging to non-special 1.482 +// (i.e. those with principals) compartments of the zone. 1.483 +// 1.484 +// FIXME: Remove entirely once bug 716647 lands. 1.485 +// 1.486 +class js::AutoDebugModeInvalidation 1.487 +{ 1.488 + JSCompartment *comp_; 1.489 + JS::Zone *zone_; 1.490 + 1.491 + enum { 1.492 + NoNeed = 0, 1.493 + ToggledOn = 1, 1.494 + ToggledOff = 2 1.495 + } needInvalidation_; 1.496 + 1.497 + public: 1.498 + explicit AutoDebugModeInvalidation(JSCompartment *comp) 1.499 + : comp_(comp), zone_(nullptr), needInvalidation_(NoNeed) 1.500 + { } 1.501 + 1.502 + explicit AutoDebugModeInvalidation(JS::Zone *zone) 1.503 + : comp_(nullptr), zone_(zone), needInvalidation_(NoNeed) 1.504 + { } 1.505 + 1.506 +#ifdef JS_ION 1.507 + ~AutoDebugModeInvalidation(); 1.508 +#else 1.509 + ~AutoDebugModeInvalidation() { } 1.510 +#endif 1.511 + 1.512 + bool isFor(JSCompartment *comp) { 1.513 + if (comp_) 1.514 + return comp == comp_; 1.515 + return comp->zone() == zone_; 1.516 + } 1.517 + 1.518 + void scheduleInvalidation(bool debugMode) { 1.519 + // If we are scheduling invalidation for multiple compartments, they 1.520 + // must all agree on the toggle. This is so we can decide if we need 1.521 + // to invalidate on-stack scripts. 1.522 + MOZ_ASSERT_IF(needInvalidation_ != NoNeed, 1.523 + needInvalidation_ == (debugMode ? ToggledOn : ToggledOff)); 1.524 + needInvalidation_ = debugMode ? ToggledOn : ToggledOff; 1.525 + } 1.526 +}; 1.527 + 1.528 +namespace js { 1.529 + 1.530 +inline js::Handle<js::GlobalObject*> 1.531 +ExclusiveContext::global() const 1.532 +{ 1.533 + /* 1.534 + * It's safe to use |unsafeGet()| here because any compartment that is 1.535 + * on-stack will be marked automatically, so there's no need for a read 1.536 + * barrier on it. Once the compartment is popped, the handle is no longer 1.537 + * safe to use. 1.538 + */ 1.539 + MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first"); 1.540 + return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet()); 1.541 +} 1.542 + 1.543 +class AssertCompartmentUnchanged 1.544 +{ 1.545 + public: 1.546 + AssertCompartmentUnchanged(JSContext *cx 1.547 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.548 + : cx(cx), oldCompartment(cx->compartment()) 1.549 + { 1.550 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.551 + } 1.552 + 1.553 + ~AssertCompartmentUnchanged() { 1.554 + JS_ASSERT(cx->compartment() == oldCompartment); 1.555 + } 1.556 + 1.557 + protected: 1.558 + JSContext * const cx; 1.559 + JSCompartment * const oldCompartment; 1.560 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.561 +}; 1.562 + 1.563 +class AutoCompartment 1.564 +{ 1.565 + ExclusiveContext * const cx_; 1.566 + JSCompartment * const origin_; 1.567 + 1.568 + public: 1.569 + inline AutoCompartment(ExclusiveContext *cx, JSObject *target); 1.570 + inline AutoCompartment(ExclusiveContext *cx, JSCompartment *target); 1.571 + inline ~AutoCompartment(); 1.572 + 1.573 + ExclusiveContext *context() const { return cx_; } 1.574 + JSCompartment *origin() const { return origin_; } 1.575 + 1.576 + private: 1.577 + AutoCompartment(const AutoCompartment &) MOZ_DELETE; 1.578 + AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE; 1.579 +}; 1.580 + 1.581 +/* 1.582 + * Use this to change the behavior of an AutoCompartment slightly on error. If 1.583 + * the exception happens to be an Error object, copy it to the origin compartment 1.584 + * instead of wrapping it. 1.585 + */ 1.586 +class ErrorCopier 1.587 +{ 1.588 + mozilla::Maybe<AutoCompartment> ∾ 1.589 + RootedObject scope; 1.590 + 1.591 + public: 1.592 + ErrorCopier(mozilla::Maybe<AutoCompartment> &ac, JSObject *scope) 1.593 + : ac(ac), scope(ac.ref().context(), scope) {} 1.594 + ~ErrorCopier(); 1.595 +}; 1.596 + 1.597 +/* 1.598 + * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that 1.599 + * are obtained from the cross-compartment map. However, these classes should 1.600 + * not be used if the wrapper will escape. For example, it should not be stored 1.601 + * in the heap. 1.602 + * 1.603 + * The AutoWrapper rooters are different from other autorooters because their 1.604 + * wrappers are marked on every GC slice rather than just the first one. If 1.605 + * there's some wrapper that we want to use temporarily without causing it to be 1.606 + * marked, we can use these AutoWrapper classes. If we get unlucky and a GC 1.607 + * slice runs during the code using the wrapper, the GC will mark the wrapper so 1.608 + * that it doesn't get swept out from under us. Otherwise, the wrapper needn't 1.609 + * be marked. This is useful in functions like JS_TransplantObject that 1.610 + * manipulate wrappers in compartments that may no longer be alive. 1.611 + */ 1.612 + 1.613 +/* 1.614 + * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It 1.615 + * should not be used in any other situations. 1.616 + */ 1.617 +struct WrapperValue 1.618 +{ 1.619 + /* 1.620 + * We use unsafeGet() in the constructors to avoid invoking a read barrier 1.621 + * on the wrapper, which may be dead (see the comment about bug 803376 in 1.622 + * jsgc.cpp regarding this). If there is an incremental GC while the wrapper 1.623 + * is in use, the AutoWrapper rooter will ensure the wrapper gets marked. 1.624 + */ 1.625 + explicit WrapperValue(const WrapperMap::Ptr &ptr) 1.626 + : value(*ptr->value().unsafeGet()) 1.627 + {} 1.628 + 1.629 + explicit WrapperValue(const WrapperMap::Enum &e) 1.630 + : value(*e.front().value().unsafeGet()) 1.631 + {} 1.632 + 1.633 + Value &get() { return value; } 1.634 + Value get() const { return value; } 1.635 + operator const Value &() const { return value; } 1.636 + JSObject &toObject() const { return value.toObject(); } 1.637 + 1.638 + private: 1.639 + Value value; 1.640 +}; 1.641 + 1.642 +class AutoWrapperVector : public AutoVectorRooter<WrapperValue> 1.643 +{ 1.644 + public: 1.645 + explicit AutoWrapperVector(JSContext *cx 1.646 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.647 + : AutoVectorRooter<WrapperValue>(cx, WRAPVECTOR) 1.648 + { 1.649 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.650 + } 1.651 + 1.652 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.653 +}; 1.654 + 1.655 +class AutoWrapperRooter : private AutoGCRooter { 1.656 + public: 1.657 + AutoWrapperRooter(JSContext *cx, WrapperValue v 1.658 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.659 + : AutoGCRooter(cx, WRAPPER), value(v) 1.660 + { 1.661 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.662 + } 1.663 + 1.664 + operator JSObject *() const { 1.665 + return value.get().toObjectOrNull(); 1.666 + } 1.667 + 1.668 + friend void AutoGCRooter::trace(JSTracer *trc); 1.669 + 1.670 + private: 1.671 + WrapperValue value; 1.672 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.673 +}; 1.674 + 1.675 +} /* namespace js */ 1.676 + 1.677 +#endif /* jscompartment_h */