1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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 "js/shadow/Realm.h"  // JS::shadow::Realm
8 #include "vm/Realm-inl.h"
9 
10 #include "mozilla/MemoryReporting.h"
11 
12 #include <stddef.h>
13 
14 #include "jsfriendapi.h"
15 
16 #include "debugger/DebugAPI.h"
17 #include "debugger/Debugger.h"
18 #include "gc/Policy.h"
19 #include "gc/PublicIterators.h"
20 #include "jit/JitOptions.h"
21 #include "jit/JitRealm.h"
22 #include "jit/JitRuntime.h"
23 #include "js/Date.h"
24 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
25 #include "js/Proxy.h"
26 #include "js/RootingAPI.h"
27 #include "js/Wrapper.h"
28 #include "proxy/DeadObjectProxy.h"
29 #include "vm/DateTime.h"
30 #include "vm/Iteration.h"
31 #include "vm/JSContext.h"
32 #include "vm/WrapperObject.h"
33 
34 #include "gc/GC-inl.h"
35 #include "gc/Marking-inl.h"
36 #include "vm/JSAtom-inl.h"
37 #include "vm/JSFunction-inl.h"
38 #include "vm/JSObject-inl.h"
39 #include "vm/JSScript-inl.h"
40 #include "vm/NativeObject-inl.h"
41 
42 using namespace js;
43 
DebuggerVectorEntry(js::Debugger * dbg_,JSObject * link)44 Realm::DebuggerVectorEntry::DebuggerVectorEntry(js::Debugger* dbg_,
45                                                 JSObject* link)
46     : dbg(dbg_), debuggerLink(link) {}
47 
ObjectRealm(JS::Zone * zone)48 ObjectRealm::ObjectRealm(JS::Zone* zone)
49     : innerViews(zone, zone), iteratorCache(zone) {}
50 
~ObjectRealm()51 ObjectRealm::~ObjectRealm() {
52   MOZ_ASSERT(enumerators == iteratorSentinel_.get());
53 }
54 
Realm(Compartment * comp,const JS::RealmOptions & options)55 Realm::Realm(Compartment* comp, const JS::RealmOptions& options)
56     : JS::shadow::Realm(comp),
57       zone_(comp->zone()),
58       runtime_(comp->runtimeFromMainThread()),
59       creationOptions_(options.creationOptions()),
60       behaviors_(options.behaviors()),
61       objects_(zone_),
62       varNames_(zone_),
63       randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
64       debuggers_(zone_),
65       wasm(runtime_) {
66   MOZ_ASSERT_IF(creationOptions_.mergeable(),
67                 creationOptions_.invisibleToDebugger());
68 
69   runtime_->numRealms++;
70 }
71 
~Realm()72 Realm::~Realm() {
73   MOZ_ASSERT(!hasBeenEnteredIgnoringJit());
74   MOZ_ASSERT(!isDebuggee());
75 
76   // Write the code coverage information in a file.
77   if (lcovRealm_) {
78     runtime_->lcovOutput().writeLCovResult(*lcovRealm_);
79   }
80 
81   MOZ_ASSERT(runtime_->numRealms > 0);
82   runtime_->numRealms--;
83 }
84 
init(JSContext * cx)85 bool ObjectRealm::init(JSContext* cx) {
86   NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
87   if (!sentinel) {
88     return false;
89   }
90 
91   iteratorSentinel_ = std::move(sentinel);
92   enumerators = iteratorSentinel_.get();
93   return true;
94 }
95 
init(JSContext * cx,JSPrincipals * principals)96 bool Realm::init(JSContext* cx, JSPrincipals* principals) {
97   /*
98    * As a hack, we clear our timezone cache every time we create a new realm.
99    * This ensures that the cache is always relatively fresh, but shouldn't
100    * interfere with benchmarks that create tons of date objects (unless they
101    * also create tons of iframes, which seems unlikely).
102    */
103   js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged);
104 
105   if (!objects_.init(cx)) {
106     return false;
107   }
108 
109   if (principals) {
110     // Any realm with the trusted principals -- and there can be
111     // multiple -- is a system realm.
112     isSystem_ = (principals == cx->runtime()->trustedPrincipals());
113     JS_HoldPrincipals(principals);
114     principals_ = principals;
115   }
116 
117   return true;
118 }
119 
setIsSelfHostingRealm()120 void Realm::setIsSelfHostingRealm() {
121   MOZ_ASSERT(!isSelfHostingRealm_);
122   MOZ_ASSERT(zone()->isSelfHostingZone());
123   isSelfHostingRealm_ = true;
124   isSystem_ = true;
125 }
126 
createJitRuntime(JSContext * cx)127 bool JSRuntime::createJitRuntime(JSContext* cx) {
128   using namespace js::jit;
129 
130   MOZ_ASSERT(!jitRuntime_);
131 
132   if (!CanLikelyAllocateMoreExecutableMemory()) {
133     // Try to release memory first instead of potentially reporting OOM below.
134     if (OnLargeAllocationFailure) {
135       OnLargeAllocationFailure();
136     }
137   }
138 
139   jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>();
140   if (!jrt) {
141     return false;
142   }
143 
144   // Unfortunately, initialization depends on jitRuntime_ being non-null, so
145   // we can't just wait to assign jitRuntime_.
146   jitRuntime_ = jrt;
147 
148   if (!jitRuntime_->initialize(cx)) {
149     js_delete(jitRuntime_.ref());
150     jitRuntime_ = nullptr;
151     return false;
152   }
153 
154   return true;
155 }
156 
ensureJitRealmExists(JSContext * cx)157 bool Realm::ensureJitRealmExists(JSContext* cx) {
158   using namespace js::jit;
159 
160   if (jitRealm_) {
161     return true;
162   }
163 
164   if (!zone()->getJitZone(cx)) {
165     return false;
166   }
167 
168   UniquePtr<JitRealm> jitRealm = cx->make_unique<JitRealm>();
169   if (!jitRealm) {
170     return false;
171   }
172 
173   if (!jitRealm->initialize(cx, zone()->allocNurseryStrings)) {
174     return false;
175   }
176 
177   jitRealm_ = std::move(jitRealm);
178   return true;
179 }
180 
181 #ifdef JSGC_HASH_TABLE_CHECKS
182 
checkCacheAfterMovingGC()183 void js::DtoaCache::checkCacheAfterMovingGC() {
184   MOZ_ASSERT(!s || !IsForwarded(s));
185 }
186 
187 #endif  // JSGC_HASH_TABLE_CHECKS
188 
189 NonSyntacticLexicalEnvironmentObject*
getOrCreateNonSyntacticLexicalEnvironment(JSContext * cx,HandleObject enclosing,HandleObject key,HandleObject thisv)190 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx,
191                                                        HandleObject enclosing,
192                                                        HandleObject key,
193                                                        HandleObject thisv) {
194   MOZ_ASSERT(&ObjectRealm::get(enclosing) == this);
195 
196   if (!nonSyntacticLexicalEnvironments_) {
197     auto map = cx->make_unique<ObjectWeakMap>(cx);
198     if (!map) {
199       return nullptr;
200     }
201 
202     nonSyntacticLexicalEnvironments_ = std::move(map);
203   }
204 
205   RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));
206 
207   if (!lexicalEnv) {
208     MOZ_ASSERT(key->is<NonSyntacticVariablesObject>() ||
209                !key->is<EnvironmentObject>());
210     lexicalEnv =
211         NonSyntacticLexicalEnvironmentObject::create(cx, enclosing, thisv);
212     if (!lexicalEnv) {
213       return nullptr;
214     }
215     if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv)) {
216       return nullptr;
217     }
218   }
219 
220   return &lexicalEnv->as<NonSyntacticLexicalEnvironmentObject>();
221 }
222 
223 NonSyntacticLexicalEnvironmentObject*
getOrCreateNonSyntacticLexicalEnvironment(JSContext * cx,HandleObject enclosing)224 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx,
225                                                        HandleObject enclosing) {
226   // If a wrapped WithEnvironmentObject was passed in, unwrap it, as we may
227   // be creating different WithEnvironmentObject wrappers each time.
228   RootedObject key(cx, enclosing);
229   if (enclosing->is<WithEnvironmentObject>()) {
230     MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
231     key = &enclosing->as<WithEnvironmentObject>().object();
232   }
233 
234   // NOTE: The default global |this| value is set to key for compatibility
235   // with existing users of the lexical environment cache.
236   //  - When used by shared-global JSM loader, |this| must be the
237   //    NonSyntacticVariablesObject passed as enclosing.
238   //  - When used by SubscriptLoader, |this| must be the target object of
239   //    the WithEnvironmentObject wrapper.
240   //  - When used by XBL/DOM Events, we execute directly as a function and
241   //    do not access the |this| value.
242   // See js::GetFunctionThis / js::GetNonSyntacticGlobalThis
243   return getOrCreateNonSyntacticLexicalEnvironment(cx, enclosing, key,
244                                                    /*thisv = */ key);
245 }
246 
247 NonSyntacticLexicalEnvironmentObject*
getNonSyntacticLexicalEnvironment(JSObject * key) const248 ObjectRealm::getNonSyntacticLexicalEnvironment(JSObject* key) const {
249   MOZ_ASSERT(&ObjectRealm::get(key) == this);
250 
251   if (!nonSyntacticLexicalEnvironments_) {
252     return nullptr;
253   }
254   // If a wrapped WithEnvironmentObject was passed in, unwrap it as in
255   // getOrCreateNonSyntacticLexicalEnvironment.
256   if (key->is<WithEnvironmentObject>()) {
257     MOZ_ASSERT(!key->as<WithEnvironmentObject>().isSyntactic());
258     key = &key->as<WithEnvironmentObject>().object();
259   }
260   JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
261   if (!lexicalEnv) {
262     return nullptr;
263   }
264   return &lexicalEnv->as<NonSyntacticLexicalEnvironmentObject>();
265 }
266 
addToVarNames(JSContext * cx,JS::Handle<JSAtom * > name)267 bool Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name) {
268   MOZ_ASSERT(name);
269 
270   if (varNames_.put(name)) {
271     return true;
272   }
273 
274   ReportOutOfMemory(cx);
275   return false;
276 }
277 
traceGlobal(JSTracer * trc)278 void Realm::traceGlobal(JSTracer* trc) {
279   // Trace things reachable from the realm's global. Note that these edges
280   // must be swept too in case the realm is live but the global is not.
281 
282   TraceEdge(trc, &lexicalEnv_, "realm-global-lexical");
283 
284   savedStacks_.trace(trc);
285 
286   DebugAPI::traceFromRealm(trc, this);
287 
288   // Atoms are always tenured.
289   if (!JS::RuntimeHeapIsMinorCollecting()) {
290     varNames_.trace(trc);
291   }
292 }
293 
trace(JSTracer * trc)294 void ObjectRealm::trace(JSTracer* trc) {
295   if (objectMetadataTable) {
296     objectMetadataTable->trace(trc);
297   }
298 
299   if (nonSyntacticLexicalEnvironments_) {
300     nonSyntacticLexicalEnvironments_->trace(trc);
301   }
302 }
303 
traceRoots(JSTracer * trc,js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)304 void Realm::traceRoots(JSTracer* trc,
305                        js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark) {
306   if (objectMetadataState_.is<PendingMetadata>()) {
307     GCPolicy<NewObjectMetadataState>::trace(trc, &objectMetadataState_,
308                                             "on-stack object pending metadata");
309   }
310 
311   if (!JS::RuntimeHeapIsMinorCollecting()) {
312     // The global is never nursery allocated, so we don't need to
313     // trace it when doing a minor collection.
314     //
315     // If a realm is on-stack, we mark its global so that
316     // JSContext::global() remains valid.
317     if (shouldTraceGlobal() && global_) {
318       TraceRoot(trc, global_.unbarrieredAddress(), "on-stack realm global");
319     }
320   }
321 
322   // Nothing below here needs to be treated as a root if we aren't marking
323   // this zone for a collection.
324   if (traceOrMark == js::gc::GCRuntime::MarkRuntime &&
325       !zone()->isCollectingFromAnyThread()) {
326     return;
327   }
328 
329   /* Mark debug scopes, if present */
330   if (debugEnvs_) {
331     debugEnvs_->trace(trc);
332   }
333 
334   objects_.trace(trc);
335 }
336 
finishRoots()337 void ObjectRealm::finishRoots() {
338   if (objectMetadataTable) {
339     objectMetadataTable->clear();
340   }
341 
342   if (nonSyntacticLexicalEnvironments_) {
343     nonSyntacticLexicalEnvironments_->clear();
344   }
345 }
346 
finishRoots()347 void Realm::finishRoots() {
348   if (debugEnvs_) {
349     debugEnvs_->finish();
350   }
351 
352   objects_.finishRoots();
353 }
354 
sweepAfterMinorGC()355 void ObjectRealm::sweepAfterMinorGC() {
356   InnerViewTable& table = innerViews.get();
357   if (table.needsSweepAfterMinorGC()) {
358     table.sweepAfterMinorGC();
359   }
360 }
361 
sweepAfterMinorGC()362 void Realm::sweepAfterMinorGC() {
363   globalWriteBarriered = 0;
364   dtoaCache.purge();
365   objects_.sweepAfterMinorGC();
366 }
367 
traceWeakSavedStacks(JSTracer * trc)368 void Realm::traceWeakSavedStacks(JSTracer* trc) { savedStacks_.traceWeak(trc); }
369 
traceWeakObjects(JSTracer * trc)370 void Realm::traceWeakObjects(JSTracer* trc) {
371   if (global_) {
372     TraceWeakEdge(trc, &global_, "Realm::global_");
373   }
374   if (lexicalEnv_) {
375     TraceWeakEdge(trc, &lexicalEnv_, "Realm::lexicalEnv_");
376   }
377 }
378 
traceWeakSelfHostingScriptSource(JSTracer * trc)379 void Realm::traceWeakSelfHostingScriptSource(JSTracer* trc) {
380   if (selfHostingScriptSource.unbarrieredGet()) {
381     TraceWeakEdge(trc, &selfHostingScriptSource,
382                   "Realm::selfHostingScriptSource");
383   }
384 }
385 
traceWeakEdgesInJitRealm(JSTracer * trc)386 void Realm::traceWeakEdgesInJitRealm(JSTracer* trc) {
387   if (jitRealm_) {
388     jitRealm_->traceWeak(trc, this);
389   }
390 }
391 
traceWeakRegExps(JSTracer * trc)392 void Realm::traceWeakRegExps(JSTracer* trc) {
393   /*
394    * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
395    * code for the lifetime of the JIT script. Thus, we must perform
396    * sweeping after clearing jit code.
397    */
398   regExps.traceWeak(trc);
399 }
400 
sweepDebugEnvironments()401 void Realm::sweepDebugEnvironments() {
402   if (debugEnvs_) {
403     debugEnvs_->sweep();
404   }
405 }
406 
traceWeakNativeIterators(JSTracer * trc)407 void ObjectRealm::traceWeakNativeIterators(JSTracer* trc) {
408   /* Sweep list of native iterators. */
409   NativeIterator* ni = enumerators->next();
410   while (ni != enumerators) {
411     JSObject* iterObj = ni->iterObj();
412     NativeIterator* next = ni->next();
413     if (!TraceManuallyBarrieredWeakEdge(trc, &iterObj,
414                                         "ObjectRealm::enumerators")) {
415       ni->unlink();
416     }
417     MOZ_ASSERT_IF(ni->objectBeingIterated(),
418                   &ObjectRealm::get(ni->objectBeingIterated()) == this);
419     ni = next;
420   }
421 }
422 
traceWeakObjectRealm(JSTracer * trc)423 void Realm::traceWeakObjectRealm(JSTracer* trc) {
424   objects_.traceWeakNativeIterators(trc);
425 }
426 
tracekWeakVarNames(JSTracer * trc)427 void Realm::tracekWeakVarNames(JSTracer* trc) { varNames_.traceWeak(trc); }
428 
traceWeakTemplateObjects(JSTracer * trc)429 void Realm::traceWeakTemplateObjects(JSTracer* trc) {
430   if (mappedArgumentsTemplate_) {
431     TraceWeakEdge(trc, &mappedArgumentsTemplate_,
432                   "Realm::mappedArgumentsTemplate_");
433   }
434 
435   if (unmappedArgumentsTemplate_) {
436     TraceWeakEdge(trc, &unmappedArgumentsTemplate_,
437                   "Realm::unmappedArgumentsTemplate_");
438   }
439 
440   if (iterResultTemplate_) {
441     TraceWeakEdge(trc, &iterResultTemplate_, "Realm::iterResultTemplate_");
442   }
443 
444   if (iterResultWithoutPrototypeTemplate_) {
445     TraceWeakEdge(trc, &iterResultWithoutPrototypeTemplate_,
446                   "Realm::iterResultWithoutPrototypeTemplate_");
447   }
448 }
449 
fixupAfterMovingGC(JSTracer * trc)450 void Realm::fixupAfterMovingGC(JSTracer* trc) {
451   purge();
452   fixupGlobal();
453 }
454 
fixupGlobal()455 void Realm::fixupGlobal() {
456   GlobalObject* global = global_.unbarrieredGet();
457   if (global) {
458     global_.unbarrieredSet(MaybeForwarded(global));
459   }
460 }
461 
purge()462 void Realm::purge() {
463   dtoaCache.purge();
464   newProxyCache.purge();
465   objects_.iteratorCache.clearAndCompact();
466   arraySpeciesLookup.purge();
467   promiseLookup.purge();
468 }
469 
clearTables()470 void Realm::clearTables() {
471   global_.set(nullptr);
472   lexicalEnv_.set(nullptr);
473 
474   // No scripts should have run in this realm. This is used when merging
475   // a realm that has been used off thread into another realm and zone.
476   compartment()->assertNoCrossCompartmentWrappers();
477   MOZ_ASSERT(!jitRealm_);
478   MOZ_ASSERT(!debugEnvs_);
479   MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
480 
481   savedStacks_.clear();
482   varNames_.clear();
483 }
484 
485 // Check to see if this individual realm is recording allocations. Debuggers or
486 // runtimes can try and record allocations, so this method can check to see if
487 // any initialization is needed.
isRecordingAllocations()488 bool Realm::isRecordingAllocations() { return !!allocationMetadataBuilder_; }
489 
setAllocationMetadataBuilder(const js::AllocationMetadataBuilder * builder)490 void Realm::setAllocationMetadataBuilder(
491     const js::AllocationMetadataBuilder* builder) {
492   // Clear any jitcode in the runtime, which behaves differently depending on
493   // whether there is a creation callback.
494   ReleaseAllJITCode(runtime_->defaultFreeOp());
495 
496   allocationMetadataBuilder_ = builder;
497 }
498 
forgetAllocationMetadataBuilder()499 void Realm::forgetAllocationMetadataBuilder() {
500   // Unlike setAllocationMetadataBuilder, we don't have to discard all JIT
501   // code here (code is still valid, just a bit slower because it doesn't do
502   // inline GC allocations when a metadata builder is present), but we do want
503   // to cancel off-thread Ion compilations to avoid races when Ion calls
504   // hasAllocationMetadataBuilder off-thread.
505   CancelOffThreadIonCompile(this);
506 
507   allocationMetadataBuilder_ = nullptr;
508 }
509 
setNewObjectMetadata(JSContext * cx,HandleObject obj)510 void Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj) {
511   MOZ_ASSERT(obj->maybeCCWRealm() == this);
512   cx->check(compartment(), obj);
513 
514   AutoEnterOOMUnsafeRegion oomUnsafe;
515   if (JSObject* metadata =
516           allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
517     MOZ_ASSERT(metadata->maybeCCWRealm() == obj->maybeCCWRealm());
518     cx->check(metadata);
519 
520     if (!objects_.objectMetadataTable) {
521       auto table = cx->make_unique<ObjectWeakMap>(cx);
522       if (!table) {
523         oomUnsafe.crash("setNewObjectMetadata");
524       }
525 
526       objects_.objectMetadataTable = std::move(table);
527     }
528 
529     if (!objects_.objectMetadataTable->add(cx, obj, metadata)) {
530       oomUnsafe.crash("setNewObjectMetadata");
531     }
532   }
533 }
534 
updateDebuggerObservesFlag(unsigned flag)535 void Realm::updateDebuggerObservesFlag(unsigned flag) {
536   MOZ_ASSERT(isDebuggee());
537   MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
538              flag == DebuggerObservesCoverage || flag == DebuggerObservesAsmJS);
539 
540   GlobalObject* global =
541       zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
542           ? unsafeUnbarrieredMaybeGlobal()
543           : maybeGlobal();
544   bool observes = false;
545   if (flag == DebuggerObservesAllExecution) {
546     observes = DebugAPI::debuggerObservesAllExecution(global);
547   } else if (flag == DebuggerObservesCoverage) {
548     observes = DebugAPI::debuggerObservesCoverage(global);
549   } else if (flag == DebuggerObservesAsmJS) {
550     observes = DebugAPI::debuggerObservesAsmJS(global);
551   }
552 
553   if (observes) {
554     debugModeBits_ |= flag;
555   } else {
556     debugModeBits_ &= ~flag;
557   }
558 }
559 
setIsDebuggee()560 void Realm::setIsDebuggee() {
561   if (!isDebuggee()) {
562     debugModeBits_ |= IsDebuggee;
563     runtimeFromMainThread()->incrementNumDebuggeeRealms();
564   }
565 }
566 
unsetIsDebuggee()567 void Realm::unsetIsDebuggee() {
568   if (isDebuggee()) {
569     if (debuggerObservesCoverage()) {
570       runtime_->decrementNumDebuggeeRealmsObservingCoverage();
571     }
572     debugModeBits_ = 0;
573     DebugEnvironments::onRealmUnsetIsDebuggee(this);
574     runtimeFromMainThread()->decrementNumDebuggeeRealms();
575   }
576 }
577 
updateDebuggerObservesCoverage()578 void Realm::updateDebuggerObservesCoverage() {
579   bool previousState = debuggerObservesCoverage();
580   updateDebuggerObservesFlag(DebuggerObservesCoverage);
581   if (previousState == debuggerObservesCoverage()) {
582     return;
583   }
584 
585   if (debuggerObservesCoverage()) {
586     // Interrupt any running interpreter frame. The scriptCounts are
587     // allocated on demand when a script resumes its execution.
588     JSContext* cx = TlsContext.get();
589     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
590       if (iter->isInterpreter()) {
591         iter->asInterpreter()->enableInterruptsUnconditionally();
592       }
593     }
594     runtime_->incrementNumDebuggeeRealmsObservingCoverage();
595     return;
596   }
597 
598   runtime_->decrementNumDebuggeeRealmsObservingCoverage();
599 
600   // If code coverage is enabled by any other means, keep it.
601   if (collectCoverageForDebug()) {
602     return;
603   }
604 
605   clearScriptCounts();
606   clearScriptLCov();
607 }
608 
lcovRealm()609 coverage::LCovRealm* Realm::lcovRealm() {
610   if (!lcovRealm_) {
611     lcovRealm_ = js::MakeUnique<coverage::LCovRealm>(this);
612   }
613   return lcovRealm_.get();
614 }
615 
collectCoverageForDebug() const616 bool Realm::collectCoverageForDebug() const {
617   return debuggerObservesCoverage() || coverage::IsLCovEnabled();
618 }
619 
clearScriptCounts()620 void Realm::clearScriptCounts() { zone()->clearScriptCounts(this); }
621 
clearScriptLCov()622 void Realm::clearScriptLCov() { zone()->clearScriptLCov(this); }
623 
addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,size_t * innerViewsArg,size_t * objectMetadataTablesArg,size_t * nonSyntacticLexicalEnvironmentsArg)624 void ObjectRealm::addSizeOfExcludingThis(
625     mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg,
626     size_t* objectMetadataTablesArg,
627     size_t* nonSyntacticLexicalEnvironmentsArg) {
628   *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
629 
630   if (objectMetadataTable) {
631     *objectMetadataTablesArg +=
632         objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
633   }
634 
635   if (auto& map = nonSyntacticLexicalEnvironments_) {
636     *nonSyntacticLexicalEnvironmentsArg +=
637         map->sizeOfIncludingThis(mallocSizeOf);
638   }
639 }
640 
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,size_t * realmObject,size_t * realmTables,size_t * innerViewsArg,size_t * objectMetadataTablesArg,size_t * savedStacksSet,size_t * varNamesSet,size_t * nonSyntacticLexicalEnvironmentsArg,size_t * jitRealm)641 void Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
642                                    size_t* realmObject, size_t* realmTables,
643                                    size_t* innerViewsArg,
644                                    size_t* objectMetadataTablesArg,
645                                    size_t* savedStacksSet, size_t* varNamesSet,
646                                    size_t* nonSyntacticLexicalEnvironmentsArg,
647                                    size_t* jitRealm) {
648   *realmObject += mallocSizeOf(this);
649   wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
650 
651   objects_.addSizeOfExcludingThis(mallocSizeOf, innerViewsArg,
652                                   objectMetadataTablesArg,
653                                   nonSyntacticLexicalEnvironmentsArg);
654 
655   *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
656   *varNamesSet += varNames_.shallowSizeOfExcludingThis(mallocSizeOf);
657 
658   if (jitRealm_) {
659     *jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
660   }
661 }
662 
randomHashCodeScrambler()663 mozilla::HashCodeScrambler Realm::randomHashCodeScrambler() {
664   return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
665                                     randomKeyGenerator_.next());
666 }
667 
AutoSetNewObjectMetadata(JSContext * cx)668 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx)
669     : cx_(cx->isHelperThreadContext() ? nullptr : cx),
670       prevState_(cx, cx->realm()->objectMetadataState_) {
671   if (cx_) {
672     cx_->realm()->objectMetadataState_ =
673         NewObjectMetadataState(DelayMetadata());
674   }
675 }
676 
~AutoSetNewObjectMetadata()677 AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata() {
678   // If we don't have a cx, we didn't change the metadata state, so no need to
679   // reset it here.
680   if (!cx_) {
681     return;
682   }
683 
684   if (!cx_->isExceptionPending() && cx_->realm()->hasObjectPendingMetadata()) {
685     // This destructor often runs upon exit from a function that is
686     // returning an unrooted pointer to a Cell. The allocation metadata
687     // callback often allocates; if it causes a GC, then the Cell pointer
688     // being returned won't be traced or relocated.
689     //
690     // The only extant callbacks are those internal to SpiderMonkey that
691     // capture the JS stack. In fact, we're considering removing general
692     // callbacks altogther in bug 1236748. Since it's not running arbitrary
693     // code, it's adequate to simply suppress GC while we run the callback.
694     gc::AutoSuppressGC autoSuppressGC(cx_);
695 
696     JSObject* obj = cx_->realm()->objectMetadataState_.as<PendingMetadata>();
697 
698     // Make sure to restore the previous state before setting the object's
699     // metadata. SetNewObjectMetadata asserts that the state is not
700     // PendingMetadata in order to ensure that metadata callbacks are called
701     // in order.
702     cx_->realm()->objectMetadataState_ = prevState_;
703 
704     obj = SetNewObjectMetadata(cx_, obj);
705   } else {
706     cx_->realm()->objectMetadataState_ = prevState_;
707   }
708 }
709 
TraceRealm(JSTracer * trc,JS::Realm * realm,const char * name)710 JS_PUBLIC_API void gc::TraceRealm(JSTracer* trc, JS::Realm* realm,
711                                   const char* name) {
712   // The way GC works with compartments is basically incomprehensible.
713   // For Realms, what we want is very simple: each Realm has a strong
714   // reference to its GlobalObject, and vice versa.
715   //
716   // Here we simply trace our side of that edge. During GC,
717   // GCRuntime::traceRuntimeCommon() marks all other realm roots, for
718   // all realms.
719   realm->traceGlobal(trc);
720 }
721 
RealmNeedsSweep(JS::Realm * realm)722 JS_PUBLIC_API bool gc::RealmNeedsSweep(JS::Realm* realm) {
723   return realm->globalIsAboutToBeFinalized();
724 }
725 
GetCurrentRealmOrNull(JSContext * cx)726 JS_PUBLIC_API JS::Realm* JS::GetCurrentRealmOrNull(JSContext* cx) {
727   return cx->realm();
728 }
729 
GetObjectRealmOrNull(JSObject * obj)730 JS_PUBLIC_API JS::Realm* JS::GetObjectRealmOrNull(JSObject* obj) {
731   return IsCrossCompartmentWrapper(obj) ? nullptr : obj->nonCCWRealm();
732 }
733 
GetRealmPrivate(JS::Realm * realm)734 JS_PUBLIC_API void* JS::GetRealmPrivate(JS::Realm* realm) {
735   return realm->realmPrivate();
736 }
737 
SetRealmPrivate(JS::Realm * realm,void * data)738 JS_PUBLIC_API void JS::SetRealmPrivate(JS::Realm* realm, void* data) {
739   realm->setRealmPrivate(data);
740 }
741 
SetDestroyRealmCallback(JSContext * cx,JS::DestroyRealmCallback callback)742 JS_PUBLIC_API void JS::SetDestroyRealmCallback(
743     JSContext* cx, JS::DestroyRealmCallback callback) {
744   cx->runtime()->destroyRealmCallback = callback;
745 }
746 
SetRealmNameCallback(JSContext * cx,JS::RealmNameCallback callback)747 JS_PUBLIC_API void JS::SetRealmNameCallback(JSContext* cx,
748                                             JS::RealmNameCallback callback) {
749   cx->runtime()->realmNameCallback = callback;
750 }
751 
GetRealmGlobalOrNull(JS::Realm * realm)752 JS_PUBLIC_API JSObject* JS::GetRealmGlobalOrNull(JS::Realm* realm) {
753   return realm->maybeGlobal();
754 }
755 
InitRealmStandardClasses(JSContext * cx)756 JS_PUBLIC_API bool JS::InitRealmStandardClasses(JSContext* cx) {
757   MOZ_ASSERT(!cx->zone()->isAtomsZone());
758   AssertHeapIsIdle();
759   CHECK_THREAD(cx);
760   return GlobalObject::initStandardClasses(cx, cx->global());
761 }
762 
GetRealmObjectPrototype(JSContext * cx)763 JS_PUBLIC_API JSObject* JS::GetRealmObjectPrototype(JSContext* cx) {
764   CHECK_THREAD(cx);
765   return GlobalObject::getOrCreateObjectPrototype(cx, cx->global());
766 }
767 
GetRealmFunctionPrototype(JSContext * cx)768 JS_PUBLIC_API JSObject* JS::GetRealmFunctionPrototype(JSContext* cx) {
769   CHECK_THREAD(cx);
770   return GlobalObject::getOrCreateFunctionPrototype(cx, cx->global());
771 }
772 
GetRealmArrayPrototype(JSContext * cx)773 JS_PUBLIC_API JSObject* JS::GetRealmArrayPrototype(JSContext* cx) {
774   CHECK_THREAD(cx);
775   return GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
776 }
777 
GetRealmErrorPrototype(JSContext * cx)778 JS_PUBLIC_API JSObject* JS::GetRealmErrorPrototype(JSContext* cx) {
779   CHECK_THREAD(cx);
780   return GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(),
781                                                        JSEXN_ERR);
782 }
783 
GetRealmIteratorPrototype(JSContext * cx)784 JS_PUBLIC_API JSObject* JS::GetRealmIteratorPrototype(JSContext* cx) {
785   CHECK_THREAD(cx);
786   return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
787 }
788 
GetRealmKeyObject(JSContext * cx)789 JS_PUBLIC_API JSObject* JS::GetRealmKeyObject(JSContext* cx) {
790   return GlobalObject::getOrCreateRealmKeyObject(cx, cx->global());
791 }
792 
GetFunctionRealm(JSContext * cx,HandleObject objArg)793 JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) {
794   // https://tc39.github.io/ecma262/#sec-getfunctionrealm
795   // 7.3.22 GetFunctionRealm ( obj )
796 
797   CHECK_THREAD(cx);
798   cx->check(objArg);
799 
800   RootedObject obj(cx, objArg);
801   while (true) {
802     obj = CheckedUnwrapStatic(obj);
803     if (!obj) {
804       ReportAccessDenied(cx);
805       return nullptr;
806     }
807 
808     // Step 1.
809     MOZ_ASSERT(IsCallable(obj));
810 
811     // Steps 2 and 3. We use a loop instead of recursion to unwrap bound
812     // functions.
813     if (obj->is<JSFunction>()) {
814       JSFunction* fun = &obj->as<JSFunction>();
815       if (!fun->isBoundFunction()) {
816         return fun->realm();
817       }
818 
819       obj = fun->getBoundFunctionTarget();
820       continue;
821     }
822 
823     // Step 4.
824     if (IsScriptedProxy(obj)) {
825       // Steps 4.a-b.
826       JSObject* proxyTarget = GetProxyTargetObject(obj);
827       if (!proxyTarget) {
828         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
829                                   JSMSG_PROXY_REVOKED);
830         return nullptr;
831       }
832 
833       // Step 4.c.
834       obj = proxyTarget;
835       continue;
836     }
837 
838     // Step 5.
839     return cx->realm();
840   }
841 }
842