Line data Source code
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/LinkedList.h"
11 : #include "mozilla/Maybe.h"
12 : #include "mozilla/MemoryReporting.h"
13 : #include "mozilla/Tuple.h"
14 : #include "mozilla/Variant.h"
15 : #include "mozilla/XorShift128PlusRNG.h"
16 :
17 : #include <stddef.h>
18 :
19 : #include "frontend/LanguageExtensions.h"
20 : #include "gc/Barrier.h"
21 : #include "gc/NurseryAwareHashMap.h"
22 : #include "gc/Zone.h"
23 : #include "vm/PIC.h"
24 : #include "vm/RegExpShared.h"
25 : #include "vm/SavedStacks.h"
26 : #include "vm/TemplateRegistry.h"
27 : #include "vm/Time.h"
28 : #include "wasm/WasmCompartment.h"
29 :
30 : namespace js {
31 :
32 : namespace jit {
33 : class JitCompartment;
34 : } // namespace jit
35 :
36 : namespace gc {
37 : template <typename Node, typename Derived> class ComponentFinder;
38 : } // namespace gc
39 :
40 : class GlobalObject;
41 : class LexicalEnvironmentObject;
42 : class ScriptSourceObject;
43 : struct NativeIterator;
44 :
45 : /*
46 : * A single-entry cache for some base-10 double-to-string conversions. This
47 : * helps date-format-xparb.js. It also avoids skewing the results for
48 : * v8-splay.js when measured by the SunSpider harness, where the splay tree
49 : * initialization (which includes many repeated double-to-string conversions)
50 : * is erroneously included in the measurement; see bug 562553.
51 : */
52 : class DtoaCache {
53 : double d;
54 : int base;
55 : JSFlatString* s; // if s==nullptr, d and base are not valid
56 :
57 : public:
58 315 : DtoaCache() : s(nullptr) {}
59 223 : void purge() { s = nullptr; }
60 :
61 452 : JSFlatString* lookup(int base, double d) {
62 452 : return this->s && base == this->base && d == this->d ? this->s : nullptr;
63 : }
64 :
65 451 : void cache(int base, double d, JSFlatString* s) {
66 451 : this->base = base;
67 451 : this->d = d;
68 451 : this->s = s;
69 451 : }
70 :
71 : #ifdef JSGC_HASH_TABLE_CHECKS
72 : void checkCacheAfterMovingGC();
73 : #endif
74 : };
75 :
76 : // Cache to speed up the group/shape lookup in ProxyObject::create. A proxy's
77 : // group/shape is only determined by the Class + proto, so a small cache for
78 : // this is very effective in practice.
79 315 : class NewProxyCache
80 : {
81 : struct Entry {
82 : ObjectGroup* group;
83 : Shape* shape;
84 : };
85 : static const size_t NumEntries = 4;
86 : mozilla::UniquePtr<Entry[], JS::FreePolicy> entries_;
87 :
88 : public:
89 9333 : MOZ_ALWAYS_INLINE bool lookup(const Class* clasp, TaggedProto proto,
90 : ObjectGroup** group, Shape** shape) const
91 : {
92 9333 : if (!entries_)
93 290 : return false;
94 9540 : for (size_t i = 0; i < NumEntries; i++) {
95 9500 : const Entry& entry = entries_[i];
96 9500 : if (entry.group && entry.group->clasp() == clasp && entry.group->proto() == proto) {
97 9003 : *group = entry.group;
98 9003 : *shape = entry.shape;
99 9003 : return true;
100 : }
101 : }
102 40 : return false;
103 : }
104 330 : void add(ObjectGroup* group, Shape* shape) {
105 330 : MOZ_ASSERT(group && shape);
106 330 : if (!entries_) {
107 290 : entries_.reset(js_pod_calloc<Entry>(NumEntries));
108 290 : if (!entries_)
109 0 : return;
110 : } else {
111 160 : for (size_t i = NumEntries - 1; i > 0; i--)
112 120 : entries_[i] = entries_[i - 1];
113 : }
114 330 : entries_[0].group = group;
115 330 : entries_[0].shape = shape;
116 : }
117 223 : void purge() {
118 223 : entries_.reset();
119 223 : }
120 : };
121 :
122 112705 : class CrossCompartmentKey
123 : {
124 : public:
125 : enum DebuggerObjectKind : uint8_t { DebuggerSource, DebuggerEnvironment, DebuggerObject,
126 : DebuggerWasmScript, DebuggerWasmSource };
127 : using DebuggerAndObject = mozilla::Tuple<NativeObject*, JSObject*, DebuggerObjectKind>;
128 : using DebuggerAndScript = mozilla::Tuple<NativeObject*, JSScript*>;
129 : using WrappedType = mozilla::Variant<
130 : JSObject*,
131 : JSString*,
132 : DebuggerAndScript,
133 : DebuggerAndObject>;
134 :
135 565 : explicit CrossCompartmentKey(JSObject* obj) : wrapped(obj) { MOZ_RELEASE_ASSERT(obj); }
136 : explicit CrossCompartmentKey(JSString* str) : wrapped(str) { MOZ_RELEASE_ASSERT(str); }
137 57434 : explicit CrossCompartmentKey(const JS::Value& v)
138 57434 : : wrapped(v.isString() ? WrappedType(v.toString()) : WrappedType(&v.toObject()))
139 57434 : {}
140 0 : explicit CrossCompartmentKey(NativeObject* debugger, JSObject* obj, DebuggerObjectKind kind)
141 0 : : wrapped(DebuggerAndObject(debugger, obj, kind))
142 : {
143 0 : MOZ_RELEASE_ASSERT(debugger);
144 0 : MOZ_RELEASE_ASSERT(obj);
145 0 : }
146 0 : explicit CrossCompartmentKey(NativeObject* debugger, JSScript* script)
147 0 : : wrapped(DebuggerAndScript(debugger, script))
148 : {
149 0 : MOZ_RELEASE_ASSERT(debugger);
150 0 : MOZ_RELEASE_ASSERT(script);
151 0 : }
152 :
153 0 : bool operator==(const CrossCompartmentKey& other) const { return wrapped == other.wrapped; }
154 2101 : bool operator!=(const CrossCompartmentKey& other) const { return wrapped != other.wrapped; }
155 :
156 28864 : template <typename T> bool is() const { return wrapped.is<T>(); }
157 37 : template <typename T> const T& as() const { return wrapped.as<T>(); }
158 :
159 : template <typename F>
160 76351 : auto applyToWrapped(F f) -> decltype(f(static_cast<JSObject**>(nullptr))) {
161 : using ReturnType = decltype(f(static_cast<JSObject**>(nullptr)));
162 : struct WrappedMatcher {
163 : F f_;
164 76351 : explicit WrappedMatcher(F f) : f_(f) {}
165 75352 : ReturnType match(JSObject*& obj) { return f_(&obj); }
166 999 : ReturnType match(JSString*& str) { return f_(&str); }
167 0 : ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<1>(tpl)); }
168 0 : ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<1>(tpl)); }
169 76351 : } matcher(f);
170 76351 : return wrapped.match(matcher);
171 : }
172 :
173 : template <typename F>
174 2101 : auto applyToDebugger(F f) -> decltype(f(static_cast<NativeObject**>(nullptr))) {
175 : using ReturnType = decltype(f(static_cast<NativeObject**>(nullptr)));
176 : struct DebuggerMatcher {
177 : F f_;
178 2101 : explicit DebuggerMatcher(F f) : f_(f) {}
179 2101 : ReturnType match(JSObject*& obj) { return ReturnType(); }
180 0 : ReturnType match(JSString*& str) { return ReturnType(); }
181 0 : ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<0>(tpl)); }
182 0 : ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<0>(tpl)); }
183 2101 : } matcher(f);
184 2101 : return wrapped.match(matcher);
185 : }
186 :
187 64641 : JSCompartment* compartment() {
188 : struct GetCompartmentFunctor {
189 63972 : JSCompartment* operator()(JSObject** tp) const { return (*tp)->compartment(); }
190 0 : JSCompartment* operator()(JSScript** tp) const { return (*tp)->compartment(); }
191 669 : JSCompartment* operator()(JSString** tp) const { return nullptr; }
192 : };
193 64641 : return applyToWrapped(GetCompartmentFunctor());
194 : }
195 :
196 : struct Hasher : public DefaultHasher<CrossCompartmentKey>
197 : {
198 : struct HashFunctor {
199 57451 : HashNumber match(JSObject* obj) { return DefaultHasher<JSObject*>::hash(obj); }
200 655 : HashNumber match(JSString* str) { return DefaultHasher<JSString*>::hash(str); }
201 0 : HashNumber match(const DebuggerAndScript& tpl) {
202 0 : return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
203 0 : DefaultHasher<JSScript*>::hash(mozilla::Get<1>(tpl));
204 : }
205 0 : HashNumber match(const DebuggerAndObject& tpl) {
206 0 : return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
207 0 : DefaultHasher<JSObject*>::hash(mozilla::Get<1>(tpl)) ^
208 0 : (mozilla::Get<2>(tpl) << 5);
209 : }
210 : };
211 58106 : static HashNumber hash(const CrossCompartmentKey& key) {
212 58106 : return key.wrapped.match(HashFunctor());
213 : }
214 :
215 26321 : static bool match(const CrossCompartmentKey& l, const CrossCompartmentKey& k) {
216 26321 : return l.wrapped == k.wrapped;
217 : }
218 : };
219 :
220 9609 : bool isTenured() const {
221 : struct IsTenuredFunctor {
222 : using ReturnType = bool;
223 9279 : ReturnType operator()(JSObject** tp) { return !IsInsideNursery(*tp); }
224 0 : ReturnType operator()(JSScript** tp) { return true; }
225 330 : ReturnType operator()(JSString** tp) { return true; }
226 : };
227 9609 : return const_cast<CrossCompartmentKey*>(this)->applyToWrapped(IsTenuredFunctor());
228 : }
229 :
230 : void trace(JSTracer* trc);
231 : bool needsSweep();
232 :
233 : private:
234 : CrossCompartmentKey() = delete;
235 : WrappedType wrapped;
236 : };
237 :
238 : // The data structure for storing CCWs, which has a map per target compartment
239 : // so we can access them easily. Note string CCWs are stored separately from the
240 : // others because they have target compartment nullptr.
241 315 : class WrapperMap
242 : {
243 : static const size_t InitialInnerMapSize = 4;
244 :
245 : using InnerMap = NurseryAwareHashMap<CrossCompartmentKey,
246 : JS::Value,
247 : CrossCompartmentKey::Hasher,
248 : SystemAllocPolicy>;
249 : using OuterMap = GCHashMap<JSCompartment*,
250 : InnerMap,
251 : DefaultHasher<JSCompartment*>,
252 : SystemAllocPolicy>;
253 :
254 : OuterMap map;
255 :
256 : public:
257 624 : class Enum
258 : {
259 : public:
260 : enum SkipStrings : bool {
261 : WithStrings = false,
262 : WithoutStrings = true
263 : };
264 :
265 : private:
266 : Enum(const Enum&) = delete;
267 : void operator=(const Enum&) = delete;
268 :
269 1491 : void goToNext() {
270 1491 : if (outer.isNothing())
271 11 : return;
272 1506 : for (; !outer->empty(); outer->popFront()) {
273 1266 : JSCompartment* c = outer->front().key();
274 : // Need to skip string at first, because the filter may not be
275 : // happy with a nullptr.
276 1266 : if (!c && skipStrings)
277 13 : continue;
278 1253 : if (filter && !filter->match(c))
279 0 : continue;
280 1253 : InnerMap& m = outer->front().value();
281 1253 : if (!m.empty()) {
282 1253 : if (inner.isSome())
283 1053 : inner.reset();
284 1253 : inner.emplace(m);
285 1253 : outer->popFront();
286 1253 : return;
287 : }
288 : }
289 : }
290 :
291 : mozilla::Maybe<OuterMap::Enum> outer;
292 : mozilla::Maybe<InnerMap::Enum> inner;
293 : const CompartmentFilter* filter;
294 : SkipStrings skipStrings;
295 :
296 : public:
297 211 : explicit Enum(WrapperMap& m, SkipStrings s = WithStrings) :
298 211 : filter(nullptr), skipStrings(s) {
299 211 : outer.emplace(m.map);
300 211 : goToNext();
301 211 : }
302 :
303 16 : Enum(WrapperMap& m, const CompartmentFilter& f, SkipStrings s = WithStrings) :
304 16 : filter(&f), skipStrings(s) {
305 16 : outer.emplace(m.map);
306 16 : goToNext();
307 16 : }
308 :
309 397 : Enum(WrapperMap& m, JSCompartment* target) {
310 : // Leave the outer map as nothing and only iterate the inner map we
311 : // find here.
312 397 : auto p = m.map.lookup(target);
313 397 : if (p)
314 12 : inner.emplace(p->value());
315 397 : }
316 :
317 13982 : bool empty() const {
318 17148 : return (outer.isNothing() || outer->empty()) &&
319 18654 : (inner.isNothing() || inner->empty());
320 : }
321 :
322 6716 : InnerMap::Entry& front() const {
323 6716 : MOZ_ASSERT(inner.isSome() && !inner->empty());
324 6716 : return inner->front();
325 : }
326 :
327 6679 : void popFront() {
328 6679 : MOZ_ASSERT(!empty());
329 6679 : if (!inner->empty()) {
330 6679 : inner->popFront();
331 6679 : if (!inner->empty())
332 5415 : return;
333 : }
334 1264 : goToNext();
335 : }
336 :
337 34 : void removeFront() {
338 34 : MOZ_ASSERT(inner.isSome());
339 34 : inner->removeFront();
340 34 : }
341 : };
342 :
343 : class Ptr : public InnerMap::Ptr
344 : {
345 : friend class WrapperMap;
346 :
347 : InnerMap* map;
348 :
349 28257 : Ptr() : InnerMap::Ptr(), map(nullptr) {}
350 20133 : Ptr(const InnerMap::Ptr& p, InnerMap& m) : InnerMap::Ptr(p), map(&m) {}
351 : };
352 :
353 315 : MOZ_MUST_USE bool init(uint32_t len) { return map.init(len); }
354 :
355 15 : bool empty() {
356 15 : if (map.empty())
357 15 : return true;
358 0 : for (OuterMap::Enum e(map); !e.empty(); e.popFront()) {
359 0 : if (!e.front().value().empty())
360 0 : return false;
361 : }
362 0 : return true;
363 : }
364 :
365 48390 : Ptr lookup(const CrossCompartmentKey& k) const {
366 48390 : auto op = map.lookup(const_cast<CrossCompartmentKey&>(k).compartment());
367 48390 : if (op) {
368 40220 : auto ip = op->value().lookup(k);
369 40220 : if (ip)
370 20133 : return Ptr(ip, op->value());
371 : }
372 28257 : return Ptr();
373 : }
374 :
375 10 : void remove(Ptr p) {
376 10 : if (p)
377 10 : p.map->remove(p);
378 10 : }
379 :
380 9609 : MOZ_MUST_USE bool put(const CrossCompartmentKey& k, const JS::Value& v) {
381 9609 : JSCompartment* c = const_cast<CrossCompartmentKey&>(k).compartment();
382 9609 : MOZ_ASSERT(k.is<JSString*>() == !c);
383 9609 : auto p = map.lookupForAdd(c);
384 9609 : if (!p) {
385 3362 : InnerMap m;
386 1681 : if (!m.init(InitialInnerMapSize) || !map.add(p, c, mozilla::Move(m)))
387 0 : return false;
388 : }
389 9609 : return p->value().put(k, v);
390 : }
391 :
392 0 : size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
393 0 : size_t size = map.sizeOfExcludingThis(mallocSizeOf);
394 0 : for (OuterMap::Enum e(map); !e.empty(); e.popFront())
395 0 : size += e.front().value().sizeOfExcludingThis(mallocSizeOf);
396 0 : return size;
397 : }
398 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
399 : size_t size = map.sizeOfIncludingThis(mallocSizeOf);
400 : for (OuterMap::Enum e(map); !e.empty(); e.popFront())
401 : size += e.front().value().sizeOfIncludingThis(mallocSizeOf);
402 : return size;
403 : }
404 :
405 16 : bool hasNurseryAllocatedWrapperEntries(const CompartmentFilter& f) {
406 16 : for (OuterMap::Enum e(map); !e.empty(); e.popFront()) {
407 0 : JSCompartment* c = e.front().key();
408 0 : if (c && !f.match(c))
409 0 : continue;
410 0 : InnerMap& m = e.front().value();
411 0 : if (m.hasNurseryEntries())
412 0 : return true;
413 : }
414 16 : return false;
415 : }
416 :
417 2415 : void sweepAfterMinorGC(JSTracer* trc) {
418 14843 : for (OuterMap::Enum e(map); !e.empty(); e.popFront()) {
419 12428 : InnerMap& m = e.front().value();
420 12428 : m.sweepAfterMinorGC(trc);
421 12428 : if (m.empty())
422 31 : e.removeFront();
423 : }
424 2415 : }
425 :
426 0 : void sweep() {
427 0 : for (OuterMap::Enum e(map); !e.empty(); e.popFront()) {
428 0 : InnerMap& m = e.front().value();
429 0 : m.sweep();
430 0 : if (m.empty())
431 0 : e.removeFront();
432 : }
433 0 : }
434 : };
435 :
436 : // We must ensure that all newly allocated JSObjects get their metadata
437 : // set. However, metadata builders may require the new object be in a sane
438 : // state (eg, have its reserved slots initialized so they can get the
439 : // sizeOfExcludingThis of the object). Therefore, for objects of certain
440 : // JSClasses (those marked with JSCLASS_DELAY_METADATA_BUILDER), it is not safe
441 : // for the allocation paths to call the object metadata builder
442 : // immediately. Instead, the JSClass-specific "constructor" C++ function up the
443 : // stack makes a promise that it will ensure that the new object has its
444 : // metadata set after the object is initialized.
445 : //
446 : // To help those constructor functions keep their promise of setting metadata,
447 : // each compartment is in one of three states at any given time:
448 : //
449 : // * ImmediateMetadata: Allocators should set new object metadata immediately,
450 : // as usual.
451 : //
452 : // * DelayMetadata: Allocators should *not* set new object metadata, it will be
453 : // handled after reserved slots are initialized by custom code
454 : // for the object's JSClass. The newly allocated object's
455 : // JSClass *must* have the JSCLASS_DELAY_METADATA_BUILDER flag
456 : // set.
457 : //
458 : // * PendingMetadata: This object has been allocated and is still pending its
459 : // metadata. This should never be the case when we begin an
460 : // allocation, as a constructor function was supposed to have
461 : // set the metadata of the previous object *before*
462 : // allocating another object.
463 : //
464 : // The js::AutoSetNewObjectMetadata RAII class provides an ergonomic way for
465 : // constructor functions to navigate state transitions, and its instances
466 : // collectively maintain a stack of previous states. The stack is required to
467 : // support the lazy resolution and allocation of global builtin constructors and
468 : // prototype objects. The initial (and intuitively most common) state is
469 : // ImmediateMetadata.
470 : //
471 : // Without the presence of internal errors (such as OOM), transitions between
472 : // the states are as follows:
473 : //
474 : // ImmediateMetadata .----- previous state on stack
475 : // | | ^
476 : // | via constructor | |
477 : // | | | via setting the new
478 : // | via constructor | | object's metadata
479 : // | .-----------------------' |
480 : // | | |
481 : // V V |
482 : // DelayMetadata -------------------------> PendingMetadata
483 : // via allocation
484 : //
485 : // In the presence of internal errors, we do not set the new object's metadata
486 : // (if it was even allocated) and reset to the previous state on the stack.
487 :
488 : struct ImmediateMetadata { };
489 : struct DelayMetadata { };
490 : using PendingMetadata = JSObject*;
491 :
492 : using NewObjectMetadataState = mozilla::Variant<ImmediateMetadata,
493 : DelayMetadata,
494 : PendingMetadata>;
495 :
496 : class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter
497 : {
498 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
499 :
500 : JSContext* cx_;
501 : NewObjectMetadataState prevState_;
502 :
503 : AutoSetNewObjectMetadata(const AutoSetNewObjectMetadata& aOther) = delete;
504 : void operator=(const AutoSetNewObjectMetadata& aOther) = delete;
505 :
506 : protected:
507 0 : virtual void trace(JSTracer* trc) override {
508 0 : if (prevState_.is<PendingMetadata>()) {
509 : TraceRoot(trc,
510 0 : &prevState_.as<PendingMetadata>(),
511 0 : "Object pending metadata");
512 : }
513 0 : }
514 :
515 : public:
516 : explicit AutoSetNewObjectMetadata(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
517 : ~AutoSetNewObjectMetadata();
518 : };
519 :
520 : } /* namespace js */
521 :
522 : namespace js {
523 : class DebugEnvironments;
524 : class ObjectWeakMap;
525 : class WatchpointMap;
526 : class WeakMapBase;
527 : } // namespace js
528 :
529 : struct JSCompartment
530 : {
531 : const JS::CompartmentCreationOptions creationOptions_;
532 : JS::CompartmentBehaviors behaviors_;
533 :
534 : private:
535 : JS::Zone* zone_;
536 : JSRuntime* runtime_;
537 :
538 : public:
539 : /*
540 : * The principals associated with this compartment. Note that the
541 : * same several compartments may share the same principals and
542 : * that a compartment may change principals during its lifetime
543 : * (e.g. in case of lazy parsing).
544 : */
545 109275 : inline JSPrincipals* principals() {
546 109275 : return principals_;
547 : }
548 308 : inline void setPrincipals(JSPrincipals* principals) {
549 308 : if (principals_ == principals)
550 0 : return;
551 :
552 : // If we change principals, we need to unlink immediately this
553 : // compartment from its PerformanceGroup. For one thing, the
554 : // performance data we collect should not be improperly associated
555 : // with a group to which we do not belong anymore. For another thing,
556 : // we use `principals()` as part of the key to map compartments
557 : // to a `PerformanceGroup`, so if we do not unlink now, this will
558 : // be too late once we have updated `principals_`.
559 308 : performanceMonitoring.unlink();
560 308 : principals_ = principals;
561 : }
562 17614 : inline bool isSystem() const {
563 17614 : return isSystem_;
564 : }
565 315 : inline void setIsSystem(bool isSystem) {
566 315 : if (isSystem_ == isSystem)
567 28 : return;
568 :
569 : // If we change `isSystem*(`, we need to unlink immediately this
570 : // compartment from its PerformanceGroup. For one thing, the
571 : // performance data we collect should not be improperly associated
572 : // to a group to which we do not belong anymore. For another thing,
573 : // we use `isSystem()` as part of the key to map compartments
574 : // to a `PerformanceGroup`, so if we do not unlink now, this will
575 : // be too late once we have updated `isSystem_`.
576 287 : performanceMonitoring.unlink();
577 287 : isSystem_ = isSystem;
578 : }
579 :
580 1036278 : bool isAtomsCompartment() const {
581 1036278 : return isAtomsCompartment_;
582 : }
583 4 : void setIsAtomsCompartment() {
584 4 : isAtomsCompartment_ = true;
585 4 : }
586 :
587 : // Used to approximate non-content code when reporting telemetry.
588 21274 : inline bool isProbablySystemOrAddonCode() const {
589 21274 : if (creationOptions_.addonIdOrNull())
590 285 : return true;
591 :
592 20989 : return isSystem_;
593 : }
594 : private:
595 : JSPrincipals* principals_;
596 : bool isSystem_;
597 : bool isAtomsCompartment_;
598 :
599 : public:
600 : bool isSelfHosting;
601 : bool marked;
602 : bool warnedAboutDateToLocaleFormat;
603 : bool warnedAboutExprClosure;
604 : bool warnedAboutForEach;
605 : uint32_t warnedAboutStringGenericsMethods;
606 :
607 : #ifdef DEBUG
608 : bool firedOnNewGlobalObject;
609 : #endif
610 :
611 764 : void mark() { marked = true; }
612 :
613 : private:
614 : friend struct JSRuntime;
615 : friend struct JSContext;
616 : js::ReadBarrieredGlobalObject global_;
617 :
618 : unsigned enterCompartmentDepth;
619 :
620 : public:
621 : js::PerformanceGroupHolder performanceMonitoring;
622 :
623 146046 : void enter() {
624 146046 : enterCompartmentDepth++;
625 146046 : }
626 146038 : void leave() {
627 146038 : enterCompartmentDepth--;
628 146038 : }
629 574201 : bool hasBeenEntered() { return !!enterCompartmentDepth; }
630 :
631 4750983 : JS::Zone* zone() { return zone_; }
632 : const JS::Zone* zone() const { return zone_; }
633 :
634 2809 : const JS::CompartmentCreationOptions& creationOptions() const { return creationOptions_; }
635 31697 : JS::CompartmentBehaviors& behaviors() { return behaviors_; }
636 : const JS::CompartmentBehaviors& behaviors() const { return behaviors_; }
637 :
638 323 : JSRuntime* runtimeFromActiveCooperatingThread() const {
639 323 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
640 323 : return runtime_;
641 : }
642 :
643 : // Note: Unrestricted access to the zone's runtime from an arbitrary
644 : // thread can easily lead to races. Use this method very carefully.
645 694217 : JSRuntime* runtimeFromAnyThread() const {
646 694217 : return runtime_;
647 : }
648 :
649 : /*
650 : * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
651 : * (b) the compartment's global has been collected. The latter can happen
652 : * if e.g. a string in a compartment is rooted but no object is, and thus
653 : * the global isn't rooted, and thus the global can be finalized while the
654 : * compartment lives on.
655 : *
656 : * In contrast, JSObject::global() is infallible because marking a JSObject
657 : * always marks its global as well.
658 : * TODO: add infallible JSScript::global()
659 : */
660 : inline js::GlobalObject* maybeGlobal() const;
661 :
662 : /* An unbarriered getter for use while tracing. */
663 : inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
664 :
665 : inline void initGlobal(js::GlobalObject& global);
666 :
667 : public:
668 : void* data;
669 :
670 : private:
671 : const js::AllocationMetadataBuilder *allocationMetadataBuilder;
672 :
673 : js::SavedStacks savedStacks_;
674 :
675 : js::WrapperMap crossCompartmentWrappers;
676 :
677 : using CCKeyVector = mozilla::Vector<js::CrossCompartmentKey, 0, js::SystemAllocPolicy>;
678 : CCKeyVector nurseryCCKeys;
679 :
680 : // The global environment record's [[VarNames]] list that contains all
681 : // names declared using FunctionDeclaration, GeneratorDeclaration, and
682 : // VariableDeclaration declarations in global code in this compartment.
683 : // Names are only removed from this list by a |delete IdentifierReference|
684 : // that successfully removes that global property.
685 : JS::GCHashSet<JSAtom*,
686 : js::DefaultHasher<JSAtom*>,
687 : js::SystemAllocPolicy> varNames_;
688 :
689 : public:
690 : /* Last time at which an animation was played for a global in this compartment. */
691 : int64_t lastAnimationTime;
692 :
693 : js::RegExpCompartment regExps;
694 :
695 : /*
696 : * For generational GC, record whether a write barrier has added this
697 : * compartment's global to the store buffer since the last minor GC.
698 : *
699 : * This is used to avoid calling into the VM every time a nursery object is
700 : * written to a property of the global.
701 : */
702 : uint32_t globalWriteBarriered;
703 :
704 : // Non-zero if the storage underlying any typed object in this compartment
705 : // might be detached.
706 : int32_t detachedTypedObjects;
707 :
708 : private:
709 : friend class js::AutoSetNewObjectMetadata;
710 : js::NewObjectMetadataState objectMetadataState;
711 :
712 : public:
713 : // Recompute the probability with which this compartment should record
714 : // profiling data (stack traces, allocations log, etc.) about each
715 : // allocation. We consult the probabilities requested by the Debugger
716 : // instances observing us, if any.
717 0 : void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); }
718 :
719 325519 : bool hasObjectPendingMetadata() const { return objectMetadataState.is<js::PendingMetadata>(); }
720 :
721 16470 : void setObjectPendingMetadata(JSContext* cx, JSObject* obj) {
722 16470 : if (!cx->helperThread()) {
723 16167 : MOZ_ASSERT(objectMetadataState.is<js::DelayMetadata>());
724 16167 : objectMetadataState = js::NewObjectMetadataState(js::PendingMetadata(obj));
725 : }
726 16470 : }
727 :
728 : public:
729 : void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
730 : size_t* tiAllocationSiteTables,
731 : size_t* tiArrayTypeTables,
732 : size_t* tiObjectTypeTables,
733 : size_t* compartmentObject,
734 : size_t* compartmentTables,
735 : size_t* innerViews,
736 : size_t* lazyArrayBuffers,
737 : size_t* objectMetadataTables,
738 : size_t* crossCompartmentWrappers,
739 : size_t* savedStacksSet,
740 : size_t* varNamesSet,
741 : size_t* nonSyntacticLexicalScopes,
742 : size_t* templateLiteralMap,
743 : size_t* jitCompartment,
744 : size_t* privateData);
745 :
746 : // Object group tables and other state in the compartment.
747 : js::ObjectGroupCompartment objectGroups;
748 :
749 : #ifdef JSGC_HASH_TABLE_CHECKS
750 : void checkWrapperMapAfterMovingGC();
751 : void checkScriptMapsAfterMovingGC();
752 : #endif
753 :
754 : /*
755 : * Lazily initialized script source object to use for scripts cloned
756 : * from the self-hosting global.
757 : */
758 : js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
759 :
760 : // Keep track of the metadata objects which can be associated with each JS
761 : // object. Both keys and values are in this compartment.
762 : js::ObjectWeakMap* objectMetadataTable;
763 :
764 : // Map from array buffers to views sharing that storage.
765 : JS::WeakCache<js::InnerViewTable> innerViews;
766 :
767 : // Inline transparent typed objects do not initially have an array buffer,
768 : // but can have that buffer created lazily if it is accessed later. This
769 : // table manages references from such typed objects to their buffers.
770 : js::ObjectWeakMap* lazyArrayBuffers;
771 :
772 : // All unboxed layouts in the compartment.
773 : mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
774 :
775 : // WebAssembly state for the compartment.
776 : js::wasm::Compartment wasm;
777 :
778 : private:
779 : // All non-syntactic lexical environments in the compartment. These are kept in
780 : // a map because when loading scripts into a non-syntactic environment, we need
781 : // to use the same lexical environment to persist lexical bindings.
782 : js::ObjectWeakMap* nonSyntacticLexicalEnvironments_;
783 :
784 : // The realm's [[TemplateMap]], used for mapping template literals to
785 : // unique template objects used in evaluation of tagged template literals.
786 : //
787 : // See ES 12.2.9.3.
788 : js::TemplateRegistry templateLiteralMap_;
789 :
790 : public:
791 : /* During GC, stores the index of this compartment in rt->compartments. */
792 : unsigned gcIndex;
793 :
794 : /*
795 : * During GC, stores the head of a list of incoming pointers from gray cells.
796 : *
797 : * The objects in the list are either cross-compartment wrappers, or
798 : * debugger wrapper objects. The list link is either in the second extra
799 : * slot for the former, or a special slot for the latter.
800 : */
801 : JSObject* gcIncomingGrayPointers;
802 :
803 : private:
804 : enum {
805 : IsDebuggee = 1 << 0,
806 : DebuggerObservesAllExecution = 1 << 1,
807 : DebuggerObservesAsmJS = 1 << 2,
808 : DebuggerObservesCoverage = 1 << 3,
809 : DebuggerObservesBinarySource = 1 << 4,
810 : DebuggerNeedsDelazification = 1 << 5
811 : };
812 :
813 : unsigned debugModeBits;
814 : friend class AutoRestoreCompartmentDebugMode;
815 :
816 : static const unsigned DebuggerObservesMask = IsDebuggee |
817 : DebuggerObservesAllExecution |
818 : DebuggerObservesCoverage |
819 : DebuggerObservesAsmJS |
820 : DebuggerObservesBinarySource;
821 :
822 : void updateDebuggerObservesFlag(unsigned flag);
823 :
824 : bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
825 : bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
826 :
827 : private:
828 : // This pointer is controlled by the embedder. If it is non-null, and if
829 : // cx->enableAccessValidation is true, then we assert that *validAccessPtr
830 : // is true before running any code in this compartment.
831 : bool* validAccessPtr;
832 :
833 : public:
834 794 : bool isAccessValid() const { return validAccessPtr ? *validAccessPtr : true; }
835 4 : void setValidAccessPtr(bool* accessp) { validAccessPtr = accessp; }
836 :
837 : public:
838 : JSCompartment(JS::Zone* zone, const JS::CompartmentOptions& options);
839 : ~JSCompartment();
840 :
841 : MOZ_MUST_USE bool init(JSContext* maybecx);
842 :
843 : MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);
844 :
845 : MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp);
846 : MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandleObject obj);
847 : MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle<js::PropertyDescriptor> desc);
848 : MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle<JS::GCVector<JS::Value>> vec);
849 : MOZ_MUST_USE bool rewrap(JSContext* cx, JS::MutableHandleObject obj, JS::HandleObject existing);
850 :
851 : MOZ_MUST_USE bool putWrapper(JSContext* cx, const js::CrossCompartmentKey& wrapped,
852 : const js::Value& wrapper);
853 :
854 353 : js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) const {
855 353 : return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped));
856 : }
857 :
858 553 : js::WrapperMap::Ptr lookupWrapper(JSObject* obj) const {
859 553 : return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(obj));
860 : }
861 :
862 10 : void removeWrapper(js::WrapperMap::Ptr p) {
863 10 : crossCompartmentWrappers.remove(p);
864 10 : }
865 :
866 16 : bool hasNurseryAllocatedWrapperEntries(const js::CompartmentFilter& f) {
867 16 : return crossCompartmentWrappers.hasNurseryAllocatedWrapperEntries(f);
868 : }
869 :
870 0 : struct WrapperEnum : public js::WrapperMap::Enum {
871 0 : explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
872 : };
873 :
874 624 : struct NonStringWrapperEnum : public js::WrapperMap::Enum {
875 211 : explicit NonStringWrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers, WithoutStrings) {}
876 16 : explicit NonStringWrapperEnum(JSCompartment* c, const js::CompartmentFilter& f) : js::WrapperMap::Enum(c->crossCompartmentWrappers, f, WithoutStrings) {}
877 397 : explicit NonStringWrapperEnum(JSCompartment* c, JSCompartment* target) : js::WrapperMap::Enum(c->crossCompartmentWrappers, target) { MOZ_ASSERT(target); }
878 : };
879 :
880 0 : struct StringWrapperEnum : public js::WrapperMap::Enum {
881 0 : explicit StringWrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers, nullptr) {}
882 : };
883 :
884 : js::LexicalEnvironmentObject*
885 : getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, js::HandleObject enclosing);
886 : js::LexicalEnvironmentObject* getNonSyntacticLexicalEnvironment(JSObject* enclosing) const;
887 :
888 : /*
889 : * This method traces data that is live iff we know that this compartment's
890 : * global is still live.
891 : */
892 : void traceGlobal(JSTracer* trc);
893 : /*
894 : * This method traces JSCompartment-owned GC roots that are considered live
895 : * regardless of whether the compartment's global is still live.
896 : */
897 : void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
898 : /*
899 : * This method clears out tables of roots in preparation for the final GC.
900 : */
901 : void finishRoots();
902 : /*
903 : * These methods mark pointers that cross compartment boundaries. They are
904 : * called in per-zone GCs to prevent the wrappers' outgoing edges from
905 : * dangling (full GCs naturally follow pointers across compartments) and
906 : * when compacting to update cross-compartment pointers.
907 : */
908 : void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
909 : static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
910 :
911 : /* Whether to preserve JIT code on non-shrinking GCs. */
912 223 : bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
913 :
914 : void sweepAfterMinorGC(JSTracer* trc);
915 :
916 : void sweepCrossCompartmentWrappers();
917 : void sweepSavedStacks();
918 : void sweepTemplateLiteralMap();
919 : void sweepGlobalObject();
920 : void sweepSelfHostingScriptSource();
921 : void sweepJitCompartment(js::FreeOp* fop);
922 : void sweepRegExps();
923 : void sweepDebugEnvironments();
924 : void sweepNativeIterators();
925 : void sweepTemplateObjects();
926 : void sweepVarNames();
927 : void sweepWatchpoints();
928 :
929 : void purge();
930 : void clearTables();
931 :
932 : static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
933 : void fixupAfterMovingGC();
934 : void fixupGlobal();
935 : void fixupScriptMapsAfterMovingGC();
936 :
937 160904 : bool hasAllocationMetadataBuilder() const { return allocationMetadataBuilder; }
938 0 : const js::AllocationMetadataBuilder* getAllocationMetadataBuilder() const {
939 0 : return allocationMetadataBuilder;
940 : }
941 : void setAllocationMetadataBuilder(const js::AllocationMetadataBuilder* builder);
942 0 : void forgetAllocationMetadataBuilder() {
943 0 : allocationMetadataBuilder = nullptr;
944 0 : }
945 : void setNewObjectMetadata(JSContext* cx, JS::HandleObject obj);
946 : void clearObjectMetadata();
947 78 : const void* addressOfMetadataBuilder() const {
948 78 : return &allocationMetadataBuilder;
949 : }
950 :
951 32864 : js::SavedStacks& savedStacks() { return savedStacks_; }
952 :
953 : // Add a name to [[VarNames]]. Reports OOM on failure.
954 : MOZ_MUST_USE bool addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name);
955 :
956 0 : void removeFromVarNames(JS::Handle<JSAtom*> name) {
957 0 : varNames_.remove(name);
958 0 : }
959 :
960 : // Whether the given name is in [[VarNames]].
961 4398 : bool isInVarNames(JS::Handle<JSAtom*> name) {
962 4398 : return varNames_.has(name);
963 : }
964 :
965 : // Get a unique template object given a JS array of raw template strings
966 : // and a template object. If a template object is found in template
967 : // registry, that object is returned. Otherwise, the passed-in templateObj
968 : // is added to the registry.
969 : bool getTemplateLiteralObject(JSContext* cx, js::HandleObject rawStrings,
970 : js::MutableHandleObject templateObj);
971 :
972 : // Per above, but an entry must already exist in the template registry.
973 : JSObject* getExistingTemplateLiteralObject(JSObject* rawStrings);
974 :
975 : void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
976 :
977 : js::DtoaCache dtoaCache;
978 : js::NewProxyCache newProxyCache;
979 :
980 : // Random number generator for Math.random().
981 : mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
982 :
983 : // Initialize randomNumberGenerator if needed.
984 : void ensureRandomNumberGenerator();
985 :
986 : private:
987 : mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
988 :
989 : public:
990 : js::HashNumber randomHashCode();
991 :
992 : mozilla::HashCodeScrambler randomHashCodeScrambler();
993 :
994 0 : static size_t offsetOfRegExps() {
995 0 : return offsetof(JSCompartment, regExps);
996 : }
997 :
998 : private:
999 : JSCompartment* thisForCtor() { return this; }
1000 :
1001 : public:
1002 : //
1003 : // The Debugger observes execution on a frame-by-frame basis. The
1004 : // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
1005 : // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
1006 : // enumerated below.
1007 : //
1008 : // 1. When a compartment's isDebuggee() == true, relazification and lazy
1009 : // parsing are disabled.
1010 : //
1011 : // Whether AOT wasm is disabled is togglable by the Debugger API. By
1012 : // default it is disabled. See debuggerObservesAsmJS below.
1013 : //
1014 : // 2. When a compartment's debuggerObservesAllExecution() == true, all of
1015 : // the compartment's scripts are considered debuggee scripts.
1016 : //
1017 : // 3. A script is considered a debuggee script either when, per above, its
1018 : // compartment is observing all execution, or if it has breakpoints set.
1019 : //
1020 : // 4. A debuggee script always pushes a debuggee frame.
1021 : //
1022 : // 5. A debuggee frame calls all slow path Debugger hooks in the
1023 : // Interpreter and Baseline. A debuggee frame implies that its script's
1024 : // BaselineScript, if extant, has been compiled with debug hook calls.
1025 : //
1026 : // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
1027 : // that the compiled BaselineScript is compiled with debug hook calls
1028 : // when attempting to enter Baseline.
1029 : //
1030 : // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
1031 : // attempt to enter Ion.
1032 : //
1033 : // Note that a debuggee frame may exist without its script being a
1034 : // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
1035 : // frame in which it is evaluating as a debuggee frame.
1036 : //
1037 :
1038 : // True if this compartment's global is a debuggee of some Debugger
1039 : // object.
1040 37371 : bool isDebuggee() const { return !!(debugModeBits & IsDebuggee); }
1041 0 : void setIsDebuggee() { debugModeBits |= IsDebuggee; }
1042 : void unsetIsDebuggee();
1043 :
1044 : // True if this compartment's global is a debuggee of some Debugger
1045 : // object with a live hook that observes all execution; e.g.,
1046 : // onEnterFrame.
1047 45234 : bool debuggerObservesAllExecution() const {
1048 : static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution;
1049 45234 : return (debugModeBits & Mask) == Mask;
1050 : }
1051 0 : void updateDebuggerObservesAllExecution() {
1052 0 : updateDebuggerObservesFlag(DebuggerObservesAllExecution);
1053 0 : }
1054 :
1055 : // True if this compartment's global is a debuggee of some Debugger object
1056 : // whose allowUnobservedAsmJS flag is false.
1057 : //
1058 : // Note that since AOT wasm functions cannot bail out, this flag really
1059 : // means "observe wasm from this point forward". We cannot make
1060 : // already-compiled wasm code observable to Debugger.
1061 21274 : bool debuggerObservesAsmJS() const {
1062 : static const unsigned Mask = IsDebuggee | DebuggerObservesAsmJS;
1063 21274 : return (debugModeBits & Mask) == Mask;
1064 : }
1065 0 : void updateDebuggerObservesAsmJS() {
1066 0 : updateDebuggerObservesFlag(DebuggerObservesAsmJS);
1067 0 : }
1068 :
1069 0 : bool debuggerObservesBinarySource() const {
1070 : static const unsigned Mask = IsDebuggee | DebuggerObservesBinarySource;
1071 0 : return (debugModeBits & Mask) == Mask;
1072 : }
1073 :
1074 0 : void updateDebuggerObservesBinarySource() {
1075 0 : updateDebuggerObservesFlag(DebuggerObservesBinarySource);
1076 0 : }
1077 :
1078 : // True if this compartment's global is a debuggee of some Debugger object
1079 : // whose collectCoverageInfo flag is true.
1080 11796 : bool debuggerObservesCoverage() const {
1081 : static const unsigned Mask = DebuggerObservesCoverage;
1082 11796 : return (debugModeBits & Mask) == Mask;
1083 : }
1084 : void updateDebuggerObservesCoverage();
1085 :
1086 : // The code coverage can be enabled either for each compartment, with the
1087 : // Debugger API, or for the entire runtime.
1088 : bool collectCoverage() const;
1089 : bool collectCoverageForDebug() const;
1090 : bool collectCoverageForPGO() const;
1091 : void clearScriptCounts();
1092 : void clearScriptNames();
1093 :
1094 15 : bool needsDelazificationForDebugger() const {
1095 15 : return debugModeBits & DebuggerNeedsDelazification;
1096 : }
1097 :
1098 : /*
1099 : * Schedule the compartment to be delazified. Called from
1100 : * LazyScript::Create.
1101 : */
1102 4170 : void scheduleDelazificationForDebugger() { debugModeBits |= DebuggerNeedsDelazification; }
1103 :
1104 : /*
1105 : * If we scheduled delazification for turning on debug mode, delazify all
1106 : * scripts.
1107 : */
1108 : bool ensureDelazifyScriptsForDebugger(JSContext* cx);
1109 :
1110 : void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
1111 :
1112 : private:
1113 : void sweepBreakpoints(js::FreeOp* fop);
1114 :
1115 : public:
1116 : js::WatchpointMap* watchpointMap;
1117 :
1118 : js::ScriptCountsMap* scriptCountsMap;
1119 : js::ScriptNameMap* scriptNameMap;
1120 :
1121 : js::DebugScriptMap* debugScriptMap;
1122 :
1123 : /* Bookkeeping information for debug scope objects. */
1124 : js::DebugEnvironments* debugEnvs;
1125 :
1126 : /*
1127 : * List of potentially active iterators that may need deleted property
1128 : * suppression.
1129 : */
1130 : js::NativeIterator* enumerators;
1131 :
1132 : /* Native iterator most recently started. */
1133 : js::PropertyIteratorObject* lastCachedNativeIterator;
1134 :
1135 : private:
1136 : /* Used by memory reporters and invalid otherwise. */
1137 : JS::CompartmentStats* compartmentStats_;
1138 :
1139 : public:
1140 : // This should only be called when it is non-null, i.e. during memory
1141 : // reporting.
1142 0 : JS::CompartmentStats& compartmentStats() {
1143 : // We use MOZ_RELEASE_ASSERT here because in bug 1132502 there was some
1144 : // (inconclusive) evidence that compartmentStats_ can be nullptr
1145 : // unexpectedly.
1146 0 : MOZ_RELEASE_ASSERT(compartmentStats_);
1147 0 : return *compartmentStats_;
1148 : }
1149 0 : void nullCompartmentStats() {
1150 0 : MOZ_ASSERT(compartmentStats_);
1151 0 : compartmentStats_ = nullptr;
1152 0 : }
1153 0 : void setCompartmentStats(JS::CompartmentStats* newStats) {
1154 0 : MOZ_ASSERT(!compartmentStats_ && newStats);
1155 0 : compartmentStats_ = newStats;
1156 0 : }
1157 :
1158 : MOZ_ALWAYS_INLINE bool objectMaybeInIteration(JSObject* obj);
1159 :
1160 : // These flags help us to discover if a compartment that shouldn't be alive
1161 : // manages to outlive a GC.
1162 : bool scheduledForDestruction;
1163 : bool maybeAlive;
1164 :
1165 : private:
1166 : js::jit::JitCompartment* jitCompartment_;
1167 :
1168 : js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
1169 : js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
1170 :
1171 : public:
1172 : bool ensureJitCompartmentExists(JSContext* cx);
1173 44896 : js::jit::JitCompartment* jitCompartment() {
1174 44896 : return jitCompartment_;
1175 : }
1176 :
1177 : js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
1178 :
1179 : js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
1180 :
1181 : private:
1182 : // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
1183 : bool sawDeprecatedLanguageExtension[size_t(js::DeprecatedLanguageExtension::Count)];
1184 :
1185 : void reportTelemetry();
1186 :
1187 : public:
1188 : void addTelemetry(const char* filename, js::DeprecatedLanguageExtension e);
1189 :
1190 : public:
1191 : // Aggregated output used to collect JSScript hit counts when code coverage
1192 : // is enabled.
1193 : js::coverage::LCovCompartment lcovOutput;
1194 : };
1195 :
1196 : namespace js {
1197 :
1198 : // We only set the maybeAlive flag for objects and scripts. It's assumed that,
1199 : // if a compartment is alive, then it will have at least some live object or
1200 : // script it in. Even if we get this wrong, the worst that will happen is that
1201 : // scheduledForDestruction will be set on the compartment, which will cause
1202 : // some extra GC activity to try to free the compartment.
1203 25344 : template<typename T> inline void SetMaybeAliveFlag(T* thing) {}
1204 12882 : template<> inline void SetMaybeAliveFlag(JSObject* thing) {thing->compartment()->maybeAlive = true;}
1205 904 : template<> inline void SetMaybeAliveFlag(JSScript* thing) {thing->compartment()->maybeAlive = true;}
1206 :
1207 : } // namespace js
1208 :
1209 : inline js::Handle<js::GlobalObject*>
1210 522013 : JSContext::global() const
1211 : {
1212 : /*
1213 : * It's safe to use |unsafeGet()| here because any compartment that is
1214 : * on-stack will be marked automatically, so there's no need for a read
1215 : * barrier on it. Once the compartment is popped, the handle is no longer
1216 : * safe to use.
1217 : */
1218 522013 : MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first");
1219 522013 : return js::Handle<js::GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
1220 : }
1221 :
1222 : namespace js {
1223 :
1224 : class MOZ_RAII AssertCompartmentUnchanged
1225 : {
1226 : public:
1227 7673 : explicit AssertCompartmentUnchanged(JSContext* cx
1228 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
1229 7673 : : cx(cx), oldCompartment(cx->compartment())
1230 : {
1231 7673 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1232 7673 : }
1233 :
1234 15346 : ~AssertCompartmentUnchanged() {
1235 7673 : MOZ_ASSERT(cx->compartment() == oldCompartment);
1236 7673 : }
1237 :
1238 : protected:
1239 : JSContext * const cx;
1240 : JSCompartment * const oldCompartment;
1241 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1242 : };
1243 :
1244 : class AutoCompartment
1245 : {
1246 : JSContext * const cx_;
1247 : JSCompartment * const origin_;
1248 : const AutoLockForExclusiveAccess* maybeLock_;
1249 :
1250 : public:
1251 : template <typename T>
1252 : inline AutoCompartment(JSContext* cx, const T& target);
1253 : inline ~AutoCompartment();
1254 :
1255 0 : JSContext* context() const { return cx_; }
1256 0 : JSCompartment* origin() const { return origin_; }
1257 :
1258 : protected:
1259 : inline AutoCompartment(JSContext* cx, JSCompartment* target);
1260 :
1261 : // Used only for entering the atoms compartment.
1262 : inline AutoCompartment(JSContext* cx, JSCompartment* target,
1263 : AutoLockForExclusiveAccess& lock);
1264 :
1265 : private:
1266 : AutoCompartment(const AutoCompartment&) = delete;
1267 : AutoCompartment & operator=(const AutoCompartment&) = delete;
1268 : };
1269 :
1270 62625 : class AutoAtomsCompartment : protected AutoCompartment
1271 : {
1272 : public:
1273 : inline AutoAtomsCompartment(JSContext* cx, AutoLockForExclusiveAccess& lock);
1274 : };
1275 :
1276 : // Enter a compartment directly. Only use this where there's no target GC thing
1277 : // to pass to AutoCompartment or where you need to avoid the assertions in
1278 : // JS::Compartment::enterCompartmentOf().
1279 13101 : class AutoCompartmentUnchecked : protected AutoCompartment
1280 : {
1281 : public:
1282 : inline AutoCompartmentUnchecked(JSContext* cx, JSCompartment* target);
1283 : };
1284 :
1285 : /*
1286 : * Use this to change the behavior of an AutoCompartment slightly on error. If
1287 : * the exception happens to be an Error object, copy it to the origin compartment
1288 : * instead of wrapping it.
1289 : */
1290 : class ErrorCopier
1291 : {
1292 : mozilla::Maybe<AutoCompartment>& ac;
1293 :
1294 : public:
1295 0 : explicit ErrorCopier(mozilla::Maybe<AutoCompartment>& ac)
1296 0 : : ac(ac) {}
1297 : ~ErrorCopier();
1298 : };
1299 :
1300 : /*
1301 : * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that
1302 : * are obtained from the cross-compartment map. However, these classes should
1303 : * not be used if the wrapper will escape. For example, it should not be stored
1304 : * in the heap.
1305 : *
1306 : * The AutoWrapper rooters are different from other autorooters because their
1307 : * wrappers are marked on every GC slice rather than just the first one. If
1308 : * there's some wrapper that we want to use temporarily without causing it to be
1309 : * marked, we can use these AutoWrapper classes. If we get unlucky and a GC
1310 : * slice runs during the code using the wrapper, the GC will mark the wrapper so
1311 : * that it doesn't get swept out from under us. Otherwise, the wrapper needn't
1312 : * be marked. This is useful in functions like JS_TransplantObject that
1313 : * manipulate wrappers in compartments that may no longer be alive.
1314 : */
1315 :
1316 : /*
1317 : * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It
1318 : * should not be used in any other situations.
1319 : */
1320 : struct WrapperValue
1321 : {
1322 : /*
1323 : * We use unsafeGet() in the constructors to avoid invoking a read barrier
1324 : * on the wrapper, which may be dead (see the comment about bug 803376 in
1325 : * jsgc.cpp regarding this). If there is an incremental GC while the wrapper
1326 : * is in use, the AutoWrapper rooter will ensure the wrapper gets marked.
1327 : */
1328 10 : explicit WrapperValue(const WrapperMap::Ptr& ptr)
1329 10 : : value(*ptr->value().unsafeGet())
1330 10 : {}
1331 :
1332 37 : explicit WrapperValue(const WrapperMap::Enum& e)
1333 37 : : value(*e.front().value().unsafeGet())
1334 37 : {}
1335 :
1336 0 : Value& get() { return value; }
1337 34 : Value get() const { return value; }
1338 : operator const Value&() const { return value; }
1339 10 : JSObject& toObject() const { return value.toObject(); }
1340 :
1341 : private:
1342 : Value value;
1343 : };
1344 :
1345 6 : class MOZ_RAII AutoWrapperVector : public JS::AutoVectorRooterBase<WrapperValue>
1346 : {
1347 : public:
1348 6 : explicit AutoWrapperVector(JSContext* cx
1349 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
1350 6 : : AutoVectorRooterBase<WrapperValue>(cx, WRAPVECTOR)
1351 : {
1352 6 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1353 6 : }
1354 :
1355 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1356 : };
1357 :
1358 37 : class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
1359 : public:
1360 37 : AutoWrapperRooter(JSContext* cx, const WrapperValue& v
1361 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
1362 37 : : JS::AutoGCRooter(cx, WRAPPER), value(v)
1363 : {
1364 37 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1365 37 : }
1366 :
1367 34 : operator JSObject*() const {
1368 34 : return value.get().toObjectOrNull();
1369 : }
1370 :
1371 : friend void JS::AutoGCRooter::trace(JSTracer* trc);
1372 :
1373 : private:
1374 : WrapperValue value;
1375 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1376 : };
1377 :
1378 : class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
1379 : JS::Zone* zone;
1380 : bool saved;
1381 :
1382 : public:
1383 122973 : explicit AutoSuppressAllocationMetadataBuilder(JSContext* cx)
1384 122973 : : AutoSuppressAllocationMetadataBuilder(cx->compartment()->zone())
1385 122973 : { }
1386 :
1387 122974 : explicit AutoSuppressAllocationMetadataBuilder(JS::Zone* zone)
1388 122974 : : zone(zone),
1389 122974 : saved(zone->suppressAllocationMetadataBuilder)
1390 : {
1391 122972 : zone->suppressAllocationMetadataBuilder = true;
1392 122973 : }
1393 :
1394 245945 : ~AutoSuppressAllocationMetadataBuilder() {
1395 122972 : zone->suppressAllocationMetadataBuilder = saved;
1396 122973 : }
1397 : };
1398 :
1399 : } /* namespace js */
1400 :
1401 : namespace JS {
1402 : template <>
1403 : struct GCPolicy<js::CrossCompartmentKey> : public StructGCPolicy<js::CrossCompartmentKey> {
1404 9609 : static bool isTenured(const js::CrossCompartmentKey& key) { return key.isTenured(); }
1405 : };
1406 : } // namespace JS
1407 :
1408 : #endif /* jscompartment_h */
|