Bug 1461938 part 12 - Move script maps from JSCompartment to JS::Realm. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 22 May 2018 15:02:01 +0200
changeset 419389 ce426cc34287338d854b9d7275789f4064d7aac7
parent 419388 2f16fd31d24677cf5049e91b5e45035faef88245
child 419390 98ad6a9038624b04f643e141745a0d6be2c892cd
push id34037
push userdluca@mozilla.com
push dateWed, 23 May 2018 09:51:55 +0000
treeherdermozilla-central@d36cd8bdbc5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1461938
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1461938 part 12 - Move script maps from JSCompartment to JS::Realm. r=luke
js/src/gc/GC.cpp
js/src/vm/JSCompartment.cpp
js/src/vm/JSCompartment.h
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -8097,37 +8097,37 @@ GCRuntime::mergeCompartments(JSCompartme
     // Merge other info in source's zone into target's zone.
     target->zone()->types.typeLifoAlloc().transferFrom(&source->zone()->types.typeLifoAlloc());
     MOZ_RELEASE_ASSERT(source->zone()->types.sweepTypeLifoAlloc.ref().isEmpty());
 
     // Atoms which are marked in source's zone are now marked in target's zone.
     atomMarking.adoptMarkedAtoms(target->zone(), source->zone());
 
     // Merge script name maps in the target compartment's map.
-    if (rt->lcovOutput().isEnabled() && source->scriptNameMap) {
+    if (rt->lcovOutput().isEnabled() && sourceRealm->scriptNameMap) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
 
-        if (!target->scriptNameMap) {
-            target->scriptNameMap = cx->new_<ScriptNameMap>();
-
-            if (!target->scriptNameMap)
+        if (!targetRealm->scriptNameMap) {
+            targetRealm->scriptNameMap = cx->new_<ScriptNameMap>();
+
+            if (!targetRealm->scriptNameMap)
                 oomUnsafe.crash("Failed to create a script name map.");
 
-            if (!target->scriptNameMap->init())
+            if (!targetRealm->scriptNameMap->init())
                 oomUnsafe.crash("Failed to initialize a script name map.");
         }
 
-        for (ScriptNameMap::Range r = source->scriptNameMap->all(); !r.empty(); r.popFront()) {
+        for (ScriptNameMap::Range r = sourceRealm->scriptNameMap->all(); !r.empty(); r.popFront()) {
             JSScript* key = r.front().key();
             const char* value = r.front().value();
-            if (!target->scriptNameMap->putNew(key, value))
+            if (!targetRealm->scriptNameMap->putNew(key, value))
                 oomUnsafe.crash("Failed to add an entry in the script name map.");
         }
 
-        source->scriptNameMap->clear();
+        sourceRealm->scriptNameMap->clear();
     }
 
     // The source compartment is now completely empty, and is the only
     // compartment in its zone, which is the only zone in its group. Delete
     // compartment, zone and group without waiting for this to be cleaned up by
     // a full GC.
 
     Zone* sourceZone = source->zone();
@@ -8464,23 +8464,24 @@ js::gc::CheckHashTablesAfterMovingGC(JSR
         zone->checkBaseShapeTableAfterMovingGC();
 
         JS::AutoCheckCannotGC nogc;
         for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next()) {
             if (ShapeTable* table = baseShape->maybeTable(nogc))
                 table->checkAfterMovingGC();
         }
     }
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        c->objectGroups.checkTablesAfterMovingGC();
-        c->dtoaCache.checkCacheAfterMovingGC();
-        c->checkWrapperMapAfterMovingGC();
-        c->checkScriptMapsAfterMovingGC();
-        if (c->debugEnvs)
-            c->debugEnvs->checkHashTablesAfterMovingGC();
+
+    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
+        r->objectGroups.checkTablesAfterMovingGC();
+        r->dtoaCache.checkCacheAfterMovingGC();
+        JS::GetCompartmentForRealm(r)->checkWrapperMapAfterMovingGC();
+        r->checkScriptMapsAfterMovingGC();
+        if (r->debugEnvs)
+            r->debugEnvs->checkHashTablesAfterMovingGC();
     }
 }
 #endif
 
 JS_PUBLIC_API(void)
 JS::PrepareZoneForGC(Zone* zone)
 {
     zone->scheduleGC();
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -64,19 +64,16 @@ JSCompartment::JSCompartment(Zone* zone)
     objectMetadataTable(nullptr),
     innerViews(zone),
     lazyArrayBuffers(nullptr),
     nonSyntacticLexicalEnvironments_(nullptr),
     gcIncomingGrayPointers(nullptr),
     debugModeBits(0),
     validAccessPtr(nullptr),
     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
-    scriptCountsMap(nullptr),
-    scriptNameMap(nullptr),
-    debugScriptMap(nullptr),
     debugEnvs(nullptr),
     enumerators(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
     jitCompartment_(nullptr),
     mappedArgumentsTemplate_(nullptr),
     unmappedArgumentsTemplate_(nullptr),
     iterResultTemplate_(nullptr),
@@ -99,35 +96,39 @@ JS::Realm::Realm(JS::Zone* zone, const J
 JSCompartment::~JSCompartment()
 {
     // Write the code coverage information in a file.
     JSRuntime* rt = runtimeFromMainThread();
     if (rt->lcovOutput().isEnabled())
         rt->lcovOutput().writeLCovResult(lcovOutput);
 
     js_delete(jitCompartment_);
-    js_delete(scriptCountsMap);
-    js_delete(scriptNameMap);
-    js_delete(debugScriptMap);
     js_delete(debugEnvs);
     js_delete(objectMetadataTable);
     js_delete(lazyArrayBuffers);
     js_delete(nonSyntacticLexicalEnvironments_);
     js_free(enumerators);
 
 #ifdef DEBUG
     // Avoid assertion destroying the unboxed layouts list if the embedding
     // leaked GC things.
     if (!rt->gc.shutdownCollectedEverything())
         unboxedLayouts.clear();
 #endif
 
     runtime_->numCompartments--;
 }
 
+Realm::~Realm()
+{
+    js_delete(scriptCountsMap);
+    js_delete(scriptNameMap);
+    js_delete(debugScriptMap);
+}
+
 bool
 JSCompartment::init(JSContext* maybecx)
 {
     if (!crossCompartmentWrappers.init(0)) {
         if (maybecx)
             ReportOutOfMemory(maybecx);
         return false;
     }
@@ -899,33 +900,33 @@ JSCompartment::fixupAfterMovingGC()
 {
     MOZ_ASSERT(zone()->isGCCompacting());
 
     Realm* realm = JS::GetRealmForCompartment(this);
 
     purge();
     realm->fixupGlobal();
     objectGroups.fixupTablesAfterMovingGC();
-    fixupScriptMapsAfterMovingGC();
+    realm->fixupScriptMapsAfterMovingGC();
 
     // Sweep the wrapper map to update values (wrapper objects) in this
     // compartment that may have been moved.
     sweepCrossCompartmentWrappers();
 }
 
 void
 Realm::fixupGlobal()
 {
     GlobalObject* global = *global_.unsafeGet();
     if (global)
         global_.set(MaybeForwarded(global));
 }
 
 void
-JSCompartment::fixupScriptMapsAfterMovingGC()
+Realm::fixupScriptMapsAfterMovingGC()
 {
     // Map entries are removed by JSScript::finalize, but we need to update the
     // script pointers here in case they are moved by the GC.
 
     if (scriptCountsMap) {
         for (ScriptCountsMap::Enum e(*scriptCountsMap); !e.empty(); e.popFront()) {
             JSScript* script = e.front().key();
             if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
@@ -947,39 +948,42 @@ JSCompartment::fixupScriptMapsAfterMovin
             if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
                 e.rekeyFront(script);
         }
     }
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
-JSCompartment::checkScriptMapsAfterMovingGC()
+Realm::checkScriptMapsAfterMovingGC()
 {
     if (scriptCountsMap) {
         for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
             JSScript* script = r.front().key();
+            MOZ_ASSERT(script->realm() == this);
             CheckGCThingAfterMovingGC(script);
             auto ptr = scriptCountsMap->lookup(script);
             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
         }
     }
 
     if (scriptNameMap) {
         for (auto r = scriptNameMap->all(); !r.empty(); r.popFront()) {
             JSScript* script = r.front().key();
+            MOZ_ASSERT(script->realm() == this);
             CheckGCThingAfterMovingGC(script);
             auto ptr = scriptNameMap->lookup(script);
             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
         }
     }
 
     if (debugScriptMap) {
         for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
             JSScript* script = r.front().key();
+            MOZ_ASSERT(script->realm() == this);
             CheckGCThingAfterMovingGC(script);
             DebugScript* ds = r.front().value();
             for (uint32_t i = 0; i < ds->numSites; i++) {
                 BreakpointSite* site = ds->breakpoints[i];
                 if (site && site->type() == BreakpointSite::Type::JS)
                     CheckGCThingAfterMovingGC(site->asJS()->script);
             }
             auto ptr = debugScriptMap->lookup(script);
@@ -1222,18 +1226,19 @@ JSCompartment::updateDebuggerObservesCov
         }
         return;
     }
 
     // If code coverage is enabled by any other means, keep it.
     if (collectCoverage())
         return;
 
-    clearScriptCounts();
-    clearScriptNames();
+    Realm* realm = JS::GetRealmForCompartment(this);
+    realm->clearScriptCounts();
+    realm->clearScriptNames();
 }
 
 bool
 JSCompartment::collectCoverage() const
 {
     return collectCoverageForPGO() ||
            collectCoverageForDebug();
 }
@@ -1248,35 +1253,35 @@ bool
 JSCompartment::collectCoverageForDebug() const
 {
     return debuggerObservesCoverage() ||
            runtimeFromAnyThread()->profilingScripts ||
            runtimeFromAnyThread()->lcovOutput().isEnabled();
 }
 
 void
-JSCompartment::clearScriptCounts()
+Realm::clearScriptCounts()
 {
     if (!scriptCountsMap)
         return;
 
     // Clear all hasScriptCounts_ flags of JSScript, in order to release all
-    // ScriptCounts entry of the current compartment.
+    // ScriptCounts entries of the current realm.
     for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
         ScriptCounts* value = r.front().value();
         r.front().key()->takeOverScriptCountsMapEntry(value);
         js_delete(value);
     }
 
     js_delete(scriptCountsMap);
     scriptCountsMap = nullptr;
 }
 
 void
-JSCompartment::clearScriptNames()
+Realm::clearScriptNames()
 {
     if (!scriptNameMap)
         return;
 
     for (ScriptNameMap::Range r = scriptNameMap->all(); !r.empty(); r.popFront())
         js_delete(r.front().value());
 
     js_delete(scriptNameMap);
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -701,17 +701,16 @@ struct JSCompartment
                                 size_t* crossCompartmentWrappersArg);
 
   public:
     // Object group tables and other state in the compartment.
     js::ObjectGroupCompartment   objectGroups;
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkWrapperMapAfterMovingGC();
-    void checkScriptMapsAfterMovingGC();
 #endif
 
     /*
      * Lazily initialized script source object to use for scripts cloned
      * from the self-hosting global.
      */
     js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
 
@@ -851,17 +850,16 @@ struct JSCompartment
     void sweepDebugEnvironments();
     void sweepNativeIterators();
     void sweepTemplateObjects();
 
     void purge();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupAfterMovingGC();
-    void fixupScriptMapsAfterMovingGC();
 
     js::SavedStacks& savedStacks() { return savedStacks_; }
 
     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 
     js::DtoaCache dtoaCache;
     js::NewProxyCache newProxyCache;
 
@@ -971,18 +969,16 @@ struct JSCompartment
     }
     void updateDebuggerObservesCoverage();
 
     // The code coverage can be enabled either for each compartment, with the
     // Debugger API, or for the entire runtime.
     bool collectCoverage() const;
     bool collectCoverageForDebug() const;
     bool collectCoverageForPGO() const;
-    void clearScriptCounts();
-    void clearScriptNames();
 
     bool needsDelazificationForDebugger() const {
         return debugModeBits & DebuggerNeedsDelazification;
     }
 
     /*
      * Schedule the compartment to be delazified. Called from
      * LazyScript::Create.
@@ -996,21 +992,16 @@ struct JSCompartment
     bool ensureDelazifyScriptsForDebugger(JSContext* cx);
 
     void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
 
   private:
     void sweepBreakpoints(js::FreeOp* fop);
 
   public:
-    js::ScriptCountsMap* scriptCountsMap;
-    js::ScriptNameMap* scriptNameMap;
-
-    js::DebugScriptMap* debugScriptMap;
-
     /* Bookkeeping information for debug scope objects. */
     js::DebugEnvironments* debugEnvs;
 
     /*
      * List of potentially active iterators that may need deleted property
      * suppression.
      */
     js::NativeIterator* enumerators;
@@ -1075,17 +1066,22 @@ class JS::Realm : public JSCompartment
     unsigned enterRealmDepth_ = 0;
 
     bool isAtomsRealm_ = false;
 
   public:
     // WebAssembly state for the realm.
     js::wasm::Realm wasm;
 
+    js::ScriptCountsMap* scriptCountsMap = nullptr;
+    js::ScriptNameMap* scriptNameMap = nullptr;
+    js::DebugScriptMap* debugScriptMap = nullptr;
+
     Realm(JS::Zone* zone, const JS::RealmOptions& options);
+    ~Realm();
 
     MOZ_MUST_USE bool init(JSContext* maybecx);
     void destroy(js::FreeOp* fop);
     void clearTables();
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* tiAllocationSiteTables,
                                 size_t* tiArrayTypeTables,
@@ -1150,16 +1146,25 @@ class JS::Realm : public JSCompartment
      * regardless of whether the realm's global is still live.
      */
     void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
     /*
      * This method clears out tables of roots in preparation for the final GC.
      */
     void finishRoots();
 
+    void clearScriptCounts();
+    void clearScriptNames();
+
+    void fixupScriptMapsAfterMovingGC();
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+    void checkScriptMapsAfterMovingGC();
+#endif
+
     // Add a name to [[VarNames]].  Reports OOM on failure.
     MOZ_MUST_USE bool addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name);
     void sweepVarNames();
 
     void removeFromVarNames(JS::Handle<JSAtom*> name) {
         varNames_.remove(name);
     }
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -1046,32 +1046,32 @@ JSScript::initScriptCounts(JSContext* cx
     if (!base.reserve(jumpTargets.length())) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     for (size_t i = 0; i < jumpTargets.length(); i++)
         base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
 
-    // Create compartment's scriptCountsMap if necessary.
-    ScriptCountsMap* map = compartment()->scriptCountsMap;
+    // Create realm's scriptCountsMap if necessary.
+    ScriptCountsMap* map = realm()->scriptCountsMap;
     if (!map) {
         map = cx->new_<ScriptCountsMap>();
         if (!map) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         if (!map->init()) {
             js_delete(map);
             ReportOutOfMemory(cx);
             return false;
         }
 
-        compartment()->scriptCountsMap = map;
+        realm()->scriptCountsMap = map;
     }
 
     // Allocate the ScriptCounts.
     ScriptCounts* sc = cx->new_<ScriptCounts>(Move(base));
     if (!sc) {
         ReportOutOfMemory(cx);
         return false;
     }
@@ -1094,29 +1094,30 @@ JSScript::initScriptCounts(JSContext* cx
     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
 }
 
-static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
+static inline ScriptCountsMap::Ptr
+GetScriptCountsMapEntry(JSScript* script)
 {
     MOZ_ASSERT(script->hasScriptCounts());
-    ScriptCountsMap* map = script->compartment()->scriptCountsMap;
+    ScriptCountsMap* map = script->realm()->scriptCountsMap;
     ScriptCountsMap::Ptr p = map->lookup(script);
     MOZ_ASSERT(p);
     return p;
 }
 
 static inline ScriptNameMap::Ptr
 GetScriptNameMapEntry(JSScript* script)
 {
-    ScriptNameMap* map = script->compartment()->scriptNameMap;
+    ScriptNameMap* map = script->realm()->scriptNameMap;
     auto p = map->lookup(script);
     MOZ_ASSERT(p);
     return p;
 }
 
 ScriptCounts&
 JSScript::getScriptCounts()
 {
@@ -1302,17 +1303,17 @@ JSScript::takeOverScriptCountsMapEntry(S
 }
 
 void
 JSScript::releaseScriptCounts(ScriptCounts* counts)
 {
     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
     *counts = Move(*p->value());
     js_delete(p->value());
-    compartment()->scriptCountsMap->remove(p);
+    realm()->scriptCountsMap->remove(p);
     bitFields_.hasScriptCounts_ = false;
 }
 
 void
 JSScript::destroyScriptCounts()
 {
     if (hasScriptCounts()) {
         ScriptCounts scriptCounts;
@@ -1320,26 +1321,26 @@ JSScript::destroyScriptCounts()
     }
 }
 
 void
 JSScript::destroyScriptName()
 {
     auto p = GetScriptNameMapEntry(this);
     js_delete(p->value());
-    compartment()->scriptNameMap->remove(p);
+    realm()->scriptNameMap->remove(p);
 }
 
 bool
 JSScript::hasScriptName()
 {
-    if (!compartment()->scriptNameMap)
+    if (!realm()->scriptNameMap)
         return false;
 
-    auto p = compartment()->scriptNameMap->lookup(this);
+    auto p = realm()->scriptNameMap->lookup(this);
     return p.found();
 }
 
 void
 ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
     ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
@@ -2703,32 +2704,32 @@ JSScript::Create(JSContext* cx, const Re
 bool
 JSScript::initScriptName(JSContext* cx)
 {
     MOZ_ASSERT(!hasScriptName());
 
     if (!filename())
         return true;
 
-    // Create compartment's scriptNameMap if necessary.
-    ScriptNameMap* map = compartment()->scriptNameMap;
+    // Create realm's scriptNameMap if necessary.
+    ScriptNameMap* map = realm()->scriptNameMap;
     if (!map) {
         map = cx->new_<ScriptNameMap>();
         if (!map) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         if (!map->init()) {
             js_delete(map);
             ReportOutOfMemory(cx);
             return false;
         }
 
-        compartment()->scriptNameMap = map;
+        realm()->scriptNameMap = map;
     }
 
     char* name = js_strdup(filename());
     if (!name) {
         ReportOutOfMemory(cx);
         return false;
     }
 
@@ -3723,28 +3724,28 @@ js::CloneScriptIntoFunction(JSContext* c
 
     return dst;
 }
 
 DebugScript*
 JSScript::debugScript()
 {
     MOZ_ASSERT(bitFields_.hasDebugScript_);
-    DebugScriptMap* map = compartment()->debugScriptMap;
+    DebugScriptMap* map = realm()->debugScriptMap;
     MOZ_ASSERT(map);
     DebugScriptMap::Ptr p = map->lookup(this);
     MOZ_ASSERT(p);
     return p->value();
 }
 
 DebugScript*
 JSScript::releaseDebugScript()
 {
     MOZ_ASSERT(bitFields_.hasDebugScript_);
-    DebugScriptMap* map = compartment()->debugScriptMap;
+    DebugScriptMap* map = realm()->debugScriptMap;
     MOZ_ASSERT(map);
     DebugScriptMap::Ptr p = map->lookup(this);
     MOZ_ASSERT(p);
     DebugScript* debug = p->value();
     map->remove(p);
     bitFields_.hasDebugScript_ = false;
     return debug;
 }
@@ -3772,26 +3773,26 @@ JSScript::ensureHasDebugScript(JSContext
     if (bitFields_.hasDebugScript_)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
     DebugScript* debug = (DebugScript*) zone()->pod_calloc<uint8_t>(nbytes);
     if (!debug)
         return false;
 
-    /* Create compartment's debugScriptMap if necessary. */
-    DebugScriptMap* map = compartment()->debugScriptMap;
+    /* Create realm's debugScriptMap if necessary. */
+    DebugScriptMap* map = realm()->debugScriptMap;
     if (!map) {
         map = cx->new_<DebugScriptMap>();
         if (!map || !map->init()) {
             js_free(debug);
             js_delete(map);
             return false;
         }
-        compartment()->debugScriptMap = map;
+        realm()->debugScriptMap = map;
     }
 
     if (!map->putNew(this, debug)) {
         js_free(debug);
         return false;
     }
     bitFields_.hasDebugScript_ = true; // safe to set this;  we can't fail after this point
 
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -245,17 +245,17 @@ typedef HashMap<JSScript*,
 typedef HashMap<JSScript*,
                 const char*,
                 DefaultHasher<JSScript*>,
                 SystemAllocPolicy> ScriptNameMap;
 
 class DebugScript
 {
     friend class ::JSScript;
-    friend struct ::JSCompartment;
+    friend class JS::Realm;
 
     /*
      * When non-zero, compile script in single-step mode. The top bit is set and
      * cleared by setStepMode, as used by JSD. The lower bits are a count,
      * adjusted by changeStepModeCount, used by the Debugger object. Only
      * when the bit is clear and the count is zero may we compile the script
      * without single-step support.
      */