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 #include "jscompartmentinlines.h"
8 
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MemoryReporting.h"
11 
12 #include "jscntxt.h"
13 #include "jsfriendapi.h"
14 #include "jsgc.h"
15 #include "jsiter.h"
16 #include "jswatchpoint.h"
17 #include "jswrapper.h"
18 
19 #include "gc/Marking.h"
20 #include "jit/JitCompartment.h"
21 #include "jit/JitOptions.h"
22 #include "js/Date.h"
23 #include "js/Proxy.h"
24 #include "js/RootingAPI.h"
25 #include "proxy/DeadObjectProxy.h"
26 #include "vm/Debugger.h"
27 #include "vm/StopIterationObject.h"
28 #include "vm/WrapperObject.h"
29 
30 #include "jsatominlines.h"
31 #include "jsfuninlines.h"
32 #include "jsgcinlines.h"
33 #include "jsobjinlines.h"
34 #include "jsscriptinlines.h"
35 
36 using namespace js;
37 using namespace js::gc;
38 using namespace js::jit;
39 
40 using mozilla::DebugOnly;
41 using mozilla::PodArrayZero;
42 
JSCompartment(Zone * zone,const JS::CompartmentOptions & options=JS::CompartmentOptions ())43 JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
44   : options_(options),
45     zone_(zone),
46     runtime_(zone->runtimeFromMainThread()),
47     principals_(nullptr),
48     isSystem_(false),
49     isAtomsCompartment_(false),
50     isSelfHosting(false),
51     marked(true),
52     warnedAboutFlagsArgument(false),
53     warnedAboutExprClosure(false),
54     addonId(options.addonIdOrNull()),
55 #ifdef DEBUG
56     firedOnNewGlobalObject(false),
57 #endif
58     global_(nullptr),
59     enterCompartmentDepth(0),
60     performanceMonitoring(runtime_),
61     data(nullptr),
62     objectMetadataCallback(nullptr),
63     lastAnimationTime(0),
64     regExps(runtime_),
65     globalWriteBarriered(false),
66     neuteredTypedObjects(0),
67     objectMetadataState(ImmediateMetadata()),
68     propertyTree(thisForCtor()),
69     selfHostingScriptSource(nullptr),
70     objectMetadataTable(nullptr),
71     lazyArrayBuffers(nullptr),
72     nonSyntacticLexicalScopes_(nullptr),
73     gcIncomingGrayPointers(nullptr),
74     gcPreserveJitCode(options.preserveJitCode()),
75     debugModeBits(0),
76     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
77     watchpointMap(nullptr),
78     scriptCountsMap(nullptr),
79     debugScriptMap(nullptr),
80     debugScopes(nullptr),
81     enumerators(nullptr),
82     compartmentStats(nullptr),
83     scheduledForDestruction(false),
84     maybeAlive(true),
85     jitCompartment_(nullptr),
86     mappedArgumentsTemplate_(nullptr),
87     unmappedArgumentsTemplate_(nullptr),
88     lcovOutput()
89 {
90     PodArrayZero(sawDeprecatedLanguageExtension);
91     runtime_->numCompartments++;
92     MOZ_ASSERT_IF(options.mergeable(), options.invisibleToDebugger());
93 }
94 
~JSCompartment()95 JSCompartment::~JSCompartment()
96 {
97     reportTelemetry();
98 
99     // Write the code coverage information in a file.
100     JSRuntime* rt = runtimeFromMainThread();
101     if (rt->lcovOutput.isEnabled())
102         rt->lcovOutput.writeLCovResult(lcovOutput);
103 
104     js_delete(jitCompartment_);
105     js_delete(watchpointMap);
106     js_delete(scriptCountsMap);
107     js_delete(debugScriptMap);
108     js_delete(debugScopes);
109     js_delete(objectMetadataTable);
110     js_delete(lazyArrayBuffers);
111     js_delete(nonSyntacticLexicalScopes_),
112     js_free(enumerators);
113 
114     runtime_->numCompartments--;
115 }
116 
117 bool
init(JSContext * maybecx)118 JSCompartment::init(JSContext* maybecx)
119 {
120     /*
121      * maybecx is null when called to create the atoms compartment from
122      * JSRuntime::init().
123      *
124      * As a hack, we clear our timezone cache every time we create a new
125      * compartment. This ensures that the cache is always relatively fresh, but
126      * shouldn't interfere with benchmarks that create tons of date objects
127      * (unless they also create tons of iframes, which seems unlikely).
128      */
129     JS::ResetTimeZone();
130 
131     if (!crossCompartmentWrappers.init(0)) {
132         if (maybecx)
133             ReportOutOfMemory(maybecx);
134         return false;
135     }
136 
137     if (!regExps.init(maybecx))
138         return false;
139 
140     enumerators = NativeIterator::allocateSentinel(maybecx);
141     if (!enumerators)
142         return false;
143 
144     if (!savedStacks_.init()) {
145         if (maybecx)
146             ReportOutOfMemory(maybecx);
147         return false;
148     }
149 
150     return true;
151 }
152 
153 jit::JitRuntime*
createJitRuntime(JSContext * cx)154 JSRuntime::createJitRuntime(JSContext* cx)
155 {
156     // The shared stubs are created in the atoms compartment, which may be
157     // accessed by other threads with an exclusive context.
158     AutoLockForExclusiveAccess atomsLock(cx);
159 
160     MOZ_ASSERT(!jitRuntime_);
161 
162     if (!CanLikelyAllocateMoreExecutableMemory()) {
163         // Report OOM instead of potentially hitting the MOZ_CRASH below.
164         ReportOutOfMemory(cx);
165         return nullptr;
166     }
167 
168     jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>();
169     if (!jrt)
170         return nullptr;
171 
172     // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
173     // while it is being initialized. Unfortunately, initialization depends on
174     // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
175     JitRuntime::AutoMutateBackedges amb(jrt);
176     jitRuntime_ = jrt;
177 
178     AutoEnterOOMUnsafeRegion noOOM;
179     if (!jitRuntime_->initialize(cx)) {
180         // Handling OOM here is complicated: if we delete jitRuntime_ now, we
181         // will destroy the ExecutableAllocator, even though there may still be
182         // JitCode instances holding references to ExecutablePools.
183         noOOM.crash("OOM in createJitRuntime");
184     }
185 
186     return jitRuntime_;
187 }
188 
189 bool
ensureJitCompartmentExists(JSContext * cx)190 JSCompartment::ensureJitCompartmentExists(JSContext* cx)
191 {
192     using namespace js::jit;
193     if (jitCompartment_)
194         return true;
195 
196     if (!zone()->getJitZone(cx))
197         return false;
198 
199     /* Set the compartment early, so linking works. */
200     jitCompartment_ = cx->new_<JitCompartment>();
201 
202     if (!jitCompartment_)
203         return false;
204 
205     if (!jitCompartment_->initialize(cx)) {
206         js_delete(jitCompartment_);
207         jitCompartment_ = nullptr;
208         return false;
209     }
210 
211     return true;
212 }
213 
214 /*
215  * This class is used to add a post barrier on the crossCompartmentWrappers map,
216  * as the key is calculated based on objects which may be moved by generational
217  * GC.
218  */
219 class WrapperMapRef : public BufferableRef
220 {
221     WrapperMap* map;
222     CrossCompartmentKey key;
223 
224   public:
WrapperMapRef(WrapperMap * map,const CrossCompartmentKey & key)225     WrapperMapRef(WrapperMap* map, const CrossCompartmentKey& key)
226       : map(map), key(key) {}
227 
trace(JSTracer * trc)228     void trace(JSTracer* trc) override {
229         CrossCompartmentKey prior = key;
230         if (key.debugger)
231             TraceManuallyBarrieredEdge(trc, &key.debugger, "CCW debugger");
232         if (key.kind == CrossCompartmentKey::ObjectWrapper ||
233             key.kind == CrossCompartmentKey::DebuggerObject ||
234             key.kind == CrossCompartmentKey::DebuggerEnvironment ||
235             key.kind == CrossCompartmentKey::DebuggerSource)
236         {
237             MOZ_ASSERT(IsInsideNursery(key.wrapped) ||
238                        key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Object);
239             TraceManuallyBarrieredEdge(trc, reinterpret_cast<JSObject**>(&key.wrapped),
240                                        "CCW wrapped object");
241         }
242         if (key.debugger == prior.debugger && key.wrapped == prior.wrapped)
243             return;
244 
245         /* Look for the original entry, which might have been removed. */
246         WrapperMap::Ptr p = map->lookup(prior);
247         if (!p)
248             return;
249 
250         /* Rekey the entry. */
251         map->rekeyAs(prior, key, key);
252     }
253 };
254 
255 #ifdef JSGC_HASH_TABLE_CHECKS
256 void
checkWrapperMapAfterMovingGC()257 JSCompartment::checkWrapperMapAfterMovingGC()
258 {
259     /*
260      * Assert that the postbarriers have worked and that nothing is left in
261      * wrapperMap that points into the nursery, and that the hash table entries
262      * are discoverable.
263      */
264     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
265         CrossCompartmentKey key = e.front().key();
266         CheckGCThingAfterMovingGC(key.debugger);
267         CheckGCThingAfterMovingGC(key.wrapped);
268         CheckGCThingAfterMovingGC(
269                 static_cast<Cell*>(e.front().value().unbarrieredGet().toGCThing()));
270 
271         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(key);
272         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
273     }
274 }
275 #endif
276 
277 bool
putWrapper(JSContext * cx,const CrossCompartmentKey & wrapped,const js::Value & wrapper)278 JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped, const js::Value& wrapper)
279 {
280     MOZ_ASSERT(wrapped.wrapped);
281     MOZ_ASSERT_IF(wrapped.kind == CrossCompartmentKey::StringWrapper, wrapper.isString());
282     MOZ_ASSERT_IF(wrapped.kind != CrossCompartmentKey::StringWrapper, wrapper.isObject());
283 
284     /* There's no point allocating wrappers in the nursery since we will tenure them anyway. */
285     MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing())));
286 
287     if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
288         ReportOutOfMemory(cx);
289         return false;
290     }
291 
292     if (IsInsideNursery(wrapped.wrapped) || IsInsideNursery(wrapped.debugger)) {
293         WrapperMapRef ref(&crossCompartmentWrappers, wrapped);
294         cx->runtime()->gc.storeBuffer.putGeneric(ref);
295     }
296 
297     return true;
298 }
299 
300 static JSString*
CopyStringPure(JSContext * cx,JSString * str)301 CopyStringPure(JSContext* cx, JSString* str)
302 {
303     /*
304      * Directly allocate the copy in the destination compartment, rather than
305      * first flattening it (and possibly allocating in source compartment),
306      * because we don't know whether the flattening will pay off later.
307      */
308 
309     size_t len = str->length();
310     JSString* copy;
311     if (str->isLinear()) {
312         /* Only use AutoStableStringChars if the NoGC allocation fails. */
313         if (str->hasLatin1Chars()) {
314             JS::AutoCheckCannotGC nogc;
315             copy = NewStringCopyN<NoGC>(cx, str->asLinear().latin1Chars(nogc), len);
316         } else {
317             JS::AutoCheckCannotGC nogc;
318             copy = NewStringCopyNDontDeflate<NoGC>(cx, str->asLinear().twoByteChars(nogc), len);
319         }
320         if (copy)
321             return copy;
322 
323         AutoStableStringChars chars(cx);
324         if (!chars.init(cx, str))
325             return nullptr;
326 
327         return chars.isLatin1()
328                ? NewStringCopyN<CanGC>(cx, chars.latin1Range().start().get(), len)
329                : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().start().get(), len);
330     }
331 
332     if (str->hasLatin1Chars()) {
333         ScopedJSFreePtr<Latin1Char> copiedChars;
334         if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
335             return nullptr;
336 
337         return NewString<CanGC>(cx, copiedChars.forget(), len);
338     }
339 
340     ScopedJSFreePtr<char16_t> copiedChars;
341     if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
342         return nullptr;
343 
344     return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
345 }
346 
347 bool
wrap(JSContext * cx,MutableHandleString strp)348 JSCompartment::wrap(JSContext* cx, MutableHandleString strp)
349 {
350     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
351     MOZ_ASSERT(cx->compartment() == this);
352 
353     /* If the string is already in this compartment, we are done. */
354     JSString* str = strp;
355     if (str->zoneFromAnyThread() == zone())
356         return true;
357 
358     /* If the string is an atom, we don't have to copy. */
359     if (str->isAtom()) {
360         MOZ_ASSERT(str->isPermanentAtom() || str->zone()->isAtomsZone());
361         return true;
362     }
363 
364     /* Check the cache. */
365     RootedValue key(cx, StringValue(str));
366     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
367         strp.set(p->value().get().toString());
368         return true;
369     }
370 
371     /* No dice. Make a copy, and cache it. */
372     JSString* copy = CopyStringPure(cx, str);
373     if (!copy)
374         return false;
375     if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy)))
376         return false;
377 
378     strp.set(copy);
379     return true;
380 }
381 
382 bool
wrap(JSContext * cx,MutableHandleObject obj,HandleObject existingArg)383 JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg)
384 {
385     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
386     MOZ_ASSERT(cx->compartment() == this);
387     MOZ_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment());
388     MOZ_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg));
389 
390     if (!obj)
391         return true;
392     AutoDisableProxyCheck adpc(cx->runtime());
393 
394     // Wrappers should really be parented to the wrapped parent of the wrapped
395     // object, but in that case a wrapped global object would have a nullptr
396     // parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
397     // we parent all wrappers to the global object in their home compartment.
398     // This loses us some transparency, and is generally very cheesy.
399     HandleObject global = cx->global();
400     RootedObject objGlobal(cx, &obj->global());
401     MOZ_ASSERT(global);
402     MOZ_ASSERT(objGlobal);
403 
404     const JSWrapObjectCallbacks* cb = cx->runtime()->wrapObjectCallbacks;
405 
406     if (obj->compartment() == this) {
407         obj.set(ToWindowProxyIfWindow(obj));
408         return true;
409     }
410 
411     // If we have a cross-compartment wrapper, make sure that the cx isn't
412     // associated with the self-hosting global. We don't want to create
413     // wrappers for objects in other runtimes, which may be the case for the
414     // self-hosting global.
415     MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) &&
416                !cx->runtime()->isSelfHostingGlobal(objGlobal));
417 
418     // Unwrap the object, but don't unwrap outer windows.
419     RootedObject objectPassedToWrap(cx, obj);
420     obj.set(UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true));
421 
422     if (obj->compartment() == this) {
423         MOZ_ASSERT(!IsWindow(obj));
424         return true;
425     }
426 
427     // Translate StopIteration singleton.
428     if (obj->is<StopIterationObject>()) {
429         // StopIteration isn't a constructor, but it's stored in GlobalObject
430         // as one, out of laziness. Hence the GetBuiltinConstructor call here.
431         RootedObject stopIteration(cx);
432         if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration))
433             return false;
434         obj.set(stopIteration);
435         return true;
436     }
437 
438     // Invoke the prewrap callback. We're a bit worried about infinite
439     // recursion here, so we do a check - see bug 809295.
440     JS_CHECK_SYSTEM_RECURSION(cx, return false);
441     if (cb->preWrap) {
442         obj.set(cb->preWrap(cx, global, obj, objectPassedToWrap));
443         if (!obj)
444             return false;
445     }
446     MOZ_ASSERT(!IsWindow(obj));
447 
448     if (obj->compartment() == this)
449         return true;
450 
451     // If we already have a wrapper for this value, use it.
452     RootedValue key(cx, ObjectValue(*obj));
453     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
454         obj.set(&p->value().get().toObject());
455         MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
456         return true;
457     }
458 
459     RootedObject existing(cx, existingArg);
460     if (existing) {
461         // Is it possible to reuse |existing|?
462         if (!existing->getTaggedProto().isLazy() ||
463             // Note: Class asserted above, so all that's left to check is callability
464             existing->isCallable() ||
465             obj->isCallable())
466         {
467             existing = nullptr;
468         }
469     }
470 
471     RootedObject wrapper(cx, cb->wrap(cx, existing, obj));
472     if (!wrapper)
473         return false;
474 
475     // We maintain the invariant that the key in the cross-compartment wrapper
476     // map is always directly wrapped by the value.
477     MOZ_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject());
478 
479     if (!putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*wrapper))) {
480         // Enforce the invariant that all cross-compartment wrapper object are
481         // in the map by nuking the wrapper if we couldn't add it.
482         // Unfortunately it's possible for the wrapper to still be marked if we
483         // took this path, for example if the object metadata callback stashes a
484         // reference to it.
485         if (wrapper->is<CrossCompartmentWrapperObject>())
486             NukeCrossCompartmentWrapper(cx, wrapper);
487         return false;
488     }
489 
490     obj.set(wrapper);
491     return true;
492 }
493 
494 bool
wrap(JSContext * cx,MutableHandle<PropertyDescriptor> desc)495 JSCompartment::wrap(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
496 {
497     if (!wrap(cx, desc.object()))
498         return false;
499 
500     if (desc.hasGetterObject()) {
501         if (!wrap(cx, desc.getterObject()))
502             return false;
503     }
504     if (desc.hasSetterObject()) {
505         if (!wrap(cx, desc.setterObject()))
506             return false;
507     }
508 
509     return wrap(cx, desc.value());
510 }
511 
512 ClonedBlockObject*
getOrCreateNonSyntacticLexicalScope(JSContext * cx,HandleObject enclosingStatic,HandleObject enclosingScope)513 JSCompartment::getOrCreateNonSyntacticLexicalScope(JSContext* cx,
514                                                    HandleObject enclosingStatic,
515                                                    HandleObject enclosingScope)
516 {
517     if (!nonSyntacticLexicalScopes_) {
518         nonSyntacticLexicalScopes_ = cx->new_<ObjectWeakMap>(cx);
519         if (!nonSyntacticLexicalScopes_ || !nonSyntacticLexicalScopes_->init())
520             return nullptr;
521     }
522 
523     // The key is the unwrapped dynamic scope, as we may be creating different
524     // DynamicWithObject wrappers each time.
525     MOZ_ASSERT(!enclosingScope->as<DynamicWithObject>().isSyntactic());
526     RootedObject key(cx, &enclosingScope->as<DynamicWithObject>().object());
527     RootedObject lexicalScope(cx, nonSyntacticLexicalScopes_->lookup(key));
528 
529     if (!lexicalScope) {
530         lexicalScope = ClonedBlockObject::createNonSyntactic(cx, enclosingStatic, enclosingScope);
531         if (!lexicalScope)
532             return nullptr;
533         if (!nonSyntacticLexicalScopes_->add(cx, key, lexicalScope))
534             return nullptr;
535     }
536 
537     return &lexicalScope->as<ClonedBlockObject>();
538 }
539 
540 ClonedBlockObject*
getNonSyntacticLexicalScope(JSObject * enclosingScope) const541 JSCompartment::getNonSyntacticLexicalScope(JSObject* enclosingScope) const
542 {
543     if (!nonSyntacticLexicalScopes_)
544         return nullptr;
545     if (!enclosingScope->is<DynamicWithObject>())
546         return nullptr;
547     JSObject* key = &enclosingScope->as<DynamicWithObject>().object();
548     JSObject* lexicalScope = nonSyntacticLexicalScopes_->lookup(key);
549     if (!lexicalScope)
550         return nullptr;
551     return &lexicalScope->as<ClonedBlockObject>();
552 }
553 
554 void
traceOutgoingCrossCompartmentWrappers(JSTracer * trc)555 JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
556 {
557     MOZ_ASSERT(trc->runtime()->isHeapMajorCollecting());
558     MOZ_ASSERT(!zone()->isCollecting() || trc->runtime()->gc.isHeapCompacting());
559 
560     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
561         Value v = e.front().value().unbarrieredGet();
562         if (e.front().key().kind == CrossCompartmentKey::ObjectWrapper) {
563             ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
564 
565             /*
566              * We have a cross-compartment wrapper. Its private pointer may
567              * point into the compartment being collected, so we should mark it.
568              */
569             TraceEdge(trc, wrapper->slotOfPrivate(), "cross-compartment wrapper");
570         }
571     }
572 }
573 
574 /* static */ void
traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer * trc)575 JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
576 {
577     MOZ_ASSERT(trc->runtime()->isHeapMajorCollecting());
578     for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
579         if (!c->zone()->isCollecting())
580             c->traceOutgoingCrossCompartmentWrappers(trc);
581     }
582     Debugger::markIncomingCrossCompartmentEdges(trc);
583 }
584 
585 void
trace(JSTracer * trc)586 JSCompartment::trace(JSTracer* trc)
587 {
588     savedStacks_.trace(trc);
589 }
590 
591 void
traceRoots(JSTracer * trc,js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)592 JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
593 {
594     if (objectMetadataState.is<PendingMetadata>()) {
595         TraceRoot(trc,
596                   objectMetadataState.as<PendingMetadata>().unsafeUnbarrieredForTracing(),
597                   "on-stack object pending metadata");
598     }
599 
600     if (!trc->runtime()->isHeapMinorCollecting()) {
601         // JIT code and the global are never nursery allocated, so we only need
602         // to trace them when not doing a minor collection.
603 
604         if (jitCompartment_)
605             jitCompartment_->mark(trc, this);
606 
607         // If a compartment is on-stack, we mark its global so that
608         // JSContext::global() remains valid.
609         if (enterCompartmentDepth && global_.unbarrieredGet())
610             TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
611     }
612 
613     // Nothing below here needs to be treated as a root if we aren't marking
614     // this zone for a collection.
615     if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollecting())
616         return;
617 
618     // During a GC, these are treated as weak pointers.
619     if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
620         if (watchpointMap)
621             watchpointMap->markAll(trc);
622     }
623 
624     /* Mark debug scopes, if present */
625     if (debugScopes)
626         debugScopes->mark(trc);
627 
628     if (lazyArrayBuffers)
629         lazyArrayBuffers->trace(trc);
630 
631     if (objectMetadataTable)
632         objectMetadataTable->trace(trc);
633 
634     // If code coverage is only enabled with the Debugger or the LCovOutput,
635     // then the following comment holds.
636     //
637     // The scriptCountsMap maps JSScript weak-pointers to ScriptCounts
638     // structures. It uses a HashMap instead of a WeakMap, so that we can keep
639     // the data alive for the JSScript::finalize call. Thus, we do not trace the
640     // keys of the HashMap to avoid adding a strong reference to the JSScript
641     // pointers. Additionally, we assert that the JSScripts have not been moved
642     // in JSCompartment::fixupAfterMovingGC.
643     //
644     // If the code coverage is either enabled with the --dump-bytecode command
645     // line option, or with the PCCount JSFriend API functions, then we mark the
646     // keys of the map to hold the JSScript alive.
647     if (scriptCountsMap &&
648         trc->runtime()->profilingScripts &&
649         !trc->runtime()->isHeapMinorCollecting())
650     {
651         MOZ_ASSERT_IF(!trc->runtime()->isBeingDestroyed(), collectCoverage());
652         for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
653             JSScript* script = const_cast<JSScript*>(r.front().key());
654             MOZ_ASSERT(script->hasScriptCounts());
655             TraceRoot(trc, &script, "profilingScripts");
656             MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
657         }
658     }
659 
660     if (nonSyntacticLexicalScopes_)
661         nonSyntacticLexicalScopes_->trace(trc);
662 }
663 
664 void
sweepAfterMinorGC()665 JSCompartment::sweepAfterMinorGC()
666 {
667     globalWriteBarriered = false;
668 
669     if (innerViews.needsSweepAfterMinorGC())
670         innerViews.sweepAfterMinorGC();
671 }
672 
673 void
sweepInnerViews()674 JSCompartment::sweepInnerViews()
675 {
676     innerViews.sweep();
677 }
678 
679 void
sweepSavedStacks()680 JSCompartment::sweepSavedStacks()
681 {
682     savedStacks_.sweep(runtimeFromAnyThread());
683 }
684 
685 void
sweepGlobalObject(FreeOp * fop)686 JSCompartment::sweepGlobalObject(FreeOp* fop)
687 {
688     if (global_ && IsAboutToBeFinalized(&global_)) {
689         if (isDebuggee())
690             Debugger::detachAllDebuggersFromGlobal(fop, global_.unbarrieredGet());
691         global_.set(nullptr);
692     }
693 }
694 
695 void
sweepObjectPendingMetadata()696 JSCompartment::sweepObjectPendingMetadata()
697 {
698     if (objectMetadataState.is<PendingMetadata>()) {
699         // We should never finalize an object before it gets its metadata! That
700         // would mean we aren't calling the object metadata callback for every
701         // object!
702         MOZ_ALWAYS_TRUE(!IsAboutToBeFinalized(&objectMetadataState.as<PendingMetadata>()));
703     }
704 }
705 
706 void
sweepSelfHostingScriptSource()707 JSCompartment::sweepSelfHostingScriptSource()
708 {
709     if (selfHostingScriptSource.unbarrieredGet() &&
710         IsAboutToBeFinalized(&selfHostingScriptSource))
711     {
712         selfHostingScriptSource.set(nullptr);
713     }
714 }
715 
716 void
sweepJitCompartment(FreeOp * fop)717 JSCompartment::sweepJitCompartment(FreeOp* fop)
718 {
719     if (jitCompartment_)
720         jitCompartment_->sweep(fop, this);
721 }
722 
723 void
sweepRegExps()724 JSCompartment::sweepRegExps()
725 {
726     /*
727      * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
728      * code for the lifetime of the JIT script. Thus, we must perform
729      * sweeping after clearing jit code.
730      */
731     regExps.sweep(runtimeFromAnyThread());
732 }
733 
734 void
sweepDebugScopes()735 JSCompartment::sweepDebugScopes()
736 {
737     JSRuntime* rt = runtimeFromAnyThread();
738     if (debugScopes)
739         debugScopes->sweep(rt);
740 }
741 
742 void
sweepNativeIterators()743 JSCompartment::sweepNativeIterators()
744 {
745     /* Sweep list of native iterators. */
746     NativeIterator* ni = enumerators->next();
747     while (ni != enumerators) {
748         JSObject* iterObj = ni->iterObj();
749         NativeIterator* next = ni->next();
750         if (gc::IsAboutToBeFinalizedUnbarriered(&iterObj))
751             ni->unlink();
752         ni = next;
753     }
754 }
755 
756 /*
757  * Remove dead wrappers from the table. We must sweep all compartments, since
758  * string entries in the crossCompartmentWrappers table are not marked during
759  * markCrossCompartmentWrappers.
760  */
761 void
sweepCrossCompartmentWrappers()762 JSCompartment::sweepCrossCompartmentWrappers()
763 {
764     /* Remove dead wrappers from the table. */
765     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
766         CrossCompartmentKey key = e.front().key();
767         bool keyDying;
768         switch (key.kind) {
769           case CrossCompartmentKey::ObjectWrapper:
770           case CrossCompartmentKey::DebuggerObject:
771           case CrossCompartmentKey::DebuggerEnvironment:
772           case CrossCompartmentKey::DebuggerSource:
773               MOZ_ASSERT(IsInsideNursery(key.wrapped) ||
774                          key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Object);
775               keyDying = IsAboutToBeFinalizedUnbarriered(
776                   reinterpret_cast<JSObject**>(&key.wrapped));
777               break;
778           case CrossCompartmentKey::StringWrapper:
779               MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JS::TraceKind::String);
780               keyDying = IsAboutToBeFinalizedUnbarriered(
781                   reinterpret_cast<JSString**>(&key.wrapped));
782               break;
783           case CrossCompartmentKey::DebuggerScript:
784               MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Script);
785               keyDying = IsAboutToBeFinalizedUnbarriered(
786                   reinterpret_cast<JSScript**>(&key.wrapped));
787               break;
788           default:
789               MOZ_CRASH("Unknown key kind");
790         }
791         bool valDying = IsAboutToBeFinalized(&e.front().value());
792         bool dbgDying = key.debugger && IsAboutToBeFinalizedUnbarriered(&key.debugger);
793         if (keyDying || valDying || dbgDying) {
794             MOZ_ASSERT(key.kind != CrossCompartmentKey::StringWrapper);
795             e.removeFront();
796         } else if (key.wrapped != e.front().key().wrapped ||
797                    key.debugger != e.front().key().debugger)
798         {
799             e.rekeyFront(key);
800         }
801     }
802 }
803 
804 void
sweepTemplateObjects()805 JSCompartment::sweepTemplateObjects()
806 {
807     if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
808         mappedArgumentsTemplate_.set(nullptr);
809 
810     if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
811         unmappedArgumentsTemplate_.set(nullptr);
812 }
813 
814 /* static */ void
fixupCrossCompartmentWrappersAfterMovingGC(JSTracer * trc)815 JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
816 {
817     MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
818 
819     for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
820         // Sweep the wrapper map to update its pointers to the wrappers.
821         comp->sweepCrossCompartmentWrappers();
822         // Trace the wrappers in the map to update their edges to their referents.
823         comp->traceOutgoingCrossCompartmentWrappers(trc);
824     }
825 }
826 
827 void
fixupAfterMovingGC()828 JSCompartment::fixupAfterMovingGC()
829 {
830     fixupGlobal();
831     fixupInitialShapeTable();
832     objectGroups.fixupTablesAfterMovingGC();
833 
834 #ifdef DEBUG
835     // Assert that none of the JSScript pointers, which are used as key of the
836     // scriptCountsMap HashMap are moved. We do not mark these keys because we
837     // need weak references. We do not use a WeakMap because these entries would
838     // be collected before the JSScript::finalize calls which is used to
839     // summarized the content of the code coverage.
840     if (scriptCountsMap) {
841         for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront())
842             MOZ_ASSERT(!IsForwarded(r.front().key()));
843     }
844 #endif
845 }
846 
847 void
fixupGlobal()848 JSCompartment::fixupGlobal()
849 {
850     GlobalObject* global = *global_.unsafeGet();
851     if (global)
852         global_.set(MaybeForwarded(global));
853 }
854 
855 void
purge()856 JSCompartment::purge()
857 {
858     dtoaCache.purge();
859 }
860 
861 void
clearTables()862 JSCompartment::clearTables()
863 {
864     global_.set(nullptr);
865 
866     // No scripts should have run in this compartment. This is used when
867     // merging a compartment that has been used off thread into another
868     // compartment and zone.
869     MOZ_ASSERT(crossCompartmentWrappers.empty());
870     MOZ_ASSERT(!jitCompartment_);
871     MOZ_ASSERT(!debugScopes);
872     MOZ_ASSERT(enumerators->next() == enumerators);
873     MOZ_ASSERT(regExps.empty());
874 
875     objectGroups.clearTables();
876     if (baseShapes.initialized())
877         baseShapes.clear();
878     if (initialShapes.initialized())
879         initialShapes.clear();
880     if (savedStacks_.initialized())
881         savedStacks_.clear();
882 }
883 
884 void
setObjectMetadataCallback(js::ObjectMetadataCallback callback)885 JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback)
886 {
887     // Clear any jitcode in the runtime, which behaves differently depending on
888     // whether there is a creation callback.
889     ReleaseAllJITCode(runtime_->defaultFreeOp());
890 
891     objectMetadataCallback = callback;
892 }
893 
894 void
clearObjectMetadata()895 JSCompartment::clearObjectMetadata()
896 {
897     js_delete(objectMetadataTable);
898     objectMetadataTable = nullptr;
899 }
900 
901 void
setNewObjectMetadata(JSContext * cx,JSObject * obj)902 JSCompartment::setNewObjectMetadata(JSContext* cx, JSObject* obj)
903 {
904     assertSameCompartment(cx, this, obj);
905 
906     if (JSObject* metadata = objectMetadataCallback(cx, obj)) {
907         AutoEnterOOMUnsafeRegion oomUnsafe;
908         assertSameCompartment(cx, metadata);
909         if (!objectMetadataTable) {
910             objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
911             if (!objectMetadataTable || !objectMetadataTable->init())
912                 oomUnsafe.crash("setNewObjectMetadata");
913         }
914         if (!objectMetadataTable->add(cx, obj, metadata))
915             oomUnsafe.crash("setNewObjectMetadata");
916     }
917 }
918 
919 static bool
AddInnerLazyFunctionsFromScript(JSScript * script,AutoObjectVector & lazyFunctions)920 AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
921 {
922     if (!script->hasObjects())
923         return true;
924     ObjectArray* objects = script->objects();
925     for (size_t i = script->innerObjectsStart(); i < objects->length; i++) {
926         JSObject* obj = objects->vector[i];
927         if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
928             if (!lazyFunctions.append(obj))
929                 return false;
930         }
931     }
932     return true;
933 }
934 
935 static bool
AddLazyFunctionsForCompartment(JSContext * cx,AutoObjectVector & lazyFunctions,AllocKind kind)936 AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
937 {
938     // Find all live root lazy functions in the compartment: those which have a
939     // source object, indicating that they have a parent, and which do not have
940     // an uncompiled enclosing script. The last condition is so that we don't
941     // compile lazy scripts whose enclosing scripts failed to compile,
942     // indicating that the lazy script did not escape the script.
943     //
944     // Some LazyScripts have a non-null |JSScript* script| pointer. We still
945     // want to delazify in that case: this pointer is weak so the JSScript
946     // could be destroyed at the next GC.
947 
948     for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) {
949         JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
950 
951         // Sweeping is incremental; take care to not delazify functions that
952         // are about to be finalized. GC things referenced by objects that are
953         // about to be finalized (e.g., in slots) may already be freed.
954         if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
955             fun->compartment() != cx->compartment())
956         {
957             continue;
958         }
959 
960         if (fun->isInterpretedLazy()) {
961             LazyScript* lazy = fun->lazyScriptOrNull();
962             if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {
963                 if (!lazyFunctions.append(fun))
964                     return false;
965             }
966         }
967     }
968 
969     return true;
970 }
971 
972 static bool
CreateLazyScriptsForCompartment(JSContext * cx)973 CreateLazyScriptsForCompartment(JSContext* cx)
974 {
975     AutoObjectVector lazyFunctions(cx);
976 
977     if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION))
978         return false;
979 
980     // Methods, for instance {get method() {}}, are extended functions that can
981     // be relazified, so we need to handle those as well.
982     if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
983         return false;
984 
985     // Create scripts for each lazy function, updating the list of functions to
986     // process with any newly exposed inner functions in created scripts.
987     // A function cannot be delazified until its outer script exists.
988     for (size_t i = 0; i < lazyFunctions.length(); i++) {
989         JSFunction* fun = &lazyFunctions[i]->as<JSFunction>();
990 
991         // lazyFunctions may have been populated with multiple functions for
992         // a lazy script.
993         if (!fun->isInterpretedLazy())
994             continue;
995 
996         LazyScript* lazy = fun->lazyScript();
997         bool lazyScriptHadNoScript = !lazy->maybeScript();
998 
999         JSScript* script = fun->getOrCreateScript(cx);
1000         if (!script)
1001             return false;
1002         if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
1003             return false;
1004     }
1005 
1006     return true;
1007 }
1008 
1009 bool
ensureDelazifyScriptsForDebugger(JSContext * cx)1010 JSCompartment::ensureDelazifyScriptsForDebugger(JSContext* cx)
1011 {
1012     MOZ_ASSERT(cx->compartment() == this);
1013     if (needsDelazificationForDebugger() && !CreateLazyScriptsForCompartment(cx))
1014         return false;
1015     debugModeBits &= ~DebuggerNeedsDelazification;
1016     return true;
1017 }
1018 
1019 void
updateDebuggerObservesFlag(unsigned flag)1020 JSCompartment::updateDebuggerObservesFlag(unsigned flag)
1021 {
1022     MOZ_ASSERT(isDebuggee());
1023     MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
1024                flag == DebuggerObservesCoverage ||
1025                flag == DebuggerObservesAsmJS);
1026 
1027     GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
1028                            ? unsafeUnbarrieredMaybeGlobal()
1029                            : maybeGlobal();
1030     const GlobalObject::DebuggerVector* v = global->getDebuggers();
1031     for (Debugger * const* p = v->begin(); p != v->end(); p++) {
1032         Debugger* dbg = *p;
1033         if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
1034             flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
1035             dbg->observesAsmJS())
1036         {
1037             debugModeBits |= flag;
1038             return;
1039         }
1040     }
1041 
1042     debugModeBits &= ~flag;
1043 }
1044 
1045 void
unsetIsDebuggee()1046 JSCompartment::unsetIsDebuggee()
1047 {
1048     if (isDebuggee()) {
1049         debugModeBits &= ~DebuggerObservesMask;
1050         DebugScopes::onCompartmentUnsetIsDebuggee(this);
1051     }
1052 }
1053 
1054 void
updateDebuggerObservesCoverage()1055 JSCompartment::updateDebuggerObservesCoverage()
1056 {
1057     bool previousState = debuggerObservesCoverage();
1058     updateDebuggerObservesFlag(DebuggerObservesCoverage);
1059     if (previousState == debuggerObservesCoverage())
1060         return;
1061 
1062     if (debuggerObservesCoverage()) {
1063         // Interrupt any running interpreter frame. The scriptCounts are
1064         // allocated on demand when a script resume its execution.
1065         for (ActivationIterator iter(runtimeFromMainThread()); !iter.done(); ++iter) {
1066             if (iter->isInterpreter())
1067                 iter->asInterpreter()->enableInterruptsUnconditionally();
1068         }
1069         return;
1070     }
1071 
1072     // If code coverage is enabled by any other means, keep it.
1073     if (collectCoverage())
1074         return;
1075 
1076     clearScriptCounts();
1077 }
1078 
1079 bool
collectCoverage() const1080 JSCompartment::collectCoverage() const
1081 {
1082     return !JitOptions.disablePgo ||
1083            debuggerObservesCoverage() ||
1084            runtimeFromAnyThread()->profilingScripts ||
1085            runtimeFromAnyThread()->lcovOutput.isEnabled();
1086 }
1087 
1088 void
clearScriptCounts()1089 JSCompartment::clearScriptCounts()
1090 {
1091     if (!scriptCountsMap)
1092         return;
1093 
1094     // Clear all hasScriptCounts_ flags of JSScript, in order to release all
1095     // ScriptCounts entry of the current compartment.
1096     for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
1097         ScriptCounts* value = &r.front().value();
1098         r.front().key()->takeOverScriptCountsMapEntry(value);
1099     }
1100 
1101     js_delete(scriptCountsMap);
1102     scriptCountsMap = nullptr;
1103 }
1104 
1105 void
clearBreakpointsIn(FreeOp * fop,js::Debugger * dbg,HandleObject handler)1106 JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
1107 {
1108     for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
1109         JSScript* script = i.get<JSScript>();
1110         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
1111             script->clearBreakpointsIn(fop, dbg, handler);
1112     }
1113 }
1114 
1115 void
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,size_t * tiAllocationSiteTables,size_t * tiArrayTypeTables,size_t * tiObjectTypeTables,size_t * compartmentObject,size_t * compartmentTables,size_t * innerViewsArg,size_t * lazyArrayBuffersArg,size_t * objectMetadataTablesArg,size_t * crossCompartmentWrappersArg,size_t * regexpCompartment,size_t * savedStacksSet,size_t * nonSyntacticLexicalScopesArg)1116 JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
1117                                       size_t* tiAllocationSiteTables,
1118                                       size_t* tiArrayTypeTables,
1119                                       size_t* tiObjectTypeTables,
1120                                       size_t* compartmentObject,
1121                                       size_t* compartmentTables,
1122                                       size_t* innerViewsArg,
1123                                       size_t* lazyArrayBuffersArg,
1124                                       size_t* objectMetadataTablesArg,
1125                                       size_t* crossCompartmentWrappersArg,
1126                                       size_t* regexpCompartment,
1127                                       size_t* savedStacksSet,
1128                                       size_t* nonSyntacticLexicalScopesArg)
1129 {
1130     *compartmentObject += mallocSizeOf(this);
1131     objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
1132                                         tiArrayTypeTables, tiObjectTypeTables,
1133                                         compartmentTables);
1134     *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
1135                         + initialShapes.sizeOfExcludingThis(mallocSizeOf);
1136     *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
1137     if (lazyArrayBuffers)
1138         *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
1139     if (objectMetadataTable)
1140         *objectMetadataTablesArg += objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
1141     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
1142     *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
1143     *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
1144     if (nonSyntacticLexicalScopes_)
1145         *nonSyntacticLexicalScopesArg += nonSyntacticLexicalScopes_->sizeOfIncludingThis(mallocSizeOf);
1146 }
1147 
1148 void
reportTelemetry()1149 JSCompartment::reportTelemetry()
1150 {
1151     // Only report telemetry for web content and add-ons, not chrome JS.
1152     if (isSystem_)
1153         return;
1154 
1155     // Hazard analysis can't tell that the telemetry callbacks don't GC.
1156     JS::AutoSuppressGCAnalysis nogc;
1157 
1158     int id = addonId
1159              ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
1160              : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
1161 
1162     // Call back into Firefox's Telemetry reporter.
1163     for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) {
1164         if (sawDeprecatedLanguageExtension[i])
1165             runtime_->addTelemetry(id, i);
1166     }
1167 }
1168 
1169 void
addTelemetry(const char * filename,DeprecatedLanguageExtension e)1170 JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
1171 {
1172     // Only report telemetry for web content and add-ons, not chrome JS.
1173     if (isSystem_ || (!addonId && (!filename || strncmp(filename, "http", 4) != 0)))
1174         return;
1175 
1176     sawDeprecatedLanguageExtension[e] = true;
1177 }
1178 
1179 HashNumber
randomHashCode()1180 JSCompartment::randomHashCode()
1181 {
1182     ensureRandomNumberGenerator();
1183     return HashNumber(randomNumberGenerator.ref().next());
1184 }
1185 
1186 mozilla::HashCodeScrambler
randomHashCodeScrambler()1187 JSCompartment::randomHashCodeScrambler()
1188 {
1189     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
1190                                       randomKeyGenerator_.next());
1191 }
1192 
AutoSetNewObjectMetadata(ExclusiveContext * ecx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)1193 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(ExclusiveContext* ecx
1194                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1195     : CustomAutoRooter(ecx)
1196     , cx_(ecx->maybeJSContext())
1197     , prevState_(ecx->compartment()->objectMetadataState)
1198 {
1199     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1200     if (cx_)
1201         cx_->compartment()->objectMetadataState = NewObjectMetadataState(DelayMetadata());
1202 }
1203 
~AutoSetNewObjectMetadata()1204 AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
1205 {
1206     // If we don't have a cx, we didn't change the metadata state, so no need to
1207     // reset it here.
1208     if (!cx_)
1209         return;
1210 
1211     if (!cx_->isExceptionPending() && cx_->compartment()->hasObjectPendingMetadata()) {
1212         JSObject* obj = cx_->compartment()->objectMetadataState.as<PendingMetadata>();
1213         // Make sure to restore the previous state before setting the object's
1214         // metadata. SetNewObjectMetadata asserts that the state is not
1215         // PendingMetadata in order to ensure that metadata callbacks are called
1216         // in order.
1217         cx_->compartment()->objectMetadataState = prevState_;
1218         SetNewObjectMetadata(cx_, obj);
1219     } else {
1220         cx_->compartment()->objectMetadataState = prevState_;
1221     }
1222 }
1223 
1224