Thu, 15 Jan 2015 15:55:04 +0100
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> ∾ |
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 */ |