Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "vm/Realm-inl.h"
8 :
9 : #include "mozilla/MemoryReporting.h"
10 :
11 : #include <stddef.h>
12 :
13 : #include "jsfriendapi.h"
14 :
15 : #include "gc/Policy.h"
16 : #include "gc/PublicIterators.h"
17 : #include "jit/JitOptions.h"
18 : #include "jit/JitRealm.h"
19 : #include "js/Date.h"
20 : #include "js/Proxy.h"
21 : #include "js/RootingAPI.h"
22 : #include "js/Wrapper.h"
23 : #include "proxy/DeadObjectProxy.h"
24 : #include "vm/Debugger.h"
25 : #include "vm/Iteration.h"
26 : #include "vm/JSContext.h"
27 : #include "vm/WrapperObject.h"
28 :
29 : #include "gc/GC-inl.h"
30 : #include "gc/Marking-inl.h"
31 : #include "vm/JSAtom-inl.h"
32 : #include "vm/JSFunction-inl.h"
33 : #include "vm/JSObject-inl.h"
34 : #include "vm/JSScript-inl.h"
35 : #include "vm/NativeObject-inl.h"
36 : #include "vm/UnboxedObject-inl.h"
37 :
38 : using namespace js;
39 : using namespace js::gc;
40 : using namespace js::jit;
41 :
42 47 : ObjectRealm::ObjectRealm(JS::Zone* zone)
43 0 : : innerViews(zone)
44 0 : {}
45 :
46 35 : ObjectRealm::~ObjectRealm()
47 : {
48 10 : MOZ_ASSERT(enumerators == iteratorSentinel_.get());
49 0 : }
50 :
51 0 : Realm::Realm(Compartment* comp, const JS::RealmOptions& options)
52 : : JS::shadow::Realm(comp),
53 47 : zone_(comp->zone()),
54 47 : runtime_(comp->runtimeFromMainThread()),
55 0 : creationOptions_(options.creationOptions()),
56 47 : behaviors_(options.behaviors()),
57 : global_(nullptr),
58 : objects_(zone_),
59 47 : randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
60 : wasm(runtime_),
61 0 : performanceMonitoring(runtime_)
62 : {
63 0 : MOZ_ASSERT_IF(creationOptions_.mergeable(),
64 : creationOptions_.invisibleToDebugger());
65 :
66 94 : runtime_->numRealms++;
67 0 : }
68 :
69 0 : Realm::~Realm()
70 : {
71 10 : MOZ_ASSERT(!hasBeenEnteredIgnoringJit());
72 :
73 : // Write the code coverage information in a file.
74 5 : JSRuntime* rt = runtimeFromMainThread();
75 0 : if (rt->lcovOutput().isEnabled())
76 10 : rt->lcovOutput().writeLCovResult(lcovOutput);
77 :
78 : #ifdef DEBUG
79 : // Avoid assertion destroying the unboxed layouts list if the embedding
80 : // leaked GC things.
81 0 : if (!runtime_->gc.shutdownCollectedEverything())
82 0 : objectGroups_.unboxedLayouts.clear();
83 : #endif
84 :
85 10 : MOZ_ASSERT(runtime_->numRealms > 0);
86 0 : runtime_->numRealms--;
87 5 : }
88 :
89 : bool
90 47 : ObjectRealm::init(JSContext* cx)
91 : {
92 94 : if (!iteratorCache.init()) {
93 0 : ReportOutOfMemory(cx);
94 0 : return false;
95 : }
96 :
97 94 : NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
98 47 : if (!sentinel)
99 : return false;
100 :
101 94 : iteratorSentinel_ = std::move(sentinel);
102 0 : enumerators = iteratorSentinel_.get();
103 0 : return true;
104 : }
105 :
106 : bool
107 0 : Realm::init(JSContext* cx)
108 : {
109 : /*
110 : * As a hack, we clear our timezone cache every time we create a new realm.
111 : * This ensures that the cache is always relatively fresh, but shouldn't
112 : * interfere with benchmarks that create tons of date objects (unless they
113 : * also create tons of iframes, which seems unlikely).
114 : */
115 : JS::ResetTimeZone();
116 :
117 : if (!objects_.init(cx))
118 : return false;
119 :
120 : if (!savedStacks_.init() ||
121 : !varNames_.init())
122 : {
123 : ReportOutOfMemory(cx);
124 : return false;
125 : }
126 :
127 : return true;
128 : }
129 :
130 : jit::JitRuntime*
131 : JSRuntime::createJitRuntime(JSContext* cx)
132 : {
133 : // The shared stubs are created in the atoms zone, which may be
134 : // accessed by other threads with an exclusive context.
135 : AutoLockForExclusiveAccess atomsLock(cx);
136 :
137 : MOZ_ASSERT(!jitRuntime_);
138 :
139 : if (!CanLikelyAllocateMoreExecutableMemory()) {
140 : // Report OOM instead of potentially hitting the MOZ_CRASH below.
141 : ReportOutOfMemory(cx);
142 : return nullptr;
143 : }
144 :
145 : jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>();
146 : if (!jrt)
147 : return nullptr;
148 :
149 : // Unfortunately, initialization depends on jitRuntime_ being non-null, so
150 : // we can't just wait to assign jitRuntime_.
151 : jitRuntime_ = jrt;
152 :
153 : AutoEnterOOMUnsafeRegion noOOM;
154 : if (!jitRuntime_->initialize(cx, atomsLock)) {
155 : // Handling OOM here is complicated: if we delete jitRuntime_ now, we
156 : // will destroy the ExecutableAllocator, even though there may still be
157 : // JitCode instances holding references to ExecutablePools.
158 : noOOM.crash("OOM in createJitRuntime");
159 : }
160 :
161 : return jitRuntime_;
162 : }
163 :
164 : bool
165 : Realm::ensureJitRealmExists(JSContext* cx)
166 : {
167 : using namespace js::jit;
168 : if (jitRealm_)
169 : return true;
170 :
171 : if (!zone()->getJitZone(cx))
172 : return false;
173 :
174 : UniquePtr<JitRealm> jitRealm = cx->make_unique<JitRealm>();
175 : if (!jitRealm)
176 : return false;
177 :
178 : if (!jitRealm->initialize(cx))
179 : return false;
180 :
181 : jitRealm_ = std::move(jitRealm);
182 : return true;
183 : }
184 :
185 : #ifdef JSGC_HASH_TABLE_CHECKS
186 :
187 : void
188 : js::DtoaCache::checkCacheAfterMovingGC()
189 : {
190 : MOZ_ASSERT(!s || !IsForwarded(s));
191 : }
192 :
193 : namespace {
194 : struct CheckGCThingAfterMovingGCFunctor {
195 : template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
196 : };
197 : } // namespace (anonymous)
198 :
199 : #endif // JSGC_HASH_TABLE_CHECKS
200 :
201 : LexicalEnvironmentObject*
202 : ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, HandleObject enclosing)
203 : {
204 : MOZ_ASSERT(&ObjectRealm::get(enclosing) == this);
205 :
206 : if (!nonSyntacticLexicalEnvironments_) {
207 : auto map = cx->make_unique<ObjectWeakMap>(cx);
208 : if (!map || !map->init())
209 : return nullptr;
210 :
211 : nonSyntacticLexicalEnvironments_ = std::move(map);
212 : }
213 :
214 : // If a wrapped WithEnvironmentObject was passed in, unwrap it, as we may
215 : // be creating different WithEnvironmentObject wrappers each time.
216 : RootedObject key(cx, enclosing);
217 : if (enclosing->is<WithEnvironmentObject>()) {
218 : MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
219 : key = &enclosing->as<WithEnvironmentObject>().object();
220 : }
221 : RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));
222 :
223 : if (!lexicalEnv) {
224 : // NOTE: The default global |this| value is set to key for compatibility
225 : // with existing users of the lexical environment cache.
226 : // - When used by shared-global JSM loader, |this| must be the
227 : // NonSyntacticVariablesObject passed as enclosing.
228 : // - When used by SubscriptLoader, |this| must be the target object of
229 : // the WithEnvironmentObject wrapper.
230 : // - When used by XBL/DOM Events, we execute directly as a function and
231 : // do not access the |this| value.
232 : // See js::GetFunctionThis / js::GetNonSyntacticGlobalThis
233 : MOZ_ASSERT(key->is<NonSyntacticVariablesObject>() || !key->is<EnvironmentObject>());
234 : lexicalEnv = LexicalEnvironmentObject::createNonSyntactic(cx, enclosing, /*thisv = */key);
235 : if (!lexicalEnv)
236 : return nullptr;
237 : if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv))
238 : return nullptr;
239 : }
240 :
241 : return &lexicalEnv->as<LexicalEnvironmentObject>();
242 : }
243 :
244 : LexicalEnvironmentObject*
245 : ObjectRealm::getNonSyntacticLexicalEnvironment(JSObject* enclosing) const
246 : {
247 : MOZ_ASSERT(&ObjectRealm::get(enclosing) == this);
248 :
249 : if (!nonSyntacticLexicalEnvironments_)
250 : return nullptr;
251 : // If a wrapped WithEnvironmentObject was passed in, unwrap it as in
252 : // getOrCreateNonSyntacticLexicalEnvironment.
253 : JSObject* key = enclosing;
254 : if (enclosing->is<WithEnvironmentObject>()) {
255 : MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
256 : key = &enclosing->as<WithEnvironmentObject>().object();
257 : }
258 : JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
259 : if (!lexicalEnv)
260 : return nullptr;
261 : return &lexicalEnv->as<LexicalEnvironmentObject>();
262 : }
263 :
264 : bool
265 : Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
266 : {
267 : MOZ_ASSERT(name);
268 :
269 : if (varNames_.put(name))
270 : return true;
271 :
272 : ReportOutOfMemory(cx);
273 : return false;
274 : }
275 :
276 : void
277 : Realm::traceGlobal(JSTracer* trc)
278 : {
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 : savedStacks_.trace(trc);
283 :
284 : // Atoms are always tenured.
285 : if (!JS::RuntimeHeapIsMinorCollecting())
286 : varNames_.trace(trc);
287 : }
288 :
289 : void
290 : ObjectRealm::trace(JSTracer* trc)
291 : {
292 : if (lazyArrayBuffers)
293 : lazyArrayBuffers->trace(trc);
294 :
295 : if (objectMetadataTable)
296 : objectMetadataTable->trace(trc);
297 :
298 : if (nonSyntacticLexicalEnvironments_)
299 : nonSyntacticLexicalEnvironments_->trace(trc);
300 : }
301 :
302 : void
303 : Realm::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
304 : {
305 : if (objectMetadataState_.is<PendingMetadata>()) {
306 : TraceRoot(trc,
307 : &objectMetadataState_.as<PendingMetadata>(),
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 compartment is on-stack, we mark its global so that
316 : // JSContext::global() remains valid.
317 : if (shouldTraceGlobal() && global_.unbarrieredGet())
318 : TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
319 : }
320 :
321 : // Nothing below here needs to be treated as a root if we aren't marking
322 : // this zone for a collection.
323 : if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollectingFromAnyThread())
324 : return;
325 :
326 : /* Mark debug scopes, if present */
327 : if (debugEnvs_)
328 : debugEnvs_->trace(trc);
329 :
330 : objects_.trace(trc);
331 :
332 : // If code coverage is only enabled with the Debugger or the LCovOutput,
333 : // then the following comment holds.
334 : //
335 : // The scriptCountsMap maps JSScript weak-pointers to ScriptCounts
336 : // structures. It uses a HashMap instead of a WeakMap, so that we can keep
337 : // the data alive for the JSScript::finalize call. Thus, we do not trace the
338 : // keys of the HashMap to avoid adding a strong reference to the JSScript
339 : // pointers.
340 : //
341 : // If the code coverage is either enabled with the --dump-bytecode command
342 : // line option, or with the PCCount JSFriend API functions, then we mark the
343 : // keys of the map to hold the JSScript alive.
344 : if (scriptCountsMap &&
345 : trc->runtime()->profilingScripts &&
346 : !JS::RuntimeHeapIsMinorCollecting())
347 : {
348 : MOZ_ASSERT_IF(!trc->runtime()->isBeingDestroyed(), collectCoverage());
349 : for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
350 : JSScript* script = const_cast<JSScript*>(r.front().key());
351 : MOZ_ASSERT(script->hasScriptCounts());
352 : TraceRoot(trc, &script, "profilingScripts");
353 : MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
354 : }
355 : }
356 : }
357 :
358 : void
359 : ObjectRealm::finishRoots()
360 : {
361 : if (lazyArrayBuffers)
362 : lazyArrayBuffers->clear();
363 :
364 : if (objectMetadataTable)
365 : objectMetadataTable->clear();
366 :
367 : if (nonSyntacticLexicalEnvironments_)
368 : nonSyntacticLexicalEnvironments_->clear();
369 : }
370 :
371 : void
372 : Realm::finishRoots()
373 : {
374 : if (debugEnvs_)
375 : debugEnvs_->finish();
376 :
377 : objects_.finishRoots();
378 :
379 : clearScriptCounts();
380 : clearScriptNames();
381 : }
382 :
383 : void
384 : ObjectRealm::sweepAfterMinorGC()
385 : {
386 : InnerViewTable& table = innerViews.get();
387 : if (table.needsSweepAfterMinorGC())
388 : table.sweepAfterMinorGC();
389 : }
390 :
391 : void
392 : Realm::sweepAfterMinorGC()
393 : {
394 : globalWriteBarriered = 0;
395 : dtoaCache.purge();
396 : objects_.sweepAfterMinorGC();
397 : }
398 :
399 : void
400 : Realm::sweepSavedStacks()
401 : {
402 : savedStacks_.sweep();
403 : }
404 :
405 : void
406 : Realm::sweepGlobalObject()
407 : {
408 : if (global_ && IsAboutToBeFinalized(&global_))
409 : global_.set(nullptr);
410 : }
411 :
412 : void
413 : Realm::sweepSelfHostingScriptSource()
414 : {
415 : if (selfHostingScriptSource.unbarrieredGet() &&
416 : IsAboutToBeFinalized(&selfHostingScriptSource))
417 : {
418 : selfHostingScriptSource.set(nullptr);
419 : }
420 : }
421 :
422 : void
423 : Realm::sweepJitRealm()
424 : {
425 : if (jitRealm_)
426 : jitRealm_->sweep(this);
427 : }
428 :
429 : void
430 : Realm::sweepRegExps()
431 : {
432 : /*
433 : * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
434 : * code for the lifetime of the JIT script. Thus, we must perform
435 : * sweeping after clearing jit code.
436 : */
437 : regExps.sweep();
438 : }
439 :
440 : void
441 : Realm::sweepDebugEnvironments()
442 : {
443 : if (debugEnvs_)
444 : debugEnvs_->sweep();
445 : }
446 :
447 : void
448 : ObjectRealm::sweepNativeIterators()
449 : {
450 : /* Sweep list of native iterators. */
451 : NativeIterator* ni = enumerators->next();
452 : while (ni != enumerators) {
453 : JSObject* iterObj = ni->iterObj();
454 : NativeIterator* next = ni->next();
455 : if (gc::IsAboutToBeFinalizedUnbarriered(&iterObj))
456 : ni->unlink();
457 : MOZ_ASSERT_IF(ni->objectBeingIterated(),
458 : &ObjectRealm::get(ni->objectBeingIterated()) == this);
459 : ni = next;
460 : }
461 : }
462 :
463 : void
464 : Realm::sweepObjectRealm()
465 : {
466 : objects_.sweepNativeIterators();
467 : }
468 :
469 : void
470 : Realm::sweepVarNames()
471 : {
472 : varNames_.sweep();
473 : }
474 :
475 : namespace {
476 : struct TraceRootFunctor {
477 : JSTracer* trc;
478 : const char* name;
479 : TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
480 : template <class T> void operator()(T* t) { return TraceRoot(trc, t, name); }
481 : };
482 : struct NeedsSweepUnbarrieredFunctor {
483 : template <class T> bool operator()(T* t) const { return IsAboutToBeFinalizedUnbarriered(t); }
484 : };
485 : } // namespace (anonymous)
486 :
487 : void
488 : Realm::sweepTemplateObjects()
489 : {
490 : if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
491 : mappedArgumentsTemplate_.set(nullptr);
492 :
493 : if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
494 : unmappedArgumentsTemplate_.set(nullptr);
495 :
496 : if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_))
497 : iterResultTemplate_.set(nullptr);
498 : }
499 :
500 : void
501 : Realm::fixupAfterMovingGC()
502 : {
503 : purge();
504 : fixupGlobal();
505 : objectGroups_.fixupTablesAfterMovingGC();
506 : fixupScriptMapsAfterMovingGC();
507 : }
508 :
509 : void
510 : Realm::fixupGlobal()
511 : {
512 : GlobalObject* global = *global_.unsafeGet();
513 : if (global)
514 : global_.set(MaybeForwarded(global));
515 : }
516 :
517 : void
518 : Realm::fixupScriptMapsAfterMovingGC()
519 : {
520 : // Map entries are removed by JSScript::finalize, but we need to update the
521 : // script pointers here in case they are moved by the GC.
522 :
523 : if (scriptCountsMap) {
524 : for (ScriptCountsMap::Enum e(*scriptCountsMap); !e.empty(); e.popFront()) {
525 : JSScript* script = e.front().key();
526 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
527 : e.rekeyFront(script);
528 : }
529 : }
530 :
531 : if (scriptNameMap) {
532 : for (ScriptNameMap::Enum e(*scriptNameMap); !e.empty(); e.popFront()) {
533 : JSScript* script = e.front().key();
534 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
535 : e.rekeyFront(script);
536 : }
537 : }
538 :
539 : if (debugScriptMap) {
540 : for (DebugScriptMap::Enum e(*debugScriptMap); !e.empty(); e.popFront()) {
541 : JSScript* script = e.front().key();
542 : if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
543 : e.rekeyFront(script);
544 : }
545 : }
546 : }
547 :
548 : #ifdef JSGC_HASH_TABLE_CHECKS
549 : void
550 : Realm::checkScriptMapsAfterMovingGC()
551 : {
552 : if (scriptCountsMap) {
553 : for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
554 : JSScript* script = r.front().key();
555 : MOZ_ASSERT(script->realm() == this);
556 : CheckGCThingAfterMovingGC(script);
557 : auto ptr = scriptCountsMap->lookup(script);
558 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
559 : }
560 : }
561 :
562 : if (scriptNameMap) {
563 : for (auto r = scriptNameMap->all(); !r.empty(); r.popFront()) {
564 : JSScript* script = r.front().key();
565 : MOZ_ASSERT(script->realm() == this);
566 : CheckGCThingAfterMovingGC(script);
567 : auto ptr = scriptNameMap->lookup(script);
568 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
569 : }
570 : }
571 :
572 : if (debugScriptMap) {
573 : for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
574 : JSScript* script = r.front().key();
575 : MOZ_ASSERT(script->realm() == this);
576 : CheckGCThingAfterMovingGC(script);
577 : DebugScript* ds = r.front().value().get();
578 : for (uint32_t i = 0; i < ds->numSites; i++) {
579 : BreakpointSite* site = ds->breakpoints[i];
580 : if (site && site->type() == BreakpointSite::Type::JS)
581 : CheckGCThingAfterMovingGC(site->asJS()->script);
582 : }
583 : auto ptr = debugScriptMap->lookup(script);
584 : MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
585 : }
586 : }
587 : }
588 : #endif
589 :
590 : void
591 : Realm::purge()
592 : {
593 : dtoaCache.purge();
594 : newProxyCache.purge();
595 : objectGroups_.purge();
596 : objects_.iteratorCache.clearAndShrink();
597 : arraySpeciesLookup.purge();
598 : }
599 :
600 : void
601 : Realm::clearTables()
602 : {
603 : global_.set(nullptr);
604 :
605 : // No scripts should have run in this realm. This is used when merging
606 : // a realm that has been used off thread into another realm and zone.
607 : compartment()->assertNoCrossCompartmentWrappers();
608 : MOZ_ASSERT(!jitRealm_);
609 : MOZ_ASSERT(!debugEnvs_);
610 : MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
611 :
612 : objectGroups_.clearTables();
613 : if (savedStacks_.initialized())
614 : savedStacks_.clear();
615 : if (varNames_.initialized())
616 : varNames_.clear();
617 : }
618 :
619 : void
620 : Realm::setAllocationMetadataBuilder(const js::AllocationMetadataBuilder* builder)
621 : {
622 : // Clear any jitcode in the runtime, which behaves differently depending on
623 : // whether there is a creation callback.
624 : ReleaseAllJITCode(runtime_->defaultFreeOp());
625 :
626 : allocationMetadataBuilder_ = builder;
627 : }
628 :
629 : void
630 : Realm::forgetAllocationMetadataBuilder()
631 : {
632 : // Unlike setAllocationMetadataBuilder, we don't have to discard all JIT
633 : // code here (code is still valid, just a bit slower because it doesn't do
634 : // inline GC allocations when a metadata builder is present), but we do want
635 : // to cancel off-thread Ion compilations to avoid races when Ion calls
636 : // hasAllocationMetadataBuilder off-thread.
637 : CancelOffThreadIonCompile(this);
638 :
639 : allocationMetadataBuilder_ = nullptr;
640 : }
641 :
642 : void
643 : Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
644 : {
645 : MOZ_ASSERT(obj->realm() == this);
646 : assertSameCompartment(cx, compartment(), obj);
647 :
648 : AutoEnterOOMUnsafeRegion oomUnsafe;
649 : if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
650 : MOZ_ASSERT(metadata->realm() == obj->realm());
651 : assertSameCompartment(cx, metadata);
652 :
653 : if (!objects_.objectMetadataTable) {
654 : auto table = cx->make_unique<ObjectWeakMap>(cx);
655 : if (!table || !table->init())
656 : oomUnsafe.crash("setNewObjectMetadata");
657 :
658 : objects_.objectMetadataTable = std::move(table);
659 : }
660 :
661 : if (!objects_.objectMetadataTable->add(cx, obj, metadata))
662 : oomUnsafe.crash("setNewObjectMetadata");
663 : }
664 : }
665 :
666 : static bool
667 : AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
668 : {
669 : if (!script->hasObjects())
670 : return true;
671 : ObjectArray* objects = script->objects();
672 : for (size_t i = 0; i < objects->length; i++) {
673 : JSObject* obj = objects->vector[i];
674 : if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
675 : if (!lazyFunctions.append(obj))
676 : return false;
677 : }
678 : }
679 : return true;
680 : }
681 :
682 : static bool
683 : AddLazyFunctionsForRealm(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
684 : {
685 : // Find all live root lazy functions in the realm: those which have a
686 : // non-lazy enclosing script, and which do not have an uncompiled enclosing
687 : // script. The last condition is so that we don't compile lazy scripts
688 : // whose enclosing scripts failed to compile, indicating that the lazy
689 : // script did not escape the script.
690 : //
691 : // Some LazyScripts have a non-null |JSScript* script| pointer. We still
692 : // want to delazify in that case: this pointer is weak so the JSScript
693 : // could be destroyed at the next GC.
694 :
695 : for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
696 : JSFunction* fun = &i->as<JSFunction>();
697 :
698 : // Sweeping is incremental; take care to not delazify functions that
699 : // are about to be finalized. GC things referenced by objects that are
700 : // about to be finalized (e.g., in slots) may already be freed.
701 : if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
702 : fun->realm() != cx->realm())
703 : {
704 : continue;
705 : }
706 :
707 : if (fun->isInterpretedLazy()) {
708 : LazyScript* lazy = fun->lazyScriptOrNull();
709 : if (lazy && !lazy->isEnclosingScriptLazy() && !lazy->hasUncompletedEnclosingScript()) {
710 : if (!lazyFunctions.append(fun))
711 : return false;
712 : }
713 : }
714 : }
715 :
716 : return true;
717 : }
718 :
719 : static bool
720 : CreateLazyScriptsForRealm(JSContext* cx)
721 : {
722 : AutoObjectVector lazyFunctions(cx);
723 :
724 : if (!AddLazyFunctionsForRealm(cx, lazyFunctions, AllocKind::FUNCTION))
725 : return false;
726 :
727 : // Methods, for instance {get method() {}}, are extended functions that can
728 : // be relazified, so we need to handle those as well.
729 : if (!AddLazyFunctionsForRealm(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
730 : return false;
731 :
732 : // Create scripts for each lazy function, updating the list of functions to
733 : // process with any newly exposed inner functions in created scripts.
734 : // A function cannot be delazified until its outer script exists.
735 : RootedFunction fun(cx);
736 : for (size_t i = 0; i < lazyFunctions.length(); i++) {
737 : fun = &lazyFunctions[i]->as<JSFunction>();
738 :
739 : // lazyFunctions may have been populated with multiple functions for
740 : // a lazy script.
741 : if (!fun->isInterpretedLazy())
742 : continue;
743 :
744 : bool lazyScriptHadNoScript = !fun->lazyScript()->maybeScript();
745 :
746 : JSScript* script = JSFunction::getOrCreateScript(cx, fun);
747 : if (!script)
748 : return false;
749 : if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
750 : return false;
751 : }
752 :
753 : return true;
754 : }
755 :
756 : bool
757 : Realm::ensureDelazifyScriptsForDebugger(JSContext* cx)
758 : {
759 : AutoRealmUnchecked ar(cx, this);
760 : if (needsDelazificationForDebugger() && !CreateLazyScriptsForRealm(cx))
761 : return false;
762 : debugModeBits_ &= ~DebuggerNeedsDelazification;
763 : return true;
764 : }
765 :
766 : void
767 : Realm::updateDebuggerObservesFlag(unsigned flag)
768 : {
769 : MOZ_ASSERT(isDebuggee());
770 : MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
771 : flag == DebuggerObservesCoverage ||
772 : flag == DebuggerObservesAsmJS ||
773 : flag == DebuggerObservesBinarySource);
774 :
775 : GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
776 : ? unsafeUnbarrieredMaybeGlobal()
777 : : maybeGlobal();
778 : const GlobalObject::DebuggerVector* v = global->getDebuggers();
779 : for (auto p = v->begin(); p != v->end(); p++) {
780 : Debugger* dbg = *p;
781 : if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
782 : flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
783 : flag == DebuggerObservesAsmJS ? dbg->observesAsmJS() :
784 : dbg->observesBinarySource())
785 : {
786 : debugModeBits_ |= flag;
787 : return;
788 : }
789 : }
790 :
791 : debugModeBits_ &= ~flag;
792 : }
793 :
794 : void
795 : Realm::unsetIsDebuggee()
796 : {
797 : if (isDebuggee()) {
798 : debugModeBits_ &= ~DebuggerObservesMask;
799 : DebugEnvironments::onRealmUnsetIsDebuggee(this);
800 : }
801 : }
802 :
803 : void
804 : Realm::updateDebuggerObservesCoverage()
805 : {
806 : bool previousState = debuggerObservesCoverage();
807 : updateDebuggerObservesFlag(DebuggerObservesCoverage);
808 : if (previousState == debuggerObservesCoverage())
809 : return;
810 :
811 : if (debuggerObservesCoverage()) {
812 : // Interrupt any running interpreter frame. The scriptCounts are
813 : // allocated on demand when a script resumes its execution.
814 : JSContext* cx = TlsContext.get();
815 : for (ActivationIterator iter(cx); !iter.done(); ++iter) {
816 : if (iter->isInterpreter())
817 : iter->asInterpreter()->enableInterruptsUnconditionally();
818 : }
819 : return;
820 : }
821 :
822 : // If code coverage is enabled by any other means, keep it.
823 : if (collectCoverage())
824 : return;
825 :
826 : clearScriptCounts();
827 : clearScriptNames();
828 : }
829 :
830 : bool
831 : Realm::collectCoverage() const
832 : {
833 : return collectCoverageForPGO() ||
834 : collectCoverageForDebug();
835 : }
836 :
837 : bool
838 : Realm::collectCoverageForPGO() const
839 : {
840 : return !JitOptions.disablePgo;
841 : }
842 :
843 : bool
844 : Realm::collectCoverageForDebug() const
845 : {
846 : return debuggerObservesCoverage() ||
847 : runtimeFromAnyThread()->profilingScripts ||
848 : runtimeFromAnyThread()->lcovOutput().isEnabled();
849 : }
850 :
851 : void
852 : Realm::clearScriptCounts()
853 : {
854 : if (!scriptCountsMap)
855 : return;
856 :
857 : // Clear all hasScriptCounts_ flags of JSScript, in order to release all
858 : // ScriptCounts entries of the current realm.
859 : for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront())
860 : r.front().key()->clearHasScriptCounts();
861 :
862 : scriptCountsMap.reset();
863 : }
864 :
865 : void
866 : Realm::clearScriptNames()
867 : {
868 : scriptNameMap.reset();
869 : }
870 :
871 : void
872 : Realm::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
873 : {
874 : for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
875 : if (script->realm() == this && script->hasAnyBreakpointsOrStepMode())
876 : script->clearBreakpointsIn(fop, dbg, handler);
877 : }
878 : }
879 :
880 : void
881 : ObjectRealm::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
882 : size_t* innerViewsArg,
883 : size_t* lazyArrayBuffersArg,
884 : size_t* objectMetadataTablesArg,
885 : size_t* nonSyntacticLexicalEnvironmentsArg)
886 : {
887 : *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
888 :
889 : if (lazyArrayBuffers)
890 : *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
891 :
892 : if (objectMetadataTable)
893 : *objectMetadataTablesArg += objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
894 :
895 : if (auto& map = nonSyntacticLexicalEnvironments_)
896 : *nonSyntacticLexicalEnvironmentsArg += map->sizeOfIncludingThis(mallocSizeOf);
897 : }
898 :
899 : void
900 : Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
901 : size_t* tiAllocationSiteTables,
902 : size_t* tiArrayTypeTables,
903 : size_t* tiObjectTypeTables,
904 : size_t* realmObject,
905 : size_t* realmTables,
906 : size_t* innerViewsArg,
907 : size_t* lazyArrayBuffersArg,
908 : size_t* objectMetadataTablesArg,
909 : size_t* savedStacksSet,
910 : size_t* varNamesSet,
911 : size_t* nonSyntacticLexicalEnvironmentsArg,
912 : size_t* jitRealm,
913 : size_t* scriptCountsMapArg)
914 : {
915 : *realmObject += mallocSizeOf(this);
916 : objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
917 : tiArrayTypeTables, tiObjectTypeTables,
918 : realmTables);
919 : wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
920 :
921 : objects_.addSizeOfExcludingThis(mallocSizeOf,
922 : innerViewsArg,
923 : lazyArrayBuffersArg,
924 : objectMetadataTablesArg,
925 : nonSyntacticLexicalEnvironmentsArg);
926 :
927 : *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
928 : *varNamesSet += varNames_.sizeOfExcludingThis(mallocSizeOf);
929 :
930 : if (jitRealm_)
931 : *jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
932 :
933 : if (scriptCountsMap) {
934 : *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
935 : for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront())
936 : *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
937 : }
938 : }
939 :
940 : mozilla::HashCodeScrambler
941 : Realm::randomHashCodeScrambler()
942 : {
943 : return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
944 : randomKeyGenerator_.next());
945 : }
946 :
947 : AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
948 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
949 : : CustomAutoRooter(cx)
950 : , cx_(cx->helperThread() ? nullptr : cx)
951 : , prevState_(cx->realm()->objectMetadataState_)
952 : {
953 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
954 : if (cx_)
955 : cx_->realm()->objectMetadataState_ = NewObjectMetadataState(DelayMetadata());
956 : }
957 :
958 : AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
959 : {
960 : // If we don't have a cx, we didn't change the metadata state, so no need to
961 : // reset it here.
962 : if (!cx_)
963 : return;
964 :
965 : if (!cx_->isExceptionPending() && cx_->realm()->hasObjectPendingMetadata()) {
966 : // This destructor often runs upon exit from a function that is
967 : // returning an unrooted pointer to a Cell. The allocation metadata
968 : // callback often allocates; if it causes a GC, then the Cell pointer
969 : // being returned won't be traced or relocated.
970 : //
971 : // The only extant callbacks are those internal to SpiderMonkey that
972 : // capture the JS stack. In fact, we're considering removing general
973 : // callbacks altogther in bug 1236748. Since it's not running arbitrary
974 : // code, it's adequate to simply suppress GC while we run the callback.
975 : AutoSuppressGC autoSuppressGC(cx_);
976 :
977 : JSObject* obj = cx_->realm()->objectMetadataState_.as<PendingMetadata>();
978 :
979 : // Make sure to restore the previous state before setting the object's
980 : // metadata. SetNewObjectMetadata asserts that the state is not
981 : // PendingMetadata in order to ensure that metadata callbacks are called
982 : // in order.
983 : cx_->realm()->objectMetadataState_ = prevState_;
984 :
985 : obj = SetNewObjectMetadata(cx_, obj);
986 : } else {
987 : cx_->realm()->objectMetadataState_ = prevState_;
988 : }
989 : }
990 :
991 : JS_PUBLIC_API(void)
992 : gc::TraceRealm(JSTracer* trc, JS::Realm* realm, const char* name)
993 : {
994 : // The way GC works with compartments is basically incomprehensible.
995 : // For Realms, what we want is very simple: each Realm has a strong
996 : // reference to its GlobalObject, and vice versa.
997 : //
998 : // Here we simply trace our side of that edge. During GC,
999 : // GCRuntime::traceRuntimeCommon() marks all other realm roots, for
1000 : // all realms.
1001 : realm->traceGlobal(trc);
1002 : }
1003 :
1004 : JS_PUBLIC_API(bool)
1005 : gc::RealmNeedsSweep(JS::Realm* realm)
1006 : {
1007 : return realm->globalIsAboutToBeFinalized();
1008 : }
1009 :
1010 : JS_PUBLIC_API(JS::Realm*)
1011 : JS::GetCurrentRealmOrNull(JSContext* cx)
1012 : {
1013 : return cx->realm();
1014 : }
1015 :
1016 : JS_PUBLIC_API(JS::Realm*)
1017 : JS::GetObjectRealmOrNull(JSObject* obj)
1018 : {
1019 : return IsCrossCompartmentWrapper(obj) ? nullptr : obj->realm();
1020 : }
1021 :
1022 : JS_PUBLIC_API(void*)
1023 : JS::GetRealmPrivate(JS::Realm* realm)
1024 : {
1025 : return realm->realmPrivate();
1026 : }
1027 :
1028 : JS_PUBLIC_API(void)
1029 : JS::SetRealmPrivate(JS::Realm* realm, void* data)
1030 : {
1031 : realm->setRealmPrivate(data);
1032 : }
1033 :
1034 : JS_PUBLIC_API(void)
1035 : JS::SetDestroyRealmCallback(JSContext* cx, JS::DestroyRealmCallback callback)
1036 : {
1037 : cx->runtime()->destroyRealmCallback = callback;
1038 : }
1039 :
1040 : JS_PUBLIC_API(void)
1041 : JS::SetRealmNameCallback(JSContext* cx, JS::RealmNameCallback callback)
1042 : {
1043 : cx->runtime()->realmNameCallback = callback;
1044 : }
1045 :
1046 : JS_PUBLIC_API(JSObject*)
1047 : JS::GetRealmGlobalOrNull(Handle<JS::Realm*> realm)
1048 : {
1049 : return realm->maybeGlobal();
1050 : }
1051 :
1052 : JS_PUBLIC_API(bool)
1053 : JS::InitRealmStandardClasses(JSContext* cx)
1054 : {
1055 : MOZ_ASSERT(!cx->zone()->isAtomsZone());
1056 : AssertHeapIsIdle();
1057 : CHECK_REQUEST(cx);
1058 : return GlobalObject::initStandardClasses(cx, cx->global());
1059 : }
1060 :
1061 : JS_PUBLIC_API(JSObject*)
1062 : JS::GetRealmObjectPrototype(JSContext* cx)
1063 : {
1064 : CHECK_REQUEST(cx);
1065 : return GlobalObject::getOrCreateObjectPrototype(cx, cx->global());
1066 : }
1067 :
1068 : JS_PUBLIC_API(JSObject*)
1069 : JS::GetRealmFunctionPrototype(JSContext* cx)
1070 : {
1071 : CHECK_REQUEST(cx);
1072 : return GlobalObject::getOrCreateFunctionPrototype(cx, cx->global());
1073 : }
1074 :
1075 : JS_PUBLIC_API(JSObject*)
1076 : JS::GetRealmArrayPrototype(JSContext* cx)
1077 : {
1078 : CHECK_REQUEST(cx);
1079 : return GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
1080 : }
1081 :
1082 : JS_PUBLIC_API(JSObject*)
1083 : JS::GetRealmErrorPrototype(JSContext* cx)
1084 : {
1085 : CHECK_REQUEST(cx);
1086 : return GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), JSEXN_ERR);
1087 : }
1088 :
1089 : JS_PUBLIC_API(JSObject*)
1090 : JS::GetRealmIteratorPrototype(JSContext* cx)
1091 : {
1092 : CHECK_REQUEST(cx);
1093 : return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
1094 : }
|