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