Bug 1363214 - Remove JS::Realm/JSCompartment inheritance. r=jonco
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 06 Jun 2018 14:30:52 +0200
changeset 475811 011f238cc9ab82efbaba621046514cd689bd1f86
parent 475810 21861ee0ddb89d1d3b575ccfeaa1c5043374eeab
child 475812 c7989875fbb68523aa21bda5dd464f21abe6985f
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1363214
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 1363214 - Remove JS::Realm/JSCompartment inheritance. r=jonco
js/public/MemoryMetrics.h
js/public/Realm.h
js/public/RootingAPI.h
js/rust/build.rs
js/rust/src/rust.rs
js/src/gc/GC.cpp
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/proxy/Wrapper.cpp
js/src/vm/JSCompartment.cpp
js/src/vm/JSCompartment.h
js/src/vm/JSContext.h
js/src/vm/MemoryMetrics.cpp
js/src/vm/TypeInference.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -706,17 +706,20 @@ struct ZoneStats
     macro(Other,   GCHeapUsed,  regExpSharedsGCHeap) \
     macro(Other,   MallocHeap,  regExpSharedsMallocHeap) \
     macro(Other,   MallocHeap,  typePool) \
     macro(Other,   MallocHeap,  regexpZone) \
     macro(Other,   MallocHeap,  jitZone) \
     macro(Other,   MallocHeap,  baselineStubsOptimized) \
     macro(Other,   MallocHeap,  cachedCFG) \
     macro(Other,   MallocHeap,  uniqueIdMap) \
-    macro(Other,   MallocHeap,  shapeTables)
+    macro(Other,   MallocHeap,  shapeTables) \
+    macro(Other,   MallocHeap,  compartmentObjects) \
+    macro(Other,   MallocHeap,  crossCompartmentWrappersTables) \
+    macro(Other,   MallocHeap,  compartmentsPrivateData)
 
     ZoneStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         unusedGCThings(),
         stringInfo(),
         shapeInfo(),
         extra(),
         allStrings(nullptr),
@@ -822,22 +825,20 @@ struct RealmStats
     macro(Other,   MallocHeap, typeInferenceAllocationSiteTables) \
     macro(Other,   MallocHeap, typeInferenceArrayTypeTables) \
     macro(Other,   MallocHeap, typeInferenceObjectTypeTables) \
     macro(Other,   MallocHeap, realmObject) \
     macro(Other,   MallocHeap, realmTables) \
     macro(Other,   MallocHeap, innerViewsTable) \
     macro(Other,   MallocHeap, lazyArrayBuffersTable) \
     macro(Other,   MallocHeap, objectMetadataTable) \
-    macro(Other,   MallocHeap, crossCompartmentWrappersTable) \
     macro(Other,   MallocHeap, savedStacksSet) \
     macro(Other,   MallocHeap, varNamesSet) \
     macro(Other,   MallocHeap, nonSyntacticLexicalScopesTable) \
     macro(Other,   MallocHeap, jitRealm) \
-    macro(Other,   MallocHeap, privateData) \
     macro(Other,   MallocHeap, scriptCountsMap)
 
     RealmStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         classInfo(),
         extra(),
         allClasses(nullptr),
         notableClasses(),
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -39,31 +39,43 @@ struct GCPolicy<Realm*> : public NonGCPo
     }
 };
 
 // Get the current realm, if any. The ECMAScript spec calls this "the current
 // Realm Record".
 extern JS_PUBLIC_API(Realm*)
 GetCurrentRealmOrNull(JSContext* cx);
 
+namespace shadow {
+
+class Realm
+{
+  protected:
+    JSCompartment* compartment_;
+
+    explicit Realm(JSCompartment* comp)
+      : compartment_(comp)
+    {}
+
+  public:
+    JSCompartment* compartment() {
+        return compartment_;
+    }
+    static shadow::Realm* get(JS::Realm* realm) {
+        return reinterpret_cast<shadow::Realm*>(realm);
+    }
+};
+
+}; // namespace shadow
+
 // Return the compartment that contains a given realm.
 inline JSCompartment*
-GetCompartmentForRealm(Realm* realm) {
-    // Implementation note: For now, realms are a fiction; we treat realms and
-    // compartments as being one-to-one, but they are actually identical.
-    return reinterpret_cast<JSCompartment*>(realm);
-}
-
-// Return the realm in a given compartment.
-//
-// Deprecated. There is currently exactly one realm per compartment, but this
-// will change.
-inline Realm*
-GetRealmForCompartment(JSCompartment* compartment) {
-    return reinterpret_cast<Realm*>(compartment);
+GetCompartmentForRealm(Realm* realm)
+{
+    return shadow::Realm::get(realm)->compartment();
 }
 
 // Return an object's realm. All objects except cross-compartment wrappers are
 // created in a particular realm, which never changes. Returns null if obj is
 // a cross-compartment wrapper.
 extern JS_PUBLIC_API(Realm*)
 GetObjectRealmOrNull(JSObject* obj);
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -1058,17 +1058,19 @@ inline JS::Realm*
 GetContextRealm(const JSContext* cx)
 {
     return JS::RootingContext::get(cx)->realm_;
 }
 
 inline JSCompartment*
 GetContextCompartment(const JSContext* cx)
 {
-    return GetCompartmentForRealm(GetContextRealm(cx));
+    if (JS::Realm* realm = GetContextRealm(cx))
+        return GetCompartmentForRealm(realm);
+    return nullptr;
 }
 
 inline JS::Zone*
 GetContextZone(const JSContext* cx)
 {
     return JS::RootingContext::get(cx)->zone_;
 }
 
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -231,16 +231,17 @@ const WHITELIST_TYPES: &'static [&'stati
     "js::StackFormat",
     "JSStructuredCloneCallbacks",
     "JS::Symbol",
     "JS::SymbolCode",
     "JS::TraceKind",
     "JS::TransferableOwnership",
     "JS::Value",
     "JS::WarningReporter",
+    "JS::shadow::Realm",
     "JS::shadow::Zone",
     "JS::Zone",
 ];
 
 /// Global variables we want to generate bindings to.
 const WHITELIST_VARS: &'static [&'static str] = &[
     "JS_STRUCTURED_CLONE_VERSION",
     "JSCLASS_.*",
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -1050,17 +1050,18 @@ unsafe fn get_object_group(obj: *mut JSO
 
 #[inline]
 pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
     (*get_object_group(obj)).clasp as *const _
 }
 
 #[inline]
 pub unsafe fn get_object_compartment(obj: *mut JSObject) -> *mut JSCompartment {
-    (*get_object_group(obj)).realm as *mut JSCompartment
+    let realm = (*get_object_group(obj)).realm as *const JS::shadow::Realm;
+    (*realm).compartment_
 }
 
 #[inline]
 pub fn is_dom_class(class: &JSClass) -> bool {
     class.flags & JSCLASS_IS_DOMJSCLASS != 0
 }
 
 #[inline]
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1333,18 +1333,22 @@ GCRuntime::finish()
     /* Free memory associated with GC verification. */
     finishVerifier();
 #endif
 
     /* Delete all remaining zones. */
     if (rt->gcInitialized) {
         AutoSetThreadIsSweeping threadIsSweeping;
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
-            for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
-                js_delete(JS::GetRealmForCompartment(comp.get()));
+            for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
+                for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next())
+                    js_delete(realm.get());
+                comp->realms().clear();
+                js_delete(comp.get());
+            }
             zone->compartments().clear();
             js_delete(zone.get());
         }
     }
 
     zones().clear();
 
     FreeChunkPool(fullChunks_.ref());
@@ -3815,20 +3819,27 @@ JS::Zone::sweepUniqueIds()
 }
 
 void
 Realm::destroy(FreeOp* fop)
 {
     JSRuntime* rt = fop->runtime();
     if (auto callback = rt->destroyRealmCallback)
         callback(fop, this);
+    if (principals())
+        JS_DropPrincipals(rt->mainContextFromOwnThread(), principals());
+    fop->delete_(this);
+}
+
+void
+JSCompartment::destroy(FreeOp* fop)
+{
+    JSRuntime* rt = fop->runtime();
     if (auto callback = rt->destroyCompartmentCallback)
         callback(fop, this);
-    if (principals())
-        JS_DropPrincipals(rt->mainContextFromOwnThread(), principals());
     fop->delete_(this);
     rt->gc.stats().sweptCompartment();
 }
 
 void
 Zone::destroy(FreeOp* fop)
 {
     MOZ_ASSERT(compartments().empty());
@@ -3851,32 +3862,63 @@ Zone::sweepCompartments(FreeOp* fop, boo
     MOZ_ASSERT(!compartments().empty());
     MOZ_ASSERT_IF(destroyingRuntime, !keepAtleastOne);
 
     JSCompartment** read = compartments().begin();
     JSCompartment** end = compartments().end();
     JSCompartment** write = read;
     while (read < end) {
         JSCompartment* comp = *read++;
-        Realm* realm = JS::GetRealmForCompartment(comp);
 
         /*
          * Don't delete the last compartment and realm if keepAtleastOne is
          * still true, meaning all the other compartments were deleted.
          */
-        bool dontDelete = read == end && keepAtleastOne;
-        if ((realm->marked() || dontDelete) && !destroyingRuntime) {
+        bool keepAtleastOneRealm = read == end && keepAtleastOne;
+        comp->sweepRealms(fop, keepAtleastOneRealm, destroyingRuntime);
+
+        if (!comp->realms().empty()) {
             *write++ = comp;
             keepAtleastOne = false;
         } else {
-            realm->destroy(fop);
+            comp->destroy(fop);
         }
     }
     compartments().shrinkTo(write - compartments().begin());
     MOZ_ASSERT_IF(keepAtleastOne, !compartments().empty());
+    MOZ_ASSERT_IF(destroyingRuntime, compartments().empty());
+}
+
+void
+JSCompartment::sweepRealms(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
+{
+    MOZ_ASSERT(!realms().empty());
+    MOZ_ASSERT_IF(destroyingRuntime, !keepAtleastOne);
+
+    Realm** read = realms().begin();
+    Realm** end = realms().end();
+    Realm** write = read;
+    while (read < end) {
+        Realm* realm = *read++;
+
+        /*
+         * Don't delete the last realm if keepAtleastOne is still true, meaning
+         * all the other realms were deleted.
+         */
+        bool dontDelete = read == end && keepAtleastOne;
+        if ((realm->marked() || dontDelete) && !destroyingRuntime) {
+            *write++ = realm;
+            keepAtleastOne = false;
+        } else {
+            realm->destroy(fop);
+        }
+    }
+    realms().shrinkTo(write - realms().begin());
+    MOZ_ASSERT_IF(keepAtleastOne, !realms().empty());
+    MOZ_ASSERT_IF(destroyingRuntime, realms().empty());
 }
 
 void
 GCRuntime::deleteEmptyZone(Zone* zone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     MOZ_ASSERT(zone->compartments().empty());
     for (auto& i : zones()) {
@@ -7947,16 +7989,17 @@ AutoPrepareForTracing::AutoPrepareForTra
 
 Realm*
 js::NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& options)
 {
     JSRuntime* rt = cx->runtime();
     JS_AbortIfWrongThread(cx);
 
     UniquePtr<Zone> zoneHolder;
+    UniquePtr<JSCompartment> compHolder;
 
     Zone* zone = nullptr;
     JS::ZoneSpecifier zoneSpec = options.creationOptions().zoneSpecifier();
     switch (zoneSpec) {
       case JS::SystemZone:
         // systemZone might be null here, in which case we'll make a zone and
         // set this field below.
         zone = rt->gc.systemZone;
@@ -7979,24 +8022,28 @@ js::NewRealm(JSContext* cx, JSPrincipals
         if (!zoneHolder->init(isSystem)) {
             ReportOutOfMemory(cx);
             return nullptr;
         }
 
         zone = zoneHolder.get();
     }
 
-    UniquePtr<Realm> realm = cx->make_unique<Realm>(zone, options);
+    compHolder = cx->make_unique<JSCompartment>(zone);
+    if (!compHolder || !compHolder->init(cx))
+        return nullptr;
+
+    JSCompartment* comp = compHolder.get();
+    UniquePtr<Realm> realm(cx->new_<Realm>(comp, options));
     if (!realm || !realm->init(cx))
         return nullptr;
 
     // Set up the principals.
     JS::SetRealmPrincipals(realm.get(), principals);
 
-    JSCompartment* comp = realm->compartment();
     if (!comp->realms().append(realm.get())) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     AutoLockGC lock(rt);
 
     if (!zone->compartments().append(comp)) {
@@ -8013,16 +8060,17 @@ js::NewRealm(JSContext* cx, JSPrincipals
         // Lazily set the runtime's sytem zone.
         if (zoneSpec == JS::SystemZone) {
             MOZ_RELEASE_ASSERT(!rt->gc.systemZone);
             rt->gc.systemZone = zone;
             zone->isSystem = true;
         }
     }
 
+    mozilla::Unused << compHolder.release();
     mozilla::Unused << zoneHolder.release();
     return realm.release();
 }
 
 void
 gc::MergeRealms(Realm* source, Realm* target)
 {
     JSRuntime* rt = source->runtimeFromMainThread();
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -397,17 +397,19 @@ Zone::deleteEmptyCompartment(JSCompartme
     MOZ_ASSERT(comp->zone() == this);
     MOZ_ASSERT(arenas.checkEmptyArenaLists());
 
     MOZ_ASSERT(compartments().length() == 1);
     MOZ_ASSERT(compartments()[0] == comp);
     MOZ_ASSERT(comp->realms().length() == 1);
 
     Realm* realm = comp->realms()[0];
-    realm->destroy(runtimeFromMainThread()->defaultFreeOp());
+    FreeOp* fop = runtimeFromMainThread()->defaultFreeOp();
+    realm->destroy(fop);
+    comp->destroy(fop);
 
     compartments().clear();
 }
 
 void
 Zone::setHelperThreadOwnerContext(JSContext* cx)
 {
     MOZ_ASSERT_IF(cx, TlsContext.get() == cx);
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -196,17 +196,20 @@ struct Zone : public JS::shadow::Zone,
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* typePool,
                                 size_t* regexpZone,
                                 size_t* jitZone,
                                 size_t* baselineStubsOptimized,
                                 size_t* cachedCFG,
                                 size_t* uniqueIdMap,
                                 size_t* shapeTables,
-                                size_t* atomsMarkBitmaps);
+                                size_t* atomsMarkBitmaps,
+                                size_t* compartmentObjects,
+                                size_t* crossCompartmentWrappersTables,
+                                size_t* compartmentsPrivateData);
 
     // Iterate over all cells in the zone. See the definition of ZoneCellIter
     // in gc/GC-inl.h for the possible arguments and documentation.
     template <typename T, typename... Args>
     js::gc::ZoneCellIter<T> cellIter(Args&&... args) {
         return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), std::forward<Args>(args)...);
     }
 
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -442,17 +442,17 @@ js::TransparentObjectWrapper(JSContext* 
 }
 
 ErrorCopier::~ErrorCopier()
 {
     JSContext* cx = ar->context();
 
     // The provenance of Debugger.DebuggeeWouldRun is the topmost locking
     // debugger compartment; it should not be copied around.
-    if (JS::GetCompartmentForRealm(ar->origin()) != cx->compartment() &&
+    if (ar->origin()->compartment() != cx->compartment() &&
         cx->isExceptionPending() &&
         !cx->isThrowingDebuggeeWouldRun())
     {
         RootedValue exc(cx);
         if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
             cx->clearPendingException();
             ar.reset();
             Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -50,24 +50,26 @@ ObjectRealm::ObjectRealm(JS::Zone* zone)
   : innerViews(zone)
 {}
 
 ObjectRealm::~ObjectRealm()
 {
     MOZ_ASSERT(enumerators == iteratorSentinel_.get());
 }
 
-Realm::Realm(JS::Zone* zone, const JS::RealmOptions& options)
-  : JSCompartment(zone),
+Realm::Realm(JSCompartment* comp, const JS::RealmOptions& options)
+  : JS::shadow::Realm(comp),
+    zone_(comp->zone()),
+    runtime_(comp->runtimeFromMainThread()),
     creationOptions_(options.creationOptions()),
     behaviors_(options.behaviors()),
     global_(nullptr),
-    objects_(zone),
+    objects_(zone_),
     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
-    wasm(zone->runtimeFromMainThread()),
+    wasm(runtime_),
     performanceMonitoring(runtime_)
 {
     MOZ_ASSERT_IF(creationOptions_.mergeable(),
                   creationOptions_.invisibleToDebugger());
 
     runtime_->numRealms++;
 }
 
@@ -115,21 +117,16 @@ ObjectRealm::init(JSContext* cx)
     iteratorSentinel_ = std::move(sentinel);
     enumerators = iteratorSentinel_.get();
     return true;
 }
 
 bool
 Realm::init(JSContext* cx)
 {
-    // Initialize JSCompartment. This is temporary until Realm and
-    // JSCompartment are completely separated.
-    if (!JSCompartment::init(cx))
-        return false;
-
     /*
      * As a hack, we clear our timezone cache every time we create a new realm.
      * This ensures that the cache is always relatively fresh, but shouldn't
      * interfere with benchmarks that create tons of date objects (unless they
      * also create tons of iframes, which seems unlikely).
      */
     JS::ResetTimeZone();
 
@@ -1032,17 +1029,17 @@ Realm::purge()
 
 void
 Realm::clearTables()
 {
     global_.set(nullptr);
 
     // No scripts should have run in this realm. This is used when merging
     // a realm that has been used off thread into another realm and zone.
-    JS::GetCompartmentForRealm(this)->assertNoCrossCompartmentWrappers();
+    compartment()->assertNoCrossCompartmentWrappers();
     MOZ_ASSERT(!jitRealm_);
     MOZ_ASSERT(!debugEnvs_);
     MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
 
     objectGroups_.clearTables();
     if (savedStacks_.initialized())
         savedStacks_.clear();
     if (varNames_.initialized())
@@ -1071,17 +1068,17 @@ Realm::forgetAllocationMetadataBuilder()
 
     allocationMetadataBuilder_ = nullptr;
 }
 
 void
 Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->realm() == this);
-    assertSameCompartment(cx, JS::GetCompartmentForRealm(this), obj);
+    assertSameCompartment(cx, compartment(), obj);
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
         MOZ_ASSERT(metadata->realm() == obj->realm());
         assertSameCompartment(cx, metadata);
 
         if (!objects_.objectMetadataTable) {
             auto table = cx->make_unique<ObjectWeakMap>(cx);
@@ -1306,23 +1303,26 @@ Realm::clearBreakpointsIn(FreeOp* fop, j
 {
     for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
         if (script->realm() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearBreakpointsIn(fop, dbg, handler);
     }
 }
 
 void
-JSCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                      size_t* crossCompartmentWrappersArg)
+JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                      size_t* compartmentObjects,
+                                      size_t* crossCompartmentWrappersTables,
+                                      size_t* compartmentsPrivateData)
 {
-    // Note that Realm inherits from JSCompartment (for now) so sizeof(*this) is
-    // included in that.
+    *compartmentObjects += mallocSizeOf(this);
+    *crossCompartmentWrappersTables += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
 
-    *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
+    if (auto callback = runtime_->sizeOfIncludingThisCompartmentCallback)
+        *compartmentsPrivateData += callback(mallocSizeOf, this);
 }
 
 void
 ObjectRealm::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                     size_t* innerViewsArg,
                                     size_t* lazyArrayBuffersArg,
                                     size_t* objectMetadataTablesArg,
                                     size_t* nonSyntacticLexicalEnvironmentsArg)
@@ -1344,27 +1344,22 @@ Realm::addSizeOfIncludingThis(mozilla::M
                               size_t* tiAllocationSiteTables,
                               size_t* tiArrayTypeTables,
                               size_t* tiObjectTypeTables,
                               size_t* realmObject,
                               size_t* realmTables,
                               size_t* innerViewsArg,
                               size_t* lazyArrayBuffersArg,
                               size_t* objectMetadataTablesArg,
-                              size_t* crossCompartmentWrappersArg,
                               size_t* savedStacksSet,
                               size_t* varNamesSet,
                               size_t* nonSyntacticLexicalEnvironmentsArg,
                               size_t* jitRealm,
-                              size_t* privateData,
                               size_t* scriptCountsMapArg)
 {
-    // This is temporary until Realm and JSCompartment are completely separated.
-    JSCompartment::addSizeOfExcludingThis(mallocSizeOf, crossCompartmentWrappersArg);
-
     *realmObject += mallocSizeOf(this);
     objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
                                          tiArrayTypeTables, tiObjectTypeTables,
                                          realmTables);
     wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
 
     objects_.addSizeOfExcludingThis(mallocSizeOf,
                                     innerViewsArg,
@@ -1373,25 +1368,20 @@ Realm::addSizeOfIncludingThis(mozilla::M
                                     nonSyntacticLexicalEnvironmentsArg);
 
     *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
     *varNamesSet += varNames_.sizeOfExcludingThis(mallocSizeOf);
 
     if (jitRealm_)
         *jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
 
-    auto callback = runtime_->sizeOfIncludingThisCompartmentCallback;
-    if (callback)
-        *privateData += callback(mallocSizeOf, this);
-
     if (scriptCountsMap) {
         *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
-        for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
+        for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront())
             *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
-        }
     }
 }
 
 mozilla::HashCodeScrambler
 Realm::randomHashCodeScrambler()
 {
     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                       randomKeyGenerator_.next());
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -547,21 +547,20 @@ struct IteratorHashPolicy
 namespace js {
 class DebugEnvironments;
 class ObjectWeakMap;
 class WeakMapBase;
 } // namespace js
 
 struct JSCompartment
 {
-  protected:
+  private:
     JS::Zone*                    zone_;
     JSRuntime*                   runtime_;
 
-  private:
     js::WrapperMap crossCompartmentWrappers;
 
     using RealmVector = js::Vector<JS::Realm*, 1, js::SystemAllocPolicy>;
     RealmVector realms_;
 
   public:
     /*
      * During GC, stores the head of a list of incoming pointers from gray cells.
@@ -598,35 +597,35 @@ struct JSCompartment
     RealmVector& realms() {
         return realms_;
     }
 
     void assertNoCrossCompartmentWrappers() {
         MOZ_ASSERT(crossCompartmentWrappers.empty());
     }
 
-  protected:
-    void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                size_t* crossCompartmentWrappersArg);
+    void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                size_t* compartmentObjects,
+                                size_t* crossCompartmentWrappersTables,
+                                size_t* compartmentsPrivateData);
 
-  public:
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkWrapperMapAfterMovingGC();
 #endif
 
   private:
     bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
     bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
 
-  protected:
+  public:
     explicit JSCompartment(JS::Zone* zone);
 
     MOZ_MUST_USE bool init(JSContext* cx);
+    void destroy(js::FreeOp* fop);
 
-  public:
     MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);
 
     MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp);
 #ifdef ENABLE_BIGINT
     MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandle<JS::BigInt*> bi);
 #endif
     MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandleObject obj);
     MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle<js::PropertyDescriptor> desc);
@@ -670,18 +669,18 @@ struct JSCompartment
      * These methods mark pointers that cross compartment boundaries. They are
      * called in per-zone GCs to prevent the wrappers' outgoing edges from
      * dangling (full GCs naturally follow pointers across compartments) and
      * when compacting to update cross-compartment pointers.
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
+    void sweepRealms(js::FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime);
     void sweepAfterMinorGC(JSTracer* trc);
-
     void sweepCrossCompartmentWrappers();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupAfterMovingGC();
 
     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 };
 
@@ -747,18 +746,21 @@ class ObjectRealm
 
     js::LexicalEnvironmentObject*
     getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, js::HandleObject enclosing);
     js::LexicalEnvironmentObject* getNonSyntacticLexicalEnvironment(JSObject* enclosing) const;
 };
 
 } // namespace js
 
-class JS::Realm : private JSCompartment
+class JS::Realm : public JS::shadow::Realm
 {
+    JS::Zone* zone_;
+    JSRuntime* runtime_;
+
     const JS::RealmCreationOptions creationOptions_;
     JS::RealmBehaviors behaviors_;
 
     friend struct ::JSContext;
     js::ReadBarrieredGlobalObject global_;
 
     // Note: this is private to enforce use of ObjectRealm::get(obj).
     js::ObjectRealm objects_;
@@ -880,44 +882,38 @@ class JS::Realm : private JSCompartment
 
   private:
     void updateDebuggerObservesFlag(unsigned flag);
 
     Realm(const Realm&) = delete;
     void operator=(const Realm&) = delete;
 
   public:
-    Realm(JS::Zone* zone, const JS::RealmOptions& options);
+    Realm(JSCompartment* comp, const JS::RealmOptions& options);
     ~Realm();
 
     MOZ_MUST_USE bool init(JSContext* cx);
     void destroy(js::FreeOp* fop);
     void clearTables();
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* tiAllocationSiteTables,
                                 size_t* tiArrayTypeTables,
                                 size_t* tiObjectTypeTables,
                                 size_t* realmObject,
                                 size_t* realmTables,
                                 size_t* innerViews,
                                 size_t* lazyArrayBuffers,
                                 size_t* objectMetadataTables,
-                                size_t* crossCompartmentWrappers,
                                 size_t* savedStacksSet,
                                 size_t* varNamesSet,
                                 size_t* nonSyntacticLexicalScopes,
                                 size_t* jitRealm,
-                                size_t* privateData,
                                 size_t* scriptCountsMapArg);
 
-    JSCompartment* compartment() {
-        return this;
-    }
-
     JS::Zone* zone() {
         return zone_;
     }
     const JS::Zone* zone() const {
         return zone_;
     }
 
     JSRuntime* runtimeFromMainThread() const {
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -140,17 +140,17 @@ struct JSContext : public JS::RootingCon
 
     template <typename T>
     bool isInsideCurrentZone(T thing) const {
         return thing->zoneFromAnyThread() == zone_;
     }
 
     template <typename T>
     inline bool isInsideCurrentCompartment(T thing) const {
-        return thing->compartment() == GetCompartmentForRealm(realm_);
+        return thing->compartment() == compartment();
     }
 
     void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) {
         if (helperThread()) {
             addPendingOutOfMemory();
             return nullptr;
         }
         return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, this);
@@ -237,20 +237,21 @@ struct JSContext : public JS::RootingCon
 
     void setHelperThread(js::HelperThread* helperThread);
     js::HelperThread* helperThread() const { return helperThread_; }
 
     bool isNurseryAllocSuppressed() const {
         return nurserySuppressions_;
     }
 
-    // Threads may freely access any data in their compartment and zone.
+    // Threads may freely access any data in their realm, compartment and zone.
     JSCompartment* compartment() const {
-        return JS::GetCompartmentForRealm(realm_);
+        return realm_ ? JS::GetCompartmentForRealm(realm_) : nullptr;
     }
+
     JS::Realm* realm() const {
         return realm_;
     }
 
 #ifdef DEBUG
     bool inAtomsZone() const;
 #endif
 
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -322,17 +322,20 @@ StatsZoneCallback(JSRuntime* rt, void* d
     zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
                                  &zStats.typePool,
                                  &zStats.regexpZone,
                                  &zStats.jitZone,
                                  &zStats.baselineStubsOptimized,
                                  &zStats.cachedCFG,
                                  &zStats.uniqueIdMap,
                                  &zStats.shapeTables,
-                                 &rtStats->runtime.atomsMarkBitmaps);
+                                 &rtStats->runtime.atomsMarkBitmaps,
+                                 &zStats.compartmentObjects,
+                                 &zStats.crossCompartmentWrappersTables,
+                                 &zStats.compartmentsPrivateData);
 }
 
 static void
 StatsRealmCallback(JSContext* cx, void* data, Handle<Realm*> realm)
 {
     // Append a new RealmStats to the vector.
     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
 
@@ -350,22 +353,20 @@ StatsRealmCallback(JSContext* cx, void* 
                                   &realmStats.typeInferenceAllocationSiteTables,
                                   &realmStats.typeInferenceArrayTypeTables,
                                   &realmStats.typeInferenceObjectTypeTables,
                                   &realmStats.realmObject,
                                   &realmStats.realmTables,
                                   &realmStats.innerViewsTable,
                                   &realmStats.lazyArrayBuffersTable,
                                   &realmStats.objectMetadataTable,
-                                  &realmStats.crossCompartmentWrappersTable,
                                   &realmStats.savedStacksSet,
                                   &realmStats.varNamesSet,
                                   &realmStats.nonSyntacticLexicalScopesTable,
                                   &realmStats.jitRealm,
-                                  &realmStats.privateData,
                                   &realmStats.scriptCountsMap);
 }
 
 static void
 StatsArenaCallback(JSRuntime* rt, void* data, gc::Arena* arena,
                    JS::TraceKind traceKind, size_t thingSize)
 {
     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4590,26 +4590,36 @@ void
 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                              size_t* typePool,
                              size_t* regexpZone,
                              size_t* jitZone,
                              size_t* baselineStubsOptimized,
                              size_t* cachedCFG,
                              size_t* uniqueIdMap,
                              size_t* shapeTables,
-                             size_t* atomsMarkBitmaps)
+                             size_t* atomsMarkBitmaps,
+                             size_t* compartmentObjects,
+                             size_t* crossCompartmentWrappersTables,
+                             size_t* compartmentsPrivateData)
 {
     *typePool += types.typeLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
     *regexpZone += regExps.sizeOfExcludingThis(mallocSizeOf);
     if (jitZone_)
         jitZone_->addSizeOfIncludingThis(mallocSizeOf, jitZone, baselineStubsOptimized, cachedCFG);
     *uniqueIdMap += uniqueIds().sizeOfExcludingThis(mallocSizeOf);
     *shapeTables += baseShapes().sizeOfExcludingThis(mallocSizeOf)
                   + initialShapes().sizeOfExcludingThis(mallocSizeOf);
     *atomsMarkBitmaps += markedAtoms().sizeOfExcludingThis(mallocSizeOf);
+
+    for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
+        comp->addSizeOfIncludingThis(mallocSizeOf,
+                                     compartmentObjects,
+                                     crossCompartmentWrappersTables,
+                                     compartmentsPrivateData);
+    }
 }
 
 TypeZone::TypeZone(Zone* zone)
   : zone_(zone),
     typeLifoAlloc_(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     currentCompilationId_(zone),
     generation(zone, 0),
     sweepTypeLifoAlloc(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1345,16 +1345,29 @@ ReportZoneStats(const JS::ZoneStats& zSt
     ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("unique-id-map"),
         zStats.uniqueIdMap,
         "Address-independent cell identities.");
 
     ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shape-tables"),
         zStats.shapeTables,
         "Tables storing shape information.");
 
+    ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/compartment-objects"),
+        zStats.compartmentObjects,
+        "The JSCompartment objects in this zone.");
+
+    ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/cross-compartment-wrapper-tables"),
+        zStats.crossCompartmentWrappersTables,
+        "The cross-compartment wrapper tables.");
+
+    ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/private-data"),
+        zStats.compartmentsPrivateData,
+        "Extra data attached to each compartment by XPConnect, including "
+        "its wrapped-js.");
+
     ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
         zStats.lazyScriptsGCHeap,
         "Scripts that haven't executed yet.");
 
     ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
         zStats.lazyScriptsMallocHeap,
         "Lazy script tables containing closed-over bindings or inner functions.");
 
@@ -1745,17 +1758,17 @@ ReportRealmStats(const JS::RealmStats& r
         "Tables of type objects associated with array literals.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
         realmStats.typeInferenceObjectTypeTables,
         "Tables of type objects associated with object literals.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-object"),
         realmStats.realmObject,
-        "The JSCompartment object itself.");
+        "The JS::Realm object itself.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-tables"),
         realmStats.realmTables,
         "Realm-wide tables storing object group information and wasm instances.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("inner-views"),
         realmStats.innerViewsTable,
         "The table for array buffer inner views.");
@@ -1763,37 +1776,28 @@ ReportRealmStats(const JS::RealmStats& r
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"),
         realmStats.lazyArrayBuffersTable,
         "The table for typed object lazy array buffers.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("object-metadata"),
         realmStats.objectMetadataTable,
         "The table used by debugging tools for tracking object metadata");
 
-    ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
-        realmStats.crossCompartmentWrappersTable,
-        "The cross-compartment wrapper table.");
-
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("saved-stacks-set"),
         realmStats.savedStacksSet,
         "The saved stacks set.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("non-syntactic-lexical-scopes-table"),
         realmStats.nonSyntacticLexicalScopesTable,
         "The non-syntactic lexical scopes table.");
 
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("jit-realm"),
         realmStats.jitRealm,
         "The JIT realm.");
 
-    ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("private-data"),
-        realmStats.privateData,
-        "Extra data attached to the realm by XPConnect, including "
-        "its wrapped-js.");
-
     ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("script-counts-map"),
         realmStats.scriptCountsMap,
         "Profiling-related information for scripts.");
 
     if (sundriesGCHeap > 0) {
         // We deliberately don't use ZRREPORT_GC_BYTES here.
         REPORT_GC_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
             sundriesGCHeap,