js/src/jscompartment.h

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef jscompartment_h
michael@0 8 #define jscompartment_h
michael@0 9
michael@0 10 #include "mozilla/MemoryReporting.h"
michael@0 11
michael@0 12 #include "builtin/TypedObject.h"
michael@0 13 #include "gc/Zone.h"
michael@0 14 #include "vm/GlobalObject.h"
michael@0 15 #include "vm/PIC.h"
michael@0 16 #include "vm/SavedStacks.h"
michael@0 17
michael@0 18 namespace js {
michael@0 19
michael@0 20 namespace jit {
michael@0 21 class JitCompartment;
michael@0 22 }
michael@0 23
michael@0 24 namespace gc {
michael@0 25 template<class Node> class ComponentFinder;
michael@0 26 }
michael@0 27
michael@0 28 struct NativeIterator;
michael@0 29
michael@0 30 /*
michael@0 31 * A single-entry cache for some base-10 double-to-string conversions. This
michael@0 32 * helps date-format-xparb.js. It also avoids skewing the results for
michael@0 33 * v8-splay.js when measured by the SunSpider harness, where the splay tree
michael@0 34 * initialization (which includes many repeated double-to-string conversions)
michael@0 35 * is erroneously included in the measurement; see bug 562553.
michael@0 36 */
michael@0 37 class DtoaCache {
michael@0 38 double d;
michael@0 39 int base;
michael@0 40 JSFlatString *s; // if s==nullptr, d and base are not valid
michael@0 41
michael@0 42 public:
michael@0 43 DtoaCache() : s(nullptr) {}
michael@0 44 void purge() { s = nullptr; }
michael@0 45
michael@0 46 JSFlatString *lookup(int base, double d) {
michael@0 47 return this->s && base == this->base && d == this->d ? this->s : nullptr;
michael@0 48 }
michael@0 49
michael@0 50 void cache(int base, double d, JSFlatString *s) {
michael@0 51 this->base = base;
michael@0 52 this->d = d;
michael@0 53 this->s = s;
michael@0 54 }
michael@0 55 };
michael@0 56
michael@0 57 /* If HashNumber grows, need to change WrapperHasher. */
michael@0 58 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
michael@0 59
michael@0 60 struct CrossCompartmentKey
michael@0 61 {
michael@0 62 enum Kind {
michael@0 63 ObjectWrapper,
michael@0 64 StringWrapper,
michael@0 65 DebuggerScript,
michael@0 66 DebuggerSource,
michael@0 67 DebuggerObject,
michael@0 68 DebuggerEnvironment
michael@0 69 };
michael@0 70
michael@0 71 Kind kind;
michael@0 72 JSObject *debugger;
michael@0 73 js::gc::Cell *wrapped;
michael@0 74
michael@0 75 CrossCompartmentKey()
michael@0 76 : kind(ObjectWrapper), debugger(nullptr), wrapped(nullptr) {}
michael@0 77 CrossCompartmentKey(JSObject *wrapped)
michael@0 78 : kind(ObjectWrapper), debugger(nullptr), wrapped(wrapped) {}
michael@0 79 CrossCompartmentKey(JSString *wrapped)
michael@0 80 : kind(StringWrapper), debugger(nullptr), wrapped(wrapped) {}
michael@0 81 CrossCompartmentKey(Value wrapped)
michael@0 82 : kind(wrapped.isString() ? StringWrapper : ObjectWrapper),
michael@0 83 debugger(nullptr),
michael@0 84 wrapped((js::gc::Cell *)wrapped.toGCThing()) {}
michael@0 85 CrossCompartmentKey(const RootedValue &wrapped)
michael@0 86 : kind(wrapped.get().isString() ? StringWrapper : ObjectWrapper),
michael@0 87 debugger(nullptr),
michael@0 88 wrapped((js::gc::Cell *)wrapped.get().toGCThing()) {}
michael@0 89 CrossCompartmentKey(Kind kind, JSObject *dbg, js::gc::Cell *wrapped)
michael@0 90 : kind(kind), debugger(dbg), wrapped(wrapped) {}
michael@0 91 };
michael@0 92
michael@0 93 struct WrapperHasher : public DefaultHasher<CrossCompartmentKey>
michael@0 94 {
michael@0 95 static HashNumber hash(const CrossCompartmentKey &key) {
michael@0 96 JS_ASSERT(!IsPoisonedPtr(key.wrapped));
michael@0 97 return uint32_t(uintptr_t(key.wrapped)) | uint32_t(key.kind);
michael@0 98 }
michael@0 99
michael@0 100 static bool match(const CrossCompartmentKey &l, const CrossCompartmentKey &k) {
michael@0 101 return l.kind == k.kind && l.debugger == k.debugger && l.wrapped == k.wrapped;
michael@0 102 }
michael@0 103 };
michael@0 104
michael@0 105 typedef HashMap<CrossCompartmentKey, ReadBarrieredValue,
michael@0 106 WrapperHasher, SystemAllocPolicy> WrapperMap;
michael@0 107
michael@0 108 } /* namespace js */
michael@0 109
michael@0 110 namespace JS {
michael@0 111 struct TypeInferenceSizes;
michael@0 112 }
michael@0 113
michael@0 114 namespace js {
michael@0 115 class AutoDebugModeInvalidation;
michael@0 116 class DebugScopes;
michael@0 117 class WeakMapBase;
michael@0 118 }
michael@0 119
michael@0 120 struct JSCompartment
michael@0 121 {
michael@0 122 JS::CompartmentOptions options_;
michael@0 123
michael@0 124 private:
michael@0 125 JS::Zone *zone_;
michael@0 126 JSRuntime *runtime_;
michael@0 127
michael@0 128 public:
michael@0 129 JSPrincipals *principals;
michael@0 130 bool isSystem;
michael@0 131 bool isSelfHosting;
michael@0 132 bool marked;
michael@0 133
michael@0 134 #ifdef DEBUG
michael@0 135 bool firedOnNewGlobalObject;
michael@0 136 #endif
michael@0 137
michael@0 138 void mark() { marked = true; }
michael@0 139
michael@0 140 private:
michael@0 141 friend struct JSRuntime;
michael@0 142 friend struct JSContext;
michael@0 143 friend class js::ExclusiveContext;
michael@0 144 js::ReadBarriered<js::GlobalObject> global_;
michael@0 145
michael@0 146 unsigned enterCompartmentDepth;
michael@0 147
michael@0 148 public:
michael@0 149 void enter() { enterCompartmentDepth++; }
michael@0 150 void leave() { enterCompartmentDepth--; }
michael@0 151 bool hasBeenEntered() { return !!enterCompartmentDepth; }
michael@0 152
michael@0 153 JS::Zone *zone() { return zone_; }
michael@0 154 const JS::Zone *zone() const { return zone_; }
michael@0 155 JS::CompartmentOptions &options() { return options_; }
michael@0 156 const JS::CompartmentOptions &options() const { return options_; }
michael@0 157
michael@0 158 JSRuntime *runtimeFromMainThread() {
michael@0 159 JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
michael@0 160 return runtime_;
michael@0 161 }
michael@0 162
michael@0 163 // Note: Unrestricted access to the zone's runtime from an arbitrary
michael@0 164 // thread can easily lead to races. Use this method very carefully.
michael@0 165 JSRuntime *runtimeFromAnyThread() const {
michael@0 166 return runtime_;
michael@0 167 }
michael@0 168
michael@0 169 /*
michael@0 170 * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
michael@0 171 * (b) the compartment's global has been collected. The latter can happen
michael@0 172 * if e.g. a string in a compartment is rooted but no object is, and thus
michael@0 173 * the global isn't rooted, and thus the global can be finalized while the
michael@0 174 * compartment lives on.
michael@0 175 *
michael@0 176 * In contrast, JSObject::global() is infallible because marking a JSObject
michael@0 177 * always marks its global as well.
michael@0 178 * TODO: add infallible JSScript::global()
michael@0 179 */
michael@0 180 inline js::GlobalObject *maybeGlobal() const;
michael@0 181
michael@0 182 inline void initGlobal(js::GlobalObject &global);
michael@0 183
michael@0 184 public:
michael@0 185 /*
michael@0 186 * Moves all data from the allocator |workerAllocator|, which was
michael@0 187 * in use by a parallel worker, into the compartment's main
michael@0 188 * allocator. This is used at the end of a parallel section.
michael@0 189 */
michael@0 190 void adoptWorkerAllocator(js::Allocator *workerAllocator);
michael@0 191
michael@0 192 bool activeAnalysis;
michael@0 193
michael@0 194 /* Type information about the scripts and objects in this compartment. */
michael@0 195 js::types::TypeCompartment types;
michael@0 196
michael@0 197 void *data;
michael@0 198
michael@0 199 private:
michael@0 200 js::ObjectMetadataCallback objectMetadataCallback;
michael@0 201
michael@0 202 js::SavedStacks savedStacks_;
michael@0 203
michael@0 204 js::WrapperMap crossCompartmentWrappers;
michael@0 205
michael@0 206 public:
michael@0 207 /* Last time at which an animation was played for a global in this compartment. */
michael@0 208 int64_t lastAnimationTime;
michael@0 209
michael@0 210 js::RegExpCompartment regExps;
michael@0 211
michael@0 212 /*
michael@0 213 * For generational GC, record whether a write barrier has added this
michael@0 214 * compartment's global to the store buffer since the last minor GC.
michael@0 215 *
michael@0 216 * This is used to avoid adding it to the store buffer on every write, which
michael@0 217 * can quickly fill the buffer and also cause performance problems.
michael@0 218 */
michael@0 219 bool globalWriteBarriered;
michael@0 220
michael@0 221 public:
michael@0 222 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
michael@0 223 size_t *tiAllocationSiteTables,
michael@0 224 size_t *tiArrayTypeTables,
michael@0 225 size_t *tiObjectTypeTables,
michael@0 226 size_t *compartmentObject,
michael@0 227 size_t *shapesCompartmentTables,
michael@0 228 size_t *crossCompartmentWrappers,
michael@0 229 size_t *regexpCompartment,
michael@0 230 size_t *debuggeesSet,
michael@0 231 size_t *savedStacksSet);
michael@0 232
michael@0 233 /*
michael@0 234 * Shared scope property tree, and arena-pool for allocating its nodes.
michael@0 235 */
michael@0 236 js::PropertyTree propertyTree;
michael@0 237
michael@0 238 /* Set of all unowned base shapes in the compartment. */
michael@0 239 js::BaseShapeSet baseShapes;
michael@0 240 void sweepBaseShapeTable();
michael@0 241
michael@0 242 /* Set of initial shapes in the compartment. */
michael@0 243 js::InitialShapeSet initialShapes;
michael@0 244 void sweepInitialShapeTable();
michael@0 245
michael@0 246 /* Set of default 'new' or lazy types in the compartment. */
michael@0 247 js::types::TypeObjectWithNewScriptSet newTypeObjects;
michael@0 248 js::types::TypeObjectWithNewScriptSet lazyTypeObjects;
michael@0 249 void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table);
michael@0 250 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
michael@0 251 void checkNewTypeObjectTableAfterMovingGC();
michael@0 252 void checkInitialShapesTableAfterMovingGC();
michael@0 253 void checkWrapperMapAfterMovingGC();
michael@0 254 #endif
michael@0 255
michael@0 256 /*
michael@0 257 * Hash table of all manually call site-cloned functions from within
michael@0 258 * self-hosted code. Cloning according to call site provides extra
michael@0 259 * sensitivity for type specialization and inlining.
michael@0 260 */
michael@0 261 js::CallsiteCloneTable callsiteClones;
michael@0 262 void sweepCallsiteClones();
michael@0 263
michael@0 264 /*
michael@0 265 * Lazily initialized script source object to use for scripts cloned
michael@0 266 * from the self-hosting global.
michael@0 267 */
michael@0 268 js::ReadBarriered<js::ScriptSourceObject> selfHostingScriptSource;
michael@0 269
michael@0 270 /* During GC, stores the index of this compartment in rt->compartments. */
michael@0 271 unsigned gcIndex;
michael@0 272
michael@0 273 /*
michael@0 274 * During GC, stores the head of a list of incoming pointers from gray cells.
michael@0 275 *
michael@0 276 * The objects in the list are either cross-compartment wrappers, or
michael@0 277 * debugger wrapper objects. The list link is either in the second extra
michael@0 278 * slot for the former, or a special slot for the latter.
michael@0 279 */
michael@0 280 JSObject *gcIncomingGrayPointers;
michael@0 281
michael@0 282 /* During GC, list of live array buffers with >1 view accumulated during tracing. */
michael@0 283 js::ArrayBufferVector gcLiveArrayBuffers;
michael@0 284
michael@0 285 /* Linked list of live weakmaps in this compartment. */
michael@0 286 js::WeakMapBase *gcWeakMapList;
michael@0 287
michael@0 288 private:
michael@0 289 enum {
michael@0 290 DebugFromC = 1 << 0,
michael@0 291 DebugFromJS = 1 << 1,
michael@0 292 DebugNeedDelazification = 1 << 2
michael@0 293 };
michael@0 294
michael@0 295 static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS;
michael@0 296
michael@0 297 unsigned debugModeBits; // see debugMode() below
michael@0 298
michael@0 299 public:
michael@0 300 JSCompartment(JS::Zone *zone, const JS::CompartmentOptions &options);
michael@0 301 ~JSCompartment();
michael@0 302
michael@0 303 bool init(JSContext *cx);
michael@0 304
michael@0 305 /* Mark cross-compartment wrappers. */
michael@0 306 void markCrossCompartmentWrappers(JSTracer *trc);
michael@0 307
michael@0 308 inline bool wrap(JSContext *cx, JS::MutableHandleValue vp,
michael@0 309 JS::HandleObject existing = js::NullPtr());
michael@0 310
michael@0 311 bool wrap(JSContext *cx, JSString **strp);
michael@0 312 bool wrap(JSContext *cx, js::HeapPtrString *strp);
michael@0 313 bool wrap(JSContext *cx, JS::MutableHandleObject obj,
michael@0 314 JS::HandleObject existingArg = js::NullPtr());
michael@0 315 bool wrapId(JSContext *cx, jsid *idp);
michael@0 316 bool wrap(JSContext *cx, js::PropertyOp *op);
michael@0 317 bool wrap(JSContext *cx, js::StrictPropertyOp *op);
michael@0 318 bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc);
michael@0 319 bool wrap(JSContext *cx, js::AutoIdVector &props);
michael@0 320
michael@0 321 bool putWrapper(JSContext *cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper);
michael@0 322
michael@0 323 js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) {
michael@0 324 return crossCompartmentWrappers.lookup(wrapped);
michael@0 325 }
michael@0 326
michael@0 327 void removeWrapper(js::WrapperMap::Ptr p) {
michael@0 328 crossCompartmentWrappers.remove(p);
michael@0 329 }
michael@0 330
michael@0 331 struct WrapperEnum : public js::WrapperMap::Enum {
michael@0 332 WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
michael@0 333 };
michael@0 334
michael@0 335 void trace(JSTracer *trc);
michael@0 336 void markRoots(JSTracer *trc);
michael@0 337 bool isDiscardingJitCode(JSTracer *trc);
michael@0 338 void sweep(js::FreeOp *fop, bool releaseTypes);
michael@0 339 void sweepCrossCompartmentWrappers();
michael@0 340 void purge();
michael@0 341 void clearTables();
michael@0 342
michael@0 343 bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
michael@0 344 void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
michael@0 345 bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const {
michael@0 346 return objectMetadataCallback(cx, obj);
michael@0 347 }
michael@0 348
michael@0 349 js::SavedStacks &savedStacks() { return savedStacks_; }
michael@0 350
michael@0 351 void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
michael@0 352
michael@0 353 js::DtoaCache dtoaCache;
michael@0 354
michael@0 355 /* Random number generator state, used by jsmath.cpp. */
michael@0 356 uint64_t rngState;
michael@0 357
michael@0 358 private:
michael@0 359 /*
michael@0 360 * Weak reference to each global in this compartment that is a debuggee.
michael@0 361 * Each global has its own list of debuggers.
michael@0 362 */
michael@0 363 js::GlobalObjectSet debuggees;
michael@0 364
michael@0 365 private:
michael@0 366 JSCompartment *thisForCtor() { return this; }
michael@0 367
michael@0 368 public:
michael@0 369 /*
michael@0 370 * There are dueling APIs for debug mode. It can be enabled or disabled via
michael@0 371 * JS_SetDebugModeForCompartment. It is automatically enabled and disabled
michael@0 372 * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set
michael@0 373 * if the C API wants debug mode and the DebugFromJS bit set if debuggees
michael@0 374 * is non-empty.
michael@0 375 *
michael@0 376 * When toggling on, DebugNeedDelazification is set to signal that
michael@0 377 * Debugger methods which depend on seeing all scripts (like findScripts)
michael@0 378 * need to delazify the scripts in the compartment first.
michael@0 379 */
michael@0 380 bool debugMode() const {
michael@0 381 return !!(debugModeBits & DebugModeFromMask);
michael@0 382 }
michael@0 383
michael@0 384 /* True if any scripts from this compartment are on the JS stack. */
michael@0 385 bool hasScriptsOnStack();
michael@0 386
michael@0 387 /*
michael@0 388 * Schedule the compartment to be delazified. Called from
michael@0 389 * LazyScript::Create.
michael@0 390 */
michael@0 391 void scheduleDelazificationForDebugMode() {
michael@0 392 debugModeBits |= DebugNeedDelazification;
michael@0 393 }
michael@0 394
michael@0 395 /*
michael@0 396 * If we scheduled delazification for turning on debug mode, delazify all
michael@0 397 * scripts.
michael@0 398 */
michael@0 399 bool ensureDelazifyScriptsForDebugMode(JSContext *cx);
michael@0 400
michael@0 401 private:
michael@0 402
michael@0 403 /* This is called only when debugMode() has just toggled. */
michael@0 404 bool updateJITForDebugMode(JSContext *maybecx, js::AutoDebugModeInvalidation &invalidate);
michael@0 405
michael@0 406 public:
michael@0 407 js::GlobalObjectSet &getDebuggees() { return debuggees; }
michael@0 408 bool addDebuggee(JSContext *cx, js::GlobalObject *global);
michael@0 409 bool addDebuggee(JSContext *cx, js::GlobalObject *global,
michael@0 410 js::AutoDebugModeInvalidation &invalidate);
michael@0 411 bool removeDebuggee(JSContext *cx, js::GlobalObject *global,
michael@0 412 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
michael@0 413 bool removeDebuggee(JSContext *cx, js::GlobalObject *global,
michael@0 414 js::AutoDebugModeInvalidation &invalidate,
michael@0 415 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
michael@0 416 void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
michael@0 417 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
michael@0 418 void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
michael@0 419 js::AutoDebugModeInvalidation &invalidate,
michael@0 420 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
michael@0 421 bool setDebugModeFromC(JSContext *cx, bool b,
michael@0 422 js::AutoDebugModeInvalidation &invalidate);
michael@0 423
michael@0 424 void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JS::HandleObject handler);
michael@0 425 void clearTraps(js::FreeOp *fop);
michael@0 426
michael@0 427 private:
michael@0 428 void sweepBreakpoints(js::FreeOp *fop);
michael@0 429
michael@0 430 public:
michael@0 431 js::WatchpointMap *watchpointMap;
michael@0 432
michael@0 433 js::ScriptCountsMap *scriptCountsMap;
michael@0 434
michael@0 435 js::DebugScriptMap *debugScriptMap;
michael@0 436
michael@0 437 /* Bookkeeping information for debug scope objects. */
michael@0 438 js::DebugScopes *debugScopes;
michael@0 439
michael@0 440 /*
michael@0 441 * List of potentially active iterators that may need deleted property
michael@0 442 * suppression.
michael@0 443 */
michael@0 444 js::NativeIterator *enumerators;
michael@0 445
michael@0 446 /* Used by memory reporters and invalid otherwise. */
michael@0 447 void *compartmentStats;
michael@0 448
michael@0 449 #ifdef JS_ION
michael@0 450 private:
michael@0 451 js::jit::JitCompartment *jitCompartment_;
michael@0 452
michael@0 453 public:
michael@0 454 bool ensureJitCompartmentExists(JSContext *cx);
michael@0 455 js::jit::JitCompartment *jitCompartment() {
michael@0 456 return jitCompartment_;
michael@0 457 }
michael@0 458 #endif
michael@0 459 };
michael@0 460
michael@0 461 inline bool
michael@0 462 JSRuntime::isAtomsZone(JS::Zone *zone)
michael@0 463 {
michael@0 464 return zone == atomsCompartment_->zone();
michael@0 465 }
michael@0 466
michael@0 467 // For use when changing the debug mode flag on one or more compartments.
michael@0 468 // Invalidate and discard JIT code since debug mode breaks JIT assumptions.
michael@0 469 //
michael@0 470 // AutoDebugModeInvalidation has two modes: compartment or zone
michael@0 471 // invalidation. While it is correct to always use compartment invalidation,
michael@0 472 // if you know ahead of time you need to invalidate a whole zone, it is faster
michael@0 473 // to invalidate the zone.
michael@0 474 //
michael@0 475 // Compartment invalidation only invalidates scripts belonging to that
michael@0 476 // compartment.
michael@0 477 //
michael@0 478 // Zone invalidation invalidates all scripts belonging to non-special
michael@0 479 // (i.e. those with principals) compartments of the zone.
michael@0 480 //
michael@0 481 // FIXME: Remove entirely once bug 716647 lands.
michael@0 482 //
michael@0 483 class js::AutoDebugModeInvalidation
michael@0 484 {
michael@0 485 JSCompartment *comp_;
michael@0 486 JS::Zone *zone_;
michael@0 487
michael@0 488 enum {
michael@0 489 NoNeed = 0,
michael@0 490 ToggledOn = 1,
michael@0 491 ToggledOff = 2
michael@0 492 } needInvalidation_;
michael@0 493
michael@0 494 public:
michael@0 495 explicit AutoDebugModeInvalidation(JSCompartment *comp)
michael@0 496 : comp_(comp), zone_(nullptr), needInvalidation_(NoNeed)
michael@0 497 { }
michael@0 498
michael@0 499 explicit AutoDebugModeInvalidation(JS::Zone *zone)
michael@0 500 : comp_(nullptr), zone_(zone), needInvalidation_(NoNeed)
michael@0 501 { }
michael@0 502
michael@0 503 #ifdef JS_ION
michael@0 504 ~AutoDebugModeInvalidation();
michael@0 505 #else
michael@0 506 ~AutoDebugModeInvalidation() { }
michael@0 507 #endif
michael@0 508
michael@0 509 bool isFor(JSCompartment *comp) {
michael@0 510 if (comp_)
michael@0 511 return comp == comp_;
michael@0 512 return comp->zone() == zone_;
michael@0 513 }
michael@0 514
michael@0 515 void scheduleInvalidation(bool debugMode) {
michael@0 516 // If we are scheduling invalidation for multiple compartments, they
michael@0 517 // must all agree on the toggle. This is so we can decide if we need
michael@0 518 // to invalidate on-stack scripts.
michael@0 519 MOZ_ASSERT_IF(needInvalidation_ != NoNeed,
michael@0 520 needInvalidation_ == (debugMode ? ToggledOn : ToggledOff));
michael@0 521 needInvalidation_ = debugMode ? ToggledOn : ToggledOff;
michael@0 522 }
michael@0 523 };
michael@0 524
michael@0 525 namespace js {
michael@0 526
michael@0 527 inline js::Handle<js::GlobalObject*>
michael@0 528 ExclusiveContext::global() const
michael@0 529 {
michael@0 530 /*
michael@0 531 * It's safe to use |unsafeGet()| here because any compartment that is
michael@0 532 * on-stack will be marked automatically, so there's no need for a read
michael@0 533 * barrier on it. Once the compartment is popped, the handle is no longer
michael@0 534 * safe to use.
michael@0 535 */
michael@0 536 MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first");
michael@0 537 return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
michael@0 538 }
michael@0 539
michael@0 540 class AssertCompartmentUnchanged
michael@0 541 {
michael@0 542 public:
michael@0 543 AssertCompartmentUnchanged(JSContext *cx
michael@0 544 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
michael@0 545 : cx(cx), oldCompartment(cx->compartment())
michael@0 546 {
michael@0 547 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 548 }
michael@0 549
michael@0 550 ~AssertCompartmentUnchanged() {
michael@0 551 JS_ASSERT(cx->compartment() == oldCompartment);
michael@0 552 }
michael@0 553
michael@0 554 protected:
michael@0 555 JSContext * const cx;
michael@0 556 JSCompartment * const oldCompartment;
michael@0 557 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 558 };
michael@0 559
michael@0 560 class AutoCompartment
michael@0 561 {
michael@0 562 ExclusiveContext * const cx_;
michael@0 563 JSCompartment * const origin_;
michael@0 564
michael@0 565 public:
michael@0 566 inline AutoCompartment(ExclusiveContext *cx, JSObject *target);
michael@0 567 inline AutoCompartment(ExclusiveContext *cx, JSCompartment *target);
michael@0 568 inline ~AutoCompartment();
michael@0 569
michael@0 570 ExclusiveContext *context() const { return cx_; }
michael@0 571 JSCompartment *origin() const { return origin_; }
michael@0 572
michael@0 573 private:
michael@0 574 AutoCompartment(const AutoCompartment &) MOZ_DELETE;
michael@0 575 AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE;
michael@0 576 };
michael@0 577
michael@0 578 /*
michael@0 579 * Use this to change the behavior of an AutoCompartment slightly on error. If
michael@0 580 * the exception happens to be an Error object, copy it to the origin compartment
michael@0 581 * instead of wrapping it.
michael@0 582 */
michael@0 583 class ErrorCopier
michael@0 584 {
michael@0 585 mozilla::Maybe<AutoCompartment> &ac;
michael@0 586 RootedObject scope;
michael@0 587
michael@0 588 public:
michael@0 589 ErrorCopier(mozilla::Maybe<AutoCompartment> &ac, JSObject *scope)
michael@0 590 : ac(ac), scope(ac.ref().context(), scope) {}
michael@0 591 ~ErrorCopier();
michael@0 592 };
michael@0 593
michael@0 594 /*
michael@0 595 * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that
michael@0 596 * are obtained from the cross-compartment map. However, these classes should
michael@0 597 * not be used if the wrapper will escape. For example, it should not be stored
michael@0 598 * in the heap.
michael@0 599 *
michael@0 600 * The AutoWrapper rooters are different from other autorooters because their
michael@0 601 * wrappers are marked on every GC slice rather than just the first one. If
michael@0 602 * there's some wrapper that we want to use temporarily without causing it to be
michael@0 603 * marked, we can use these AutoWrapper classes. If we get unlucky and a GC
michael@0 604 * slice runs during the code using the wrapper, the GC will mark the wrapper so
michael@0 605 * that it doesn't get swept out from under us. Otherwise, the wrapper needn't
michael@0 606 * be marked. This is useful in functions like JS_TransplantObject that
michael@0 607 * manipulate wrappers in compartments that may no longer be alive.
michael@0 608 */
michael@0 609
michael@0 610 /*
michael@0 611 * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It
michael@0 612 * should not be used in any other situations.
michael@0 613 */
michael@0 614 struct WrapperValue
michael@0 615 {
michael@0 616 /*
michael@0 617 * We use unsafeGet() in the constructors to avoid invoking a read barrier
michael@0 618 * on the wrapper, which may be dead (see the comment about bug 803376 in
michael@0 619 * jsgc.cpp regarding this). If there is an incremental GC while the wrapper
michael@0 620 * is in use, the AutoWrapper rooter will ensure the wrapper gets marked.
michael@0 621 */
michael@0 622 explicit WrapperValue(const WrapperMap::Ptr &ptr)
michael@0 623 : value(*ptr->value().unsafeGet())
michael@0 624 {}
michael@0 625
michael@0 626 explicit WrapperValue(const WrapperMap::Enum &e)
michael@0 627 : value(*e.front().value().unsafeGet())
michael@0 628 {}
michael@0 629
michael@0 630 Value &get() { return value; }
michael@0 631 Value get() const { return value; }
michael@0 632 operator const Value &() const { return value; }
michael@0 633 JSObject &toObject() const { return value.toObject(); }
michael@0 634
michael@0 635 private:
michael@0 636 Value value;
michael@0 637 };
michael@0 638
michael@0 639 class AutoWrapperVector : public AutoVectorRooter<WrapperValue>
michael@0 640 {
michael@0 641 public:
michael@0 642 explicit AutoWrapperVector(JSContext *cx
michael@0 643 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
michael@0 644 : AutoVectorRooter<WrapperValue>(cx, WRAPVECTOR)
michael@0 645 {
michael@0 646 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 647 }
michael@0 648
michael@0 649 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 650 };
michael@0 651
michael@0 652 class AutoWrapperRooter : private AutoGCRooter {
michael@0 653 public:
michael@0 654 AutoWrapperRooter(JSContext *cx, WrapperValue v
michael@0 655 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
michael@0 656 : AutoGCRooter(cx, WRAPPER), value(v)
michael@0 657 {
michael@0 658 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 659 }
michael@0 660
michael@0 661 operator JSObject *() const {
michael@0 662 return value.get().toObjectOrNull();
michael@0 663 }
michael@0 664
michael@0 665 friend void AutoGCRooter::trace(JSTracer *trc);
michael@0 666
michael@0 667 private:
michael@0 668 WrapperValue value;
michael@0 669 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 670 };
michael@0 671
michael@0 672 } /* namespace js */
michael@0 673
michael@0 674 #endif /* jscompartment_h */

mercurial