1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef jscompartment_h
8 #define jscompartment_h
9
10 #include "mozilla/Maybe.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Variant.h"
13 #include "mozilla/XorShift128PlusRNG.h"
14
15 #include "builtin/RegExp.h"
16 #include "gc/Barrier.h"
17 #include "gc/Zone.h"
18 #include "vm/GlobalObject.h"
19 #include "vm/PIC.h"
20 #include "vm/SavedStacks.h"
21 #include "vm/Time.h"
22
23 namespace js {
24
25 namespace jit {
26 class JitCompartment;
27 } // namespace jit
28
29 namespace gc {
30 template<class Node> class ComponentFinder;
31 } // namespace gc
32
33 struct NativeIterator;
34 class ClonedBlockObject;
35
36 /*
37 * A single-entry cache for some base-10 double-to-string conversions. This
38 * helps date-format-xparb.js. It also avoids skewing the results for
39 * v8-splay.js when measured by the SunSpider harness, where the splay tree
40 * initialization (which includes many repeated double-to-string conversions)
41 * is erroneously included in the measurement; see bug 562553.
42 */
43 class DtoaCache {
44 double d;
45 int base;
46 JSFlatString* s; // if s==nullptr, d and base are not valid
47
48 public:
DtoaCache()49 DtoaCache() : s(nullptr) {}
purge()50 void purge() { s = nullptr; }
51
lookup(int base,double d)52 JSFlatString* lookup(int base, double d) {
53 return this->s && base == this->base && d == this->d ? this->s : nullptr;
54 }
55
cache(int base,double d,JSFlatString * s)56 void cache(int base, double d, JSFlatString* s) {
57 this->base = base;
58 this->d = d;
59 this->s = s;
60 }
61 };
62
63 struct CrossCompartmentKey
64 {
65 enum Kind {
66 ObjectWrapper,
67 StringWrapper,
68 DebuggerScript,
69 DebuggerSource,
70 DebuggerObject,
71 DebuggerEnvironment
72 };
73
74 Kind kind;
75 JSObject* debugger;
76 js::gc::Cell* wrapped;
77
CrossCompartmentKeyCrossCompartmentKey78 explicit CrossCompartmentKey(JSObject* wrapped)
79 : kind(ObjectWrapper), debugger(nullptr), wrapped(wrapped)
80 {
81 MOZ_RELEASE_ASSERT(wrapped);
82 }
CrossCompartmentKeyCrossCompartmentKey83 explicit CrossCompartmentKey(JSString* wrapped)
84 : kind(StringWrapper), debugger(nullptr), wrapped(wrapped)
85 {
86 MOZ_RELEASE_ASSERT(wrapped);
87 }
CrossCompartmentKeyCrossCompartmentKey88 explicit CrossCompartmentKey(Value wrappedArg)
89 : kind(wrappedArg.isString() ? StringWrapper : ObjectWrapper),
90 debugger(nullptr),
91 wrapped((js::gc::Cell*)wrappedArg.toGCThing())
92 {
93 MOZ_RELEASE_ASSERT(wrappedArg.isString() || wrappedArg.isObject());
94 MOZ_RELEASE_ASSERT(wrapped);
95 }
CrossCompartmentKeyCrossCompartmentKey96 explicit CrossCompartmentKey(const RootedValue& wrappedArg)
97 : kind(wrappedArg.get().isString() ? StringWrapper : ObjectWrapper),
98 debugger(nullptr),
99 wrapped((js::gc::Cell*)wrappedArg.get().toGCThing())
100 {
101 MOZ_RELEASE_ASSERT(wrappedArg.isString() || wrappedArg.isObject());
102 MOZ_RELEASE_ASSERT(wrapped);
103 }
CrossCompartmentKeyCrossCompartmentKey104 CrossCompartmentKey(Kind kind, JSObject* dbg, js::gc::Cell* wrapped)
105 : kind(kind), debugger(dbg), wrapped(wrapped)
106 {
107 MOZ_RELEASE_ASSERT(dbg);
108 MOZ_RELEASE_ASSERT(wrapped);
109 }
110
111 private:
112 CrossCompartmentKey() = delete;
113 };
114
115 struct WrapperHasher : public DefaultHasher<CrossCompartmentKey>
116 {
hashWrapperHasher117 static HashNumber hash(const CrossCompartmentKey& key) {
118 static_assert(sizeof(HashNumber) == sizeof(uint32_t),
119 "subsequent code assumes a four-byte hash");
120 return uint32_t(uintptr_t(key.wrapped)) | uint32_t(key.kind);
121 }
122
matchWrapperHasher123 static bool match(const CrossCompartmentKey& l, const CrossCompartmentKey& k) {
124 return l.kind == k.kind && l.debugger == k.debugger && l.wrapped == k.wrapped;
125 }
126 };
127
128 typedef HashMap<CrossCompartmentKey, ReadBarrieredValue,
129 WrapperHasher, SystemAllocPolicy> WrapperMap;
130
131 // We must ensure that all newly allocated JSObjects get their metadata
132 // set. However, metadata callbacks may require the new object be in a sane
133 // state (eg, have its reserved slots initialized so they can get the
134 // sizeOfExcludingThis of the object). Therefore, for objects of certain
135 // JSClasses (those marked with JSCLASS_DELAY_METADATA_CALLBACK), it is not safe
136 // for the allocation paths to call the object metadata callback
137 // immediately. Instead, the JSClass-specific "constructor" C++ function up the
138 // stack makes a promise that it will ensure that the new object has its
139 // metadata set after the object is initialized.
140 //
141 // To help those constructor functions keep their promise of setting metadata,
142 // each compartment is in one of three states at any given time:
143 //
144 // * ImmediateMetadata: Allocators should set new object metadata immediately,
145 // as usual.
146 //
147 // * DelayMetadata: Allocators should *not* set new object metadata, it will be
148 // handled after reserved slots are initialized by custom code
149 // for the object's JSClass. The newly allocated object's
150 // JSClass *must* have the JSCLASS_DELAY_METADATA_CALLBACK flag
151 // set.
152 //
153 // * PendingMetadata: This object has been allocated and is still pending its
154 // metadata. This should never be the case when we begin an
155 // allocation, as a constructor function was supposed to have
156 // set the metadata of the previous object *before*
157 // allocating another object.
158 //
159 // The js::AutoSetNewObjectMetadata RAII class provides an ergonomic way for
160 // constructor functions to navigate state transitions, and its instances
161 // collectively maintain a stack of previous states. The stack is required to
162 // support the lazy resolution and allocation of global builtin constructors and
163 // prototype objects. The initial (and intuitively most common) state is
164 // ImmediateMetadata.
165 //
166 // Without the presence of internal errors (such as OOM), transitions between
167 // the states are as follows:
168 //
169 // ImmediateMetadata .----- previous state on stack
170 // | | ^
171 // | via constructor | |
172 // | | | via setting the new
173 // | via constructor | | object's metadata
174 // | .-----------------------' |
175 // | | |
176 // V V |
177 // DelayMetadata -------------------------> PendingMetadata
178 // via allocation
179 //
180 // In the presence of internal errors, we do not set the new object's metadata
181 // (if it was even allocated) and reset to the previous state on the stack.
182
183 struct ImmediateMetadata { };
184 struct DelayMetadata { };
185 using PendingMetadata = ReadBarrieredObject;
186
187 using NewObjectMetadataState = mozilla::Variant<ImmediateMetadata,
188 DelayMetadata,
189 PendingMetadata>;
190
191 class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter
192 {
193 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
194
195 JSContext* cx_;
196 NewObjectMetadataState prevState_;
197
198 AutoSetNewObjectMetadata(const AutoSetNewObjectMetadata& aOther) = delete;
199 void operator=(const AutoSetNewObjectMetadata& aOther) = delete;
200
201 protected:
trace(JSTracer * trc)202 virtual void trace(JSTracer* trc) override {
203 if (prevState_.is<PendingMetadata>()) {
204 TraceRoot(trc,
205 prevState_.as<PendingMetadata>().unsafeUnbarrieredForTracing(),
206 "Object pending metadata");
207 }
208 }
209
210 public:
211 explicit AutoSetNewObjectMetadata(ExclusiveContext* ecx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
212 ~AutoSetNewObjectMetadata();
213 };
214
215 } /* namespace js */
216
217 namespace js {
218 class DebugScopes;
219 class ObjectWeakMap;
220 class WatchpointMap;
221 class WeakMapBase;
222 } // namespace js
223
224 struct JSCompartment
225 {
226 JS::CompartmentOptions options_;
227
228 private:
229 JS::Zone* zone_;
230 JSRuntime* runtime_;
231
232 public:
233 /*
234 * The principals associated with this compartment. Note that the
235 * same several compartments may share the same principals and
236 * that a compartment may change principals during its lifetime
237 * (e.g. in case of lazy parsing).
238 */
principalsJSCompartment239 inline JSPrincipals* principals() {
240 return principals_;
241 }
setPrincipalsJSCompartment242 inline void setPrincipals(JSPrincipals* principals) {
243 if (principals_ == principals)
244 return;
245
246 // If we change principals, we need to unlink immediately this
247 // compartment from its PerformanceGroup. For one thing, the
248 // performance data we collect should not be improperly associated
249 // with a group to which we do not belong anymore. For another thing,
250 // we use `principals()` as part of the key to map compartments
251 // to a `PerformanceGroup`, so if we do not unlink now, this will
252 // be too late once we have updated `principals_`.
253 performanceMonitoring.unlink();
254 principals_ = principals;
255 }
isSystemJSCompartment256 inline bool isSystem() const {
257 return isSystem_;
258 }
setIsSystemJSCompartment259 inline void setIsSystem(bool isSystem) {
260 if (isSystem_ == isSystem)
261 return;
262
263 // If we change `isSystem*(`, we need to unlink immediately this
264 // compartment from its PerformanceGroup. For one thing, the
265 // performance data we collect should not be improperly associated
266 // to a group to which we do not belong anymore. For another thing,
267 // we use `isSystem()` as part of the key to map compartments
268 // to a `PerformanceGroup`, so if we do not unlink now, this will
269 // be too late once we have updated `isSystem_`.
270 performanceMonitoring.unlink();
271 isSystem_ = isSystem;
272 }
273
isAtomsCompartmentJSCompartment274 bool isAtomsCompartment() const {
275 return isAtomsCompartment_;
276 }
setIsAtomsCompartmentJSCompartment277 void setIsAtomsCompartment() {
278 isAtomsCompartment_ = true;
279 }
280
281 private:
282 JSPrincipals* principals_;
283 bool isSystem_;
284 bool isAtomsCompartment_;
285
286 public:
287 bool isSelfHosting;
288 bool marked;
289 bool warnedAboutFlagsArgument;
290 bool warnedAboutExprClosure;
291
292 // A null add-on ID means that the compartment is not associated with an
293 // add-on.
294 JSAddonId* const addonId;
295
296 #ifdef DEBUG
297 bool firedOnNewGlobalObject;
298 #endif
299
markJSCompartment300 void mark() { marked = true; }
301
302 private:
303 friend struct JSRuntime;
304 friend struct JSContext;
305 friend class js::ExclusiveContext;
306 js::ReadBarrieredGlobalObject global_;
307
308 unsigned enterCompartmentDepth;
309 int64_t startInterval;
310
311 public:
312 js::PerformanceGroupHolder performanceMonitoring;
313
enterJSCompartment314 void enter() {
315 enterCompartmentDepth++;
316 }
leaveJSCompartment317 void leave() {
318 enterCompartmentDepth--;
319 }
hasBeenEnteredJSCompartment320 bool hasBeenEntered() { return !!enterCompartmentDepth; }
321
zoneJSCompartment322 JS::Zone* zone() { return zone_; }
zoneJSCompartment323 const JS::Zone* zone() const { return zone_; }
optionsJSCompartment324 JS::CompartmentOptions& options() { return options_; }
optionsJSCompartment325 const JS::CompartmentOptions& options() const { return options_; }
326
runtimeFromMainThreadJSCompartment327 JSRuntime* runtimeFromMainThread() {
328 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
329 return runtime_;
330 }
331
332 // Note: Unrestricted access to the zone's runtime from an arbitrary
333 // thread can easily lead to races. Use this method very carefully.
runtimeFromAnyThreadJSCompartment334 JSRuntime* runtimeFromAnyThread() const {
335 return runtime_;
336 }
337
338 /*
339 * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
340 * (b) the compartment's global has been collected. The latter can happen
341 * if e.g. a string in a compartment is rooted but no object is, and thus
342 * the global isn't rooted, and thus the global can be finalized while the
343 * compartment lives on.
344 *
345 * In contrast, JSObject::global() is infallible because marking a JSObject
346 * always marks its global as well.
347 * TODO: add infallible JSScript::global()
348 */
349 inline js::GlobalObject* maybeGlobal() const;
350
351 /* An unbarriered getter for use while tracing. */
352 inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
353
354 inline void initGlobal(js::GlobalObject& global);
355
356 public:
357 void* data;
358
359 private:
360 js::ObjectMetadataCallback objectMetadataCallback;
361
362 js::SavedStacks savedStacks_;
363
364 js::WrapperMap crossCompartmentWrappers;
365
366 public:
367 /* Last time at which an animation was played for a global in this compartment. */
368 int64_t lastAnimationTime;
369
370 js::RegExpCompartment regExps;
371
372 /*
373 * For generational GC, record whether a write barrier has added this
374 * compartment's global to the store buffer since the last minor GC.
375 *
376 * This is used to avoid adding it to the store buffer on every write, which
377 * can quickly fill the buffer and also cause performance problems.
378 */
379 bool globalWriteBarriered;
380
381 // Non-zero if any typed objects in this compartment might be neutered.
382 int32_t neuteredTypedObjects;
383
384 private:
385 friend class js::AutoSetNewObjectMetadata;
386 js::NewObjectMetadataState objectMetadataState;
387
388 public:
389 // Recompute the probability with which this compartment should record
390 // profiling data (stack traces, allocations log, etc.) about each
391 // allocation. We consult the probabilities requested by the Debugger
392 // instances observing us, if any.
chooseAllocationSamplingProbabilityJSCompartment393 void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); }
394
hasObjectPendingMetadataJSCompartment395 bool hasObjectPendingMetadata() const { return objectMetadataState.is<js::PendingMetadata>(); }
396
setObjectPendingMetadataJSCompartment397 void setObjectPendingMetadata(JSContext* cx, JSObject* obj) {
398 MOZ_ASSERT(objectMetadataState.is<js::DelayMetadata>());
399 objectMetadataState = js::NewObjectMetadataState(js::PendingMetadata(obj));
400 }
401
setObjectPendingMetadataJSCompartment402 void setObjectPendingMetadata(js::ExclusiveContext* ecx, JSObject* obj) {
403 if (JSContext* cx = ecx->maybeJSContext())
404 setObjectPendingMetadata(cx, obj);
405 }
406
407 public:
408 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
409 size_t* tiAllocationSiteTables,
410 size_t* tiArrayTypeTables,
411 size_t* tiObjectTypeTables,
412 size_t* compartmentObject,
413 size_t* compartmentTables,
414 size_t* innerViews,
415 size_t* lazyArrayBuffers,
416 size_t* objectMetadataTables,
417 size_t* crossCompartmentWrappers,
418 size_t* regexpCompartment,
419 size_t* savedStacksSet,
420 size_t* nonSyntacticLexicalScopes);
421
422 /*
423 * Shared scope property tree, and arena-pool for allocating its nodes.
424 */
425 js::PropertyTree propertyTree;
426
427 /* Set of all unowned base shapes in the compartment. */
428 js::BaseShapeSet baseShapes;
429 void sweepBaseShapeTable();
430
431 /* Set of initial shapes in the compartment. */
432 js::InitialShapeSet initialShapes;
433 void sweepInitialShapeTable();
434
435 // Object group tables and other state in the compartment.
436 js::ObjectGroupCompartment objectGroups;
437
438 #ifdef JSGC_HASH_TABLE_CHECKS
439 void checkInitialShapesTableAfterMovingGC();
440 void checkWrapperMapAfterMovingGC();
441 void checkBaseShapeTableAfterMovingGC();
442 #endif
443
444 /*
445 * Lazily initialized script source object to use for scripts cloned
446 * from the self-hosting global.
447 */
448 js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
449
450 // Keep track of the metadata objects which can be associated with each JS
451 // object. Both keys and values are in this compartment.
452 js::ObjectWeakMap* objectMetadataTable;
453
454 // Map from array buffers to views sharing that storage.
455 js::InnerViewTable innerViews;
456
457 // Inline transparent typed objects do not initially have an array buffer,
458 // but can have that buffer created lazily if it is accessed later. This
459 // table manages references from such typed objects to their buffers.
460 js::ObjectWeakMap* lazyArrayBuffers;
461
462 // All unboxed layouts in the compartment.
463 mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
464
465 private:
466 // All non-syntactic lexical scopes in the compartment. These are kept in
467 // a map because when loading scripts into a non-syntactic scope, we need
468 // to use the same lexical scope to persist lexical bindings.
469 js::ObjectWeakMap* nonSyntacticLexicalScopes_;
470
471 public:
472 /* During GC, stores the index of this compartment in rt->compartments. */
473 unsigned gcIndex;
474
475 /*
476 * During GC, stores the head of a list of incoming pointers from gray cells.
477 *
478 * The objects in the list are either cross-compartment wrappers, or
479 * debugger wrapper objects. The list link is either in the second extra
480 * slot for the former, or a special slot for the latter.
481 */
482 JSObject* gcIncomingGrayPointers;
483
484 private:
485 /* Whether to preserve JIT code on non-shrinking GCs. */
486 bool gcPreserveJitCode;
487
488 enum {
489 IsDebuggee = 1 << 0,
490 DebuggerObservesAllExecution = 1 << 1,
491 DebuggerObservesAsmJS = 1 << 2,
492 DebuggerObservesCoverage = 1 << 3,
493 DebuggerNeedsDelazification = 1 << 4
494 };
495
496 unsigned debugModeBits;
497
498 static const unsigned DebuggerObservesMask = IsDebuggee |
499 DebuggerObservesAllExecution |
500 DebuggerObservesCoverage |
501 DebuggerObservesAsmJS;
502
503 void updateDebuggerObservesFlag(unsigned flag);
504
505 public:
506 JSCompartment(JS::Zone* zone, const JS::CompartmentOptions& options);
507 ~JSCompartment();
508
509 bool init(JSContext* maybecx);
510
511 inline bool wrap(JSContext* cx, JS::MutableHandleValue vp,
512 JS::HandleObject existing = nullptr);
513
514 bool wrap(JSContext* cx, js::MutableHandleString strp);
515 bool wrap(JSContext* cx, JS::MutableHandleObject obj,
516 JS::HandleObject existingArg = nullptr);
517 bool wrap(JSContext* cx, JS::MutableHandle<js::PropertyDescriptor> desc);
518
wrapJSCompartment519 template<typename T> bool wrap(JSContext* cx, JS::AutoVectorRooter<T>& vec) {
520 for (size_t i = 0; i < vec.length(); ++i) {
521 if (!wrap(cx, vec[i]))
522 return false;
523 }
524 return true;
525 };
526
527 bool putWrapper(JSContext* cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper);
528
lookupWrapperJSCompartment529 js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) const {
530 return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped));
531 }
532
removeWrapperJSCompartment533 void removeWrapper(js::WrapperMap::Ptr p) {
534 crossCompartmentWrappers.remove(p);
535 }
536
537 struct WrapperEnum : public js::WrapperMap::Enum {
WrapperEnumJSCompartment::WrapperEnum538 explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
539 };
540
541 js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(JSContext* cx,
542 js::HandleObject enclosingStatic,
543 js::HandleObject enclosingScope);
544 js::ClonedBlockObject* getNonSyntacticLexicalScope(JSObject* enclosingScope) const;
545
546 /*
547 * This method traces data that is live iff we know that this compartment's
548 * global is still live.
549 */
550 void trace(JSTracer* trc);
551 /*
552 * This method traces JSCompartment-owned GC roots that are considered live
553 * regardless of whether the JSCompartment itself is still live.
554 */
555 void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
556 /*
557 * These methods mark pointers that cross compartment boundaries. They are
558 * called in per-zone GCs to prevent the wrappers' outgoing edges from
559 * dangling (full GCs naturally follow pointers across compartments) and
560 * when compacting to update cross-compartment pointers.
561 */
562 void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
563 static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
564
preserveJitCodeJSCompartment565 bool preserveJitCode() { return gcPreserveJitCode; }
566
567 void sweepAfterMinorGC();
568
569 void sweepInnerViews();
570 void sweepCrossCompartmentWrappers();
571 void sweepSavedStacks();
572 void sweepGlobalObject(js::FreeOp* fop);
573 void sweepObjectPendingMetadata();
574 void sweepSelfHostingScriptSource();
575 void sweepJitCompartment(js::FreeOp* fop);
576 void sweepRegExps();
577 void sweepDebugScopes();
578 void sweepNativeIterators();
579 void sweepTemplateObjects();
580
581 void purge();
582 void clearTables();
583
584 static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
585 void fixupInitialShapeTable();
586 void fixupAfterMovingGC();
587 void fixupGlobal();
588
hasObjectMetadataCallbackJSCompartment589 bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
getObjectMetadataCallbackJSCompartment590 js::ObjectMetadataCallback getObjectMetadataCallback() const { return objectMetadataCallback; }
591 void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
forgetObjectMetadataCallbackJSCompartment592 void forgetObjectMetadataCallback() {
593 objectMetadataCallback = nullptr;
594 }
595 void setNewObjectMetadata(JSContext* cx, JSObject* obj);
596 void clearObjectMetadata();
addressOfMetadataCallbackJSCompartment597 const void* addressOfMetadataCallback() const {
598 return &objectMetadataCallback;
599 }
600
savedStacksJSCompartment601 js::SavedStacks& savedStacks() { return savedStacks_; }
602
603 void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
604
605 MOZ_MUST_USE bool findDeadProxyZoneEdges(bool* foundAny);
606
607 js::DtoaCache dtoaCache;
608
609 // Random number generator for Math.random().
610 mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
611
612 // Initialize randomNumberGenerator if needed.
613 void ensureRandomNumberGenerator();
614
615 private:
616 mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
617
618 public:
619 js::HashNumber randomHashCode();
620
621 mozilla::HashCodeScrambler randomHashCodeScrambler();
622
623 private:
thisForCtorJSCompartment624 JSCompartment* thisForCtor() { return this; }
625
626 public:
627 //
628 // The Debugger observes execution on a frame-by-frame basis. The
629 // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
630 // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
631 // enumerated below.
632 //
633 // 1. When a compartment's isDebuggee() == true, relazification and lazy
634 // parsing are disabled.
635 //
636 // Whether AOT asm.js is disabled is togglable by the Debugger API. By
637 // default it is disabled. See debuggerObservesAsmJS below.
638 //
639 // 2. When a compartment's debuggerObservesAllExecution() == true, all of
640 // the compartment's scripts are considered debuggee scripts.
641 //
642 // 3. A script is considered a debuggee script either when, per above, its
643 // compartment is observing all execution, or if it has breakpoints set.
644 //
645 // 4. A debuggee script always pushes a debuggee frame.
646 //
647 // 5. A debuggee frame calls all slow path Debugger hooks in the
648 // Interpreter and Baseline. A debuggee frame implies that its script's
649 // BaselineScript, if extant, has been compiled with debug hook calls.
650 //
651 // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
652 // that the compiled BaselineScript is compiled with debug hook calls
653 // when attempting to enter Baseline.
654 //
655 // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
656 // attempt to enter Ion.
657 //
658 // Note that a debuggee frame may exist without its script being a
659 // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
660 // frame in which it is evaluating as a debuggee frame.
661 //
662
663 // True if this compartment's global is a debuggee of some Debugger
664 // object.
isDebuggeeJSCompartment665 bool isDebuggee() const { return !!(debugModeBits & IsDebuggee); }
setIsDebuggeeJSCompartment666 void setIsDebuggee() { debugModeBits |= IsDebuggee; }
667 void unsetIsDebuggee();
668
669 // True if this compartment's global is a debuggee of some Debugger
670 // object with a live hook that observes all execution; e.g.,
671 // onEnterFrame.
debuggerObservesAllExecutionJSCompartment672 bool debuggerObservesAllExecution() const {
673 static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution;
674 return (debugModeBits & Mask) == Mask;
675 }
updateDebuggerObservesAllExecutionJSCompartment676 void updateDebuggerObservesAllExecution() {
677 updateDebuggerObservesFlag(DebuggerObservesAllExecution);
678 }
679
680 // True if this compartment's global is a debuggee of some Debugger object
681 // whose allowUnobservedAsmJS flag is false.
682 //
683 // Note that since AOT asm.js functions cannot bail out, this flag really
684 // means "observe asm.js from this point forward". We cannot make
685 // already-compiled asm.js code observable to Debugger.
debuggerObservesAsmJSJSCompartment686 bool debuggerObservesAsmJS() const {
687 static const unsigned Mask = IsDebuggee | DebuggerObservesAsmJS;
688 return (debugModeBits & Mask) == Mask;
689 }
updateDebuggerObservesAsmJSJSCompartment690 void updateDebuggerObservesAsmJS() {
691 updateDebuggerObservesFlag(DebuggerObservesAsmJS);
692 }
693
694 // True if this compartment's global is a debuggee of some Debugger object
695 // whose collectCoverageInfo flag is true.
debuggerObservesCoverageJSCompartment696 bool debuggerObservesCoverage() const {
697 static const unsigned Mask = DebuggerObservesCoverage;
698 return (debugModeBits & Mask) == Mask;
699 }
700 void updateDebuggerObservesCoverage();
701
702 // The code coverage can be enabled either for each compartment, with the
703 // Debugger API, or for the entire runtime.
704 bool collectCoverage() const;
705 void clearScriptCounts();
706
needsDelazificationForDebuggerJSCompartment707 bool needsDelazificationForDebugger() const {
708 return debugModeBits & DebuggerNeedsDelazification;
709 }
710
711 /*
712 * Schedule the compartment to be delazified. Called from
713 * LazyScript::Create.
714 */
scheduleDelazificationForDebuggerJSCompartment715 void scheduleDelazificationForDebugger() { debugModeBits |= DebuggerNeedsDelazification; }
716
717 /*
718 * If we scheduled delazification for turning on debug mode, delazify all
719 * scripts.
720 */
721 bool ensureDelazifyScriptsForDebugger(JSContext* cx);
722
723 void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
724
725 private:
726 void sweepBreakpoints(js::FreeOp* fop);
727
728 public:
729 js::WatchpointMap* watchpointMap;
730
731 js::ScriptCountsMap* scriptCountsMap;
732
733 js::DebugScriptMap* debugScriptMap;
734
735 /* Bookkeeping information for debug scope objects. */
736 js::DebugScopes* debugScopes;
737
738 /*
739 * List of potentially active iterators that may need deleted property
740 * suppression.
741 */
742 js::NativeIterator* enumerators;
743
744 /* Used by memory reporters and invalid otherwise. */
745 void* compartmentStats;
746
747 // These flags help us to discover if a compartment that shouldn't be alive
748 // manages to outlive a GC.
749 bool scheduledForDestruction;
750 bool maybeAlive;
751
752 private:
753 js::jit::JitCompartment* jitCompartment_;
754
755 js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
756 js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
757
758 public:
759 bool ensureJitCompartmentExists(JSContext* cx);
jitCompartmentJSCompartment760 js::jit::JitCompartment* jitCompartment() {
761 return jitCompartment_;
762 }
763
764 enum DeprecatedLanguageExtension {
765 DeprecatedForEach = 0, // JS 1.6+
766 // NO LONGER USING 1
767 DeprecatedLegacyGenerator = 2, // JS 1.7+
768 DeprecatedExpressionClosure = 3, // Added in JS 1.8
769 // NO LONGER USING 4
770 // NO LONGER USING 5
771 // NO LONGER USING 6
772 DeprecatedFlagsArgument = 7, // JS 1.3 or older
773 // NO LONGER USING 8
774 // NO LONGER USING 9
775 DeprecatedLanguageExtensionCount
776 };
777
778 js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
779
780 private:
781 // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
782 bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount];
783
784 void reportTelemetry();
785
786 public:
787 void addTelemetry(const char* filename, DeprecatedLanguageExtension e);
788
789 public:
790 // Aggregated output used to collect JSScript hit counts when code coverage
791 // is enabled.
792 js::coverage::LCovCompartment lcovOutput;
793 };
794
795 inline bool
isAtomsZone(const JS::Zone * zone)796 JSRuntime::isAtomsZone(const JS::Zone* zone) const
797 {
798 return zone == atomsCompartment_->zone();
799 }
800
801 namespace js {
802
803 // We only set the maybeAlive flag for objects and scripts. It's assumed that,
804 // if a compartment is alive, then it will have at least some live object or
805 // script it in. Even if we get this wrong, the worst that will happen is that
806 // scheduledForDestruction will be set on the compartment, which will cause
807 // some extra GC activity to try to free the compartment.
SetMaybeAliveFlag(T * thing)808 template<typename T> inline void SetMaybeAliveFlag(T* thing) {}
SetMaybeAliveFlag(JSObject * thing)809 template<> inline void SetMaybeAliveFlag(JSObject* thing) {thing->compartment()->maybeAlive = true;}
SetMaybeAliveFlag(JSScript * thing)810 template<> inline void SetMaybeAliveFlag(JSScript* thing) {thing->compartment()->maybeAlive = true;}
811
812 inline js::Handle<js::GlobalObject*>
global()813 ExclusiveContext::global() const
814 {
815 /*
816 * It's safe to use |unsafeGet()| here because any compartment that is
817 * on-stack will be marked automatically, so there's no need for a read
818 * barrier on it. Once the compartment is popped, the handle is no longer
819 * safe to use.
820 */
821 MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first");
822 return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
823 }
824
825 class MOZ_RAII AssertCompartmentUnchanged
826 {
827 public:
AssertCompartmentUnchanged(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)828 explicit AssertCompartmentUnchanged(JSContext* cx
829 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
830 : cx(cx), oldCompartment(cx->compartment())
831 {
832 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
833 }
834
~AssertCompartmentUnchanged()835 ~AssertCompartmentUnchanged() {
836 MOZ_ASSERT(cx->compartment() == oldCompartment);
837 }
838
839 protected:
840 JSContext * const cx;
841 JSCompartment * const oldCompartment;
842 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
843 };
844
845 class AutoCompartment
846 {
847 ExclusiveContext * const cx_;
848 JSCompartment * const origin_;
849
850 public:
851 inline AutoCompartment(ExclusiveContext* cx, JSObject* target);
852 inline AutoCompartment(ExclusiveContext* cx, JSCompartment* target);
853 inline ~AutoCompartment();
854
context()855 ExclusiveContext* context() const { return cx_; }
origin()856 JSCompartment* origin() const { return origin_; }
857
858 private:
859 AutoCompartment(const AutoCompartment&) = delete;
860 AutoCompartment & operator=(const AutoCompartment&) = delete;
861 };
862
863 /*
864 * Use this to change the behavior of an AutoCompartment slightly on error. If
865 * the exception happens to be an Error object, copy it to the origin compartment
866 * instead of wrapping it.
867 */
868 class ErrorCopier
869 {
870 mozilla::Maybe<AutoCompartment>& ac;
871
872 public:
ErrorCopier(mozilla::Maybe<AutoCompartment> & ac)873 explicit ErrorCopier(mozilla::Maybe<AutoCompartment>& ac)
874 : ac(ac) {}
875 ~ErrorCopier();
876 };
877
878 /*
879 * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that
880 * are obtained from the cross-compartment map. However, these classes should
881 * not be used if the wrapper will escape. For example, it should not be stored
882 * in the heap.
883 *
884 * The AutoWrapper rooters are different from other autorooters because their
885 * wrappers are marked on every GC slice rather than just the first one. If
886 * there's some wrapper that we want to use temporarily without causing it to be
887 * marked, we can use these AutoWrapper classes. If we get unlucky and a GC
888 * slice runs during the code using the wrapper, the GC will mark the wrapper so
889 * that it doesn't get swept out from under us. Otherwise, the wrapper needn't
890 * be marked. This is useful in functions like JS_TransplantObject that
891 * manipulate wrappers in compartments that may no longer be alive.
892 */
893
894 /*
895 * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It
896 * should not be used in any other situations.
897 */
898 struct WrapperValue
899 {
900 /*
901 * We use unsafeGet() in the constructors to avoid invoking a read barrier
902 * on the wrapper, which may be dead (see the comment about bug 803376 in
903 * jsgc.cpp regarding this). If there is an incremental GC while the wrapper
904 * is in use, the AutoWrapper rooter will ensure the wrapper gets marked.
905 */
WrapperValueWrapperValue906 explicit WrapperValue(const WrapperMap::Ptr& ptr)
907 : value(*ptr->value().unsafeGet())
908 {}
909
WrapperValueWrapperValue910 explicit WrapperValue(const WrapperMap::Enum& e)
911 : value(*e.front().value().unsafeGet())
912 {}
913
getWrapperValue914 Value& get() { return value; }
getWrapperValue915 Value get() const { return value; }
916 operator const Value&() const { return value; }
toObjectWrapperValue917 JSObject& toObject() const { return value.toObject(); }
918
919 private:
920 Value value;
921 };
922
923 class MOZ_RAII AutoWrapperVector : public JS::AutoVectorRooterBase<WrapperValue>
924 {
925 public:
AutoWrapperVector(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)926 explicit AutoWrapperVector(JSContext* cx
927 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
928 : AutoVectorRooterBase<WrapperValue>(cx, WRAPVECTOR)
929 {
930 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
931 }
932
933 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
934 };
935
936 class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
937 public:
AutoWrapperRooter(JSContext * cx,WrapperValue v MOZ_GUARD_OBJECT_NOTIFIER_PARAM)938 AutoWrapperRooter(JSContext* cx, WrapperValue v
939 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
940 : JS::AutoGCRooter(cx, WRAPPER), value(v)
941 {
942 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
943 }
944
945 operator JSObject*() const {
946 return value.get().toObjectOrNull();
947 }
948
949 friend void JS::AutoGCRooter::trace(JSTracer* trc);
950
951 private:
952 WrapperValue value;
953 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
954 };
955
956 } /* namespace js */
957
958 #endif /* jscompartment_h */
959