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/. */
7 #ifndef jscompartment_h
8 #define jscompartment_h
10 #include "mozilla/MemoryReporting.h"
13 #include "builtin/RegExp.h"
15 #include "vm/GlobalObject.h"
17 #include "vm/SavedStacks.h"
26 template<class Node
> class ComponentFinder
;
29 struct NativeIterator
;
32 * A single-entry cache for some base-10 double-to-string conversions. This
33 * helps date-format-xparb.js. It also avoids skewing the results for
34 * v8-splay.js when measured by the SunSpider harness, where the splay tree
35 * initialization (which includes many repeated double-to-string conversions)
36 * is erroneously included in the measurement; see bug 562553.
41 JSFlatString
* s
; // if s==nullptr, d and base are not valid
44 DtoaCache() : s(nullptr) {}
45 void purge() { s
= nullptr; }
47 JSFlatString
* lookup(int base
, double d
) {
48 return this->s
&& base
== this->base
&& d
== this->d
? this->s
: nullptr;
51 void cache(int base
, double d
, JSFlatString
* s
) {
58 struct CrossCompartmentKey
71 js::gc::Cell
* wrapped
;
73 explicit CrossCompartmentKey(JSObject
* wrapped
)
74 : kind(ObjectWrapper
), debugger(nullptr), wrapped(wrapped
)
76 MOZ_RELEASE_ASSERT(wrapped
);
78 explicit CrossCompartmentKey(JSString
* wrapped
)
79 : kind(StringWrapper
), debugger(nullptr), wrapped(wrapped
)
81 MOZ_RELEASE_ASSERT(wrapped
);
83 explicit CrossCompartmentKey(Value wrappedArg
)
84 : kind(wrappedArg
.isString() ? StringWrapper
: ObjectWrapper
),
86 wrapped((js::gc::Cell
*)wrappedArg
.toGCThing())
88 MOZ_RELEASE_ASSERT(wrappedArg
.isString() || wrappedArg
.isObject());
89 MOZ_RELEASE_ASSERT(wrapped
);
91 explicit CrossCompartmentKey(const RootedValue
& wrappedArg
)
92 : kind(wrappedArg
.get().isString() ? StringWrapper
: ObjectWrapper
),
94 wrapped((js::gc::Cell
*)wrappedArg
.get().toGCThing())
96 MOZ_RELEASE_ASSERT(wrappedArg
.isString() || wrappedArg
.isObject());
97 MOZ_RELEASE_ASSERT(wrapped
);
99 CrossCompartmentKey(Kind kind
, JSObject
* dbg
, js::gc::Cell
* wrapped
)
100 : kind(kind
), debugger(dbg
), wrapped(wrapped
)
102 MOZ_RELEASE_ASSERT(dbg
);
103 MOZ_RELEASE_ASSERT(wrapped
);
107 CrossCompartmentKey() = delete;
110 struct WrapperHasher
: public DefaultHasher
<CrossCompartmentKey
>
112 static HashNumber
hash(const CrossCompartmentKey
& key
) {
113 MOZ_ASSERT(!IsPoisonedPtr(key
.wrapped
));
114 static_assert(sizeof(HashNumber
) == sizeof(uint32_t),
115 "subsequent code assumes a four-byte hash");
116 return uint32_t(uintptr_t(key
.wrapped
)) | uint32_t(key
.kind
);
119 static bool match(const CrossCompartmentKey
& l
, const CrossCompartmentKey
& k
) {
120 return l
.kind
== k
.kind
&& l
.debugger
== k
.debugger
&& l
.wrapped
== k
.wrapped
;
124 typedef HashMap
<CrossCompartmentKey
, ReadBarrieredValue
,
125 WrapperHasher
, SystemAllocPolicy
> WrapperMap
;
130 struct TypeInferenceSizes
;
135 class LazyArrayBufferTable
;
141 JS::CompartmentOptions options_
;
148 JSPrincipals
* principals
;
153 // A null add-on ID means that the compartment is not associated with an
158 bool firedOnNewGlobalObject
;
161 void mark() { marked
= true; }
164 friend struct JSRuntime
;
165 friend struct JSContext
;
166 friend class js::ExclusiveContext
;
167 js::ReadBarrieredGlobalObject global_
;
169 unsigned enterCompartmentDepth
;
170 int64_t startInterval
;
175 if (addonId
&& !enterCompartmentDepth
) {
176 startInterval
= PRMJ_Now();
178 enterCompartmentDepth
++;
181 enterCompartmentDepth
--;
182 if (addonId
&& !enterCompartmentDepth
) {
183 totalTime
+= (PRMJ_Now() - startInterval
);
186 bool hasBeenEntered() { return !!enterCompartmentDepth
; }
188 JS::Zone
* zone() { return zone_
; }
189 const JS::Zone
* zone() const { return zone_
; }
190 JS::CompartmentOptions
& options() { return options_
; }
191 const JS::CompartmentOptions
& options() const { return options_
; }
193 JSRuntime
* runtimeFromMainThread() {
194 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_
));
198 // Note: Unrestricted access to the zone's runtime from an arbitrary
199 // thread can easily lead to races. Use this method very carefully.
200 JSRuntime
* runtimeFromAnyThread() const {
205 * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
206 * (b) the compartment's global has been collected. The latter can happen
207 * if e.g. a string in a compartment is rooted but no object is, and thus
208 * the global isn't rooted, and thus the global can be finalized while the
209 * compartment lives on.
211 * In contrast, JSObject::global() is infallible because marking a JSObject
212 * always marks its global as well.
213 * TODO: add infallible JSScript::global()
215 inline js::GlobalObject
* maybeGlobal() const;
217 /* An unbarriered getter for use while tracing. */
218 inline js::GlobalObject
* unsafeUnbarrieredMaybeGlobal() const;
220 inline void initGlobal(js::GlobalObject
& global
);
224 * Moves all data from the allocator |workerAllocator|, which was
225 * in use by a parallel worker, into the compartment's main
226 * allocator. This is used at the end of a parallel section.
228 void adoptWorkerAllocator(js::Allocator
* workerAllocator
);
230 /* Type information about the scripts and objects in this compartment. */
231 js::types::TypeCompartment types
;
236 js::ObjectMetadataCallback objectMetadataCallback
;
238 js::SavedStacks savedStacks_
;
240 js::WrapperMap crossCompartmentWrappers
;
243 /* Last time at which an animation was played for a global in this compartment. */
244 int64_t lastAnimationTime
;
246 js::RegExpCompartment regExps
;
249 * For generational GC, record whether a write barrier has added this
250 * compartment's global to the store buffer since the last minor GC.
252 * This is used to avoid adding it to the store buffer on every write, which
253 * can quickly fill the buffer and also cause performance problems.
255 bool globalWriteBarriered
;
257 // Non-zero if any typed objects in this compartment might be neutered.
258 int32_t neuteredTypedObjects
;
261 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
,
262 size_t* tiAllocationSiteTables
,
263 size_t* tiArrayTypeTables
,
264 size_t* tiObjectTypeTables
,
265 size_t* compartmentObject
,
266 size_t* compartmentTables
,
268 size_t* lazyArrayBuffers
,
269 size_t* crossCompartmentWrappers
,
270 size_t* regexpCompartment
,
271 size_t* savedStacksSet
);
274 * Shared scope property tree, and arena-pool for allocating its nodes.
276 js::PropertyTree propertyTree
;
278 /* Set of all unowned base shapes in the compartment. */
279 js::BaseShapeSet baseShapes
;
280 void sweepBaseShapeTable();
282 /* Set of initial shapes in the compartment. */
283 js::InitialShapeSet initialShapes
;
284 void sweepInitialShapeTable();
286 /* Set of default 'new' or lazy types in the compartment. */
287 js::types::NewTypeObjectTable newTypeObjects
;
288 js::types::NewTypeObjectTable lazyTypeObjects
;
289 void sweepNewTypeObjectTable(js::types::NewTypeObjectTable
& table
);
291 #ifdef JSGC_HASH_TABLE_CHECKS
292 void checkTypeObjectTablesAfterMovingGC();
293 void checkTypeObjectTableAfterMovingGC(js::types::NewTypeObjectTable
& table
);
294 void checkInitialShapesTableAfterMovingGC();
295 void checkWrapperMapAfterMovingGC();
296 void checkBaseShapeTableAfterMovingGC();
300 * Hash table of all manually call site-cloned functions from within
301 * self-hosted code. Cloning according to call site provides extra
302 * sensitivity for type specialization and inlining.
304 js::CallsiteCloneTable callsiteClones
;
305 void sweepCallsiteClones();
308 * Lazily initialized script source object to use for scripts cloned
309 * from the self-hosting global.
311 js::ReadBarrieredScriptSourceObject selfHostingScriptSource
;
313 // Map from array buffers to views sharing that storage.
314 js::InnerViewTable innerViews
;
316 // Map from typed objects to array buffers lazily created for them.
317 js::LazyArrayBufferTable
* lazyArrayBuffers
;
319 /* During GC, stores the index of this compartment in rt->compartments. */
323 * During GC, stores the head of a list of incoming pointers from gray cells.
325 * The objects in the list are either cross-compartment wrappers, or
326 * debugger wrapper objects. The list link is either in the second extra
327 * slot for the former, or a special slot for the latter.
329 JSObject
* gcIncomingGrayPointers
;
331 /* Linked list of live weakmaps in this compartment. */
332 js::WeakMapBase
* gcWeakMapList
;
335 /* Whether to preserve JIT code on non-shrinking GCs. */
336 bool gcPreserveJitCode
;
340 DebugObservesAllExecution
= 1 << 1,
341 DebugNeedDelazification
= 1 << 2
344 // DebugObservesAllExecution is a submode of DebugMode, and is only valid
345 // when DebugMode is also set.
346 static const unsigned DebugExecutionMask
= DebugMode
| DebugObservesAllExecution
;
348 unsigned debugModeBits
;
351 JSCompartment(JS::Zone
* zone
, const JS::CompartmentOptions
& options
);
354 bool init(JSContext
* cx
);
356 /* Mark cross-compartment wrappers. */
357 void markCrossCompartmentWrappers(JSTracer
* trc
);
359 inline bool wrap(JSContext
* cx
, JS::MutableHandleValue vp
,
360 JS::HandleObject existing
= js::NullPtr());
362 bool wrap(JSContext
* cx
, js::MutableHandleString strp
);
363 bool wrap(JSContext
* cx
, JS::MutableHandleObject obj
,
364 JS::HandleObject existingArg
= js::NullPtr());
365 bool wrap(JSContext
* cx
, JS::MutableHandle
<js::PropertyDescriptor
> desc
);
366 bool wrap(JSContext
* cx
, JS::MutableHandle
<js::PropDesc
> desc
);
368 template<typename T
> bool wrap(JSContext
* cx
, JS::AutoVectorRooter
<T
>& vec
) {
369 for (size_t i
= 0; i
< vec
.length(); ++i
) {
370 if (!wrap(cx
, vec
[i
]))
376 bool putWrapper(JSContext
* cx
, const js::CrossCompartmentKey
& wrapped
, const js::Value
& wrapper
);
378 js::WrapperMap::Ptr
lookupWrapper(const js::Value
& wrapped
) {
379 return crossCompartmentWrappers
.lookup(js::CrossCompartmentKey(wrapped
));
382 void removeWrapper(js::WrapperMap::Ptr p
) {
383 crossCompartmentWrappers
.remove(p
);
386 struct WrapperEnum
: public js::WrapperMap::Enum
{
387 explicit WrapperEnum(JSCompartment
* c
) : js::WrapperMap::Enum(c
->crossCompartmentWrappers
) {}
390 void trace(JSTracer
* trc
);
391 void markRoots(JSTracer
* trc
);
392 bool preserveJitCode() { return gcPreserveJitCode
; }
394 void sweepInnerViews();
395 void sweepCrossCompartmentWrappers();
396 void sweepTypeObjectTables();
397 void sweepSavedStacks();
398 void sweepGlobalObject(js::FreeOp
* fop
);
399 void sweepSelfHostingScriptSource();
400 void sweepJitCompartment(js::FreeOp
* fop
);
402 void sweepDebugScopes();
403 void sweepWeakMaps();
404 void sweepNativeIterators();
409 #ifdef JSGC_COMPACTING
410 void fixupInitialShapeTable();
411 void fixupNewTypeObjectTable(js::types::NewTypeObjectTable
& table
);
412 void fixupAfterMovingGC();
414 void fixupBaseShapeTable();
417 bool hasObjectMetadataCallback() const { return objectMetadataCallback
; }
418 void setObjectMetadataCallback(js::ObjectMetadataCallback callback
);
419 void forgetObjectMetadataCallback() {
420 objectMetadataCallback
= nullptr;
422 bool callObjectMetadataCallback(JSContext
* cx
, JSObject
** obj
) const {
423 return objectMetadataCallback(cx
, obj
);
425 const void* addressOfMetadataCallback() const {
426 return &objectMetadataCallback
;
429 js::SavedStacks
& savedStacks() { return savedStacks_
; }
431 void findOutgoingEdges(js::gc::ComponentFinder
<JS::Zone
>& finder
);
433 js::DtoaCache dtoaCache
;
435 /* Random number generator state, used by jsmath.cpp. */
439 JSCompartment
* thisForCtor() { return this; }
443 // The Debugger observes execution on a frame-by-frame basis. The
444 // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
445 // InterpreterFrame::isDebuggee, and Baseline::isDebuggee are enumerated
448 // 1. When a compartment's isDebuggee() == true, relazification, lazy
449 // parsing, and asm.js are disabled.
451 // 2. When a compartment's debugObservesAllExecution() == true, all of the
452 // compartment's scripts are considered debuggee scripts.
454 // 3. A script is considered a debuggee script either when, per above, its
455 // compartment is observing all execution, or if it has breakpoints set.
457 // 4. A debuggee script always pushes a debuggee frame.
459 // 5. A debuggee frame calls all slow path Debugger hooks in the
460 // Interpreter and Baseline. A debuggee frame implies that its script's
461 // BaselineScript, if extant, has been compiled with debug hook calls.
463 // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
464 // that the compiled BaselineScript is compiled with debug hook calls
465 // when attempting to enter Baseline.
467 // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
468 // attempt to enter Ion.
470 // Note that a debuggee frame may exist without its script being a
471 // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
472 // frame in which it is evaluating as a debuggee frame.
475 // True if this compartment's global is a debuggee of some Debugger
477 bool isDebuggee() const { return !!(debugModeBits
& DebugMode
); }
478 void setIsDebuggee() { debugModeBits
|= DebugMode
; }
479 void unsetIsDebuggee();
481 // True if an this compartment's global is a debuggee of some Debugger
482 // object with a live hook that observes all execution; e.g.,
484 bool debugObservesAllExecution() const {
485 return (debugModeBits
& DebugExecutionMask
) == DebugExecutionMask
;
487 void setDebugObservesAllExecution() {
488 MOZ_ASSERT(isDebuggee());
489 debugModeBits
|= DebugObservesAllExecution
;
491 void unsetDebugObservesAllExecution() {
492 MOZ_ASSERT(isDebuggee());
493 debugModeBits
&= ~DebugObservesAllExecution
;
497 * Schedule the compartment to be delazified. Called from
498 * LazyScript::Create.
500 void scheduleDelazificationForDebugMode() { debugModeBits
|= DebugNeedDelazification
; }
503 * If we scheduled delazification for turning on debug mode, delazify all
506 bool ensureDelazifyScriptsForDebugMode(JSContext
* cx
);
508 void clearBreakpointsIn(js::FreeOp
* fop
, js::Debugger
* dbg
, JS::HandleObject handler
);
511 void sweepBreakpoints(js::FreeOp
* fop
);
514 js::WatchpointMap
* watchpointMap
;
516 js::ScriptCountsMap
* scriptCountsMap
;
518 js::DebugScriptMap
* debugScriptMap
;
520 /* Bookkeeping information for debug scope objects. */
521 js::DebugScopes
* debugScopes
;
524 * List of potentially active iterators that may need deleted property
527 js::NativeIterator
* enumerators
;
529 /* Used by memory reporters and invalid otherwise. */
530 void* compartmentStats
;
532 // These flags help us to discover if a compartment that shouldn't be alive
533 // manages to outlive a GC.
534 bool scheduledForDestruction
;
538 js::jit::JitCompartment
* jitCompartment_
;
541 bool ensureJitCompartmentExists(JSContext
* cx
);
542 js::jit::JitCompartment
* jitCompartment() {
543 return jitCompartment_
;
548 JSRuntime::isAtomsZone(JS::Zone
* zone
)
550 return zone
== atomsCompartment_
->zone();
555 inline js::Handle
<js::GlobalObject
*>
556 ExclusiveContext::global() const
559 * It's safe to use |unsafeGet()| here because any compartment that is
560 * on-stack will be marked automatically, so there's no need for a read
561 * barrier on it. Once the compartment is popped, the handle is no longer
564 MOZ_ASSERT(compartment_
, "Caller needs to enter a compartment first");
565 return Handle
<GlobalObject
*>::fromMarkedLocation(compartment_
->global_
.unsafeGet());
568 class AssertCompartmentUnchanged
571 explicit AssertCompartmentUnchanged(JSContext
* cx
572 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
573 : cx(cx
), oldCompartment(cx
->compartment())
575 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
578 ~AssertCompartmentUnchanged() {
579 MOZ_ASSERT(cx
->compartment() == oldCompartment
);
583 JSContext
* const cx
;
584 JSCompartment
* const oldCompartment
;
585 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
588 class AutoCompartment
590 ExclusiveContext
* const cx_
;
591 JSCompartment
* const origin_
;
594 inline AutoCompartment(ExclusiveContext
* cx
, JSObject
* target
);
595 inline AutoCompartment(ExclusiveContext
* cx
, JSCompartment
* target
);
596 inline ~AutoCompartment();
598 ExclusiveContext
* context() const { return cx_
; }
599 JSCompartment
* origin() const { return origin_
; }
602 AutoCompartment(const AutoCompartment
&) = delete;
603 AutoCompartment
& operator=(const AutoCompartment
&) = delete;
607 * Use this to change the behavior of an AutoCompartment slightly on error. If
608 * the exception happens to be an Error object, copy it to the origin compartment
609 * instead of wrapping it.
613 mozilla::Maybe
<AutoCompartment
>& ac
;
616 explicit ErrorCopier(mozilla::Maybe
<AutoCompartment
>& ac
)
622 * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that
623 * are obtained from the cross-compartment map. However, these classes should
624 * not be used if the wrapper will escape. For example, it should not be stored
627 * The AutoWrapper rooters are different from other autorooters because their
628 * wrappers are marked on every GC slice rather than just the first one. If
629 * there's some wrapper that we want to use temporarily without causing it to be
630 * marked, we can use these AutoWrapper classes. If we get unlucky and a GC
631 * slice runs during the code using the wrapper, the GC will mark the wrapper so
632 * that it doesn't get swept out from under us. Otherwise, the wrapper needn't
633 * be marked. This is useful in functions like JS_TransplantObject that
634 * manipulate wrappers in compartments that may no longer be alive.
638 * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It
639 * should not be used in any other situations.
644 * We use unsafeGet() in the constructors to avoid invoking a read barrier
645 * on the wrapper, which may be dead (see the comment about bug 803376 in
646 * jsgc.cpp regarding this). If there is an incremental GC while the wrapper
647 * is in use, the AutoWrapper rooter will ensure the wrapper gets marked.
649 explicit WrapperValue(const WrapperMap::Ptr
& ptr
)
650 : value(*ptr
->value().unsafeGet())
653 explicit WrapperValue(const WrapperMap::Enum
& e
)
654 : value(*e
.front().value().unsafeGet())
657 Value
& get() { return value
; }
658 Value
get() const { return value
; }
659 operator const Value
&() const { return value
; }
660 JSObject
& toObject() const { return value
.toObject(); }
666 class AutoWrapperVector
: public AutoVectorRooter
<WrapperValue
>
669 explicit AutoWrapperVector(JSContext
* cx
670 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
671 : AutoVectorRooter
<WrapperValue
>(cx
, WRAPVECTOR
)
673 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
676 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
679 class AutoWrapperRooter
: private JS::AutoGCRooter
{
681 AutoWrapperRooter(JSContext
* cx
, WrapperValue v
682 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
683 : JS::AutoGCRooter(cx
, WRAPPER
), value(v
)
685 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
688 operator JSObject
*() const {
689 return value
.get().toObjectOrNull();
692 friend void JS::AutoGCRooter::trace(JSTracer
* trc
);
696 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
701 #endif /* jscompartment_h */