LCOV - code coverage report
Current view: top level - js/src/vm - Realm.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 18 36 50.0 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13-14-ga5dd952