Bug 1468406 part 4 - Remove remaining JSObject::realm() calls. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 20 Jun 2018 11:04:02 +0200
changeset 808763 77b8ddea1fbd524f0917844f43c21c186262de5b
parent 808762 ee6d7b68769f8a889b9217c82f932683aa707f1e
child 808764 7af2b112f3758a63801689d185eea7d03f3a1be0
child 809310 cca08218423ce0abfe73bebed9b84d6443b647dc
child 809695 4ce808d7697ca3fa93ba4a94e8df8484a1b18d81
push id113477
push userbmo:cmanchester@mozilla.com
push dateWed, 20 Jun 2018 15:54:34 +0000
reviewersluke
bugs1468406
milestone62.0a1
Bug 1468406 part 4 - Remove remaining JSObject::realm() calls. r=luke
js/src/builtin/TestingFunctions.cpp
js/src/jit/CacheIR.cpp
js/src/jsapi-tests/testPreserveJitCode.cpp
js/src/jsapi-tests/testUbiNode.cpp
js/src/jsapi.cpp
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/EnvironmentObject.cpp
js/src/vm/JSContext-inl.h
js/src/vm/JSContext.h
js/src/vm/JSObject-inl.h
js/src/vm/JSObject.h
js/src/vm/MemoryMetrics.cpp
js/src/vm/NativeObject.h
js/src/vm/Realm-inl.h
js/src/vm/Realm.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/Stack-inl.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1355,17 +1355,17 @@ CaptureFirstSubsumedFrame(JSContext* cx,
 
     RootedObject obj(cx, &args[0].toObject());
     obj = CheckedUnwrap(obj);
     if (!obj) {
         JS_ReportErrorASCII(cx, "Denied permission to object.");
         return false;
     }
 
-    JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->realm()->principals()));
+    JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
     if (args.length() > 1)
         capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted = JS::ToBoolean(args[1]);
 
     JS::RootedObject capturedStack(cx);
     if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture)))
         return false;
 
     args.rval().setObjectOrNull(capturedStack);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1123,17 +1123,17 @@ GetPropIRGenerator::tryAttachCrossCompar
     // such as de-lazifying type info.
     {
         AutoRealm ar(cx_, unwrapped);
 
         // The first CCW for iframes is almost always wrapping another WindowProxy
         // so we optimize for that case as well.
         isWindowProxy = IsWindowProxy(unwrapped);
         if (isWindowProxy) {
-            MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->realm()->maybeGlobal());
+            MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == &unwrapped->nonCCWGlobal());
             unwrapped = cx_->global();
             MOZ_ASSERT(unwrapped);
         }
 
         NativeGetPropCacheability canCache =
             CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_,
                                 resultFlags_, isTemporarilyUnoptimizable_);
         if (canCache != CanAttachReadSlot)
--- a/js/src/jsapi-tests/testPreserveJitCode.cpp
+++ b/js/src/jsapi-tests/testPreserveJitCode.cpp
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // For js::jit::IsIonEnabled().
 #include "jit/Ion.h"
 
 #include "jsapi-tests/tests.h"
 
+#include "vm/JSObject-inl.h"
+
 using namespace JS;
 
 static void
 ScriptCallback(JSRuntime* rt, void* data, JSScript* script, const JS::AutoRequireNoGC& nogc)
 {
     unsigned& count = *static_cast<unsigned*>(data);
     if (script->hasIonScript())
         ++count;
@@ -23,17 +25,17 @@ BEGIN_TEST(test_PreserveJitCode)
     CHECK(testPreserveJitCode(true, 1));
     return true;
 }
 
 unsigned
 countIonScripts(JSObject* global)
 {
     unsigned count = 0;
-    js::IterateScripts(cx, global->realm(), &count, ScriptCallback);
+    js::IterateScripts(cx, global->nonCCWRealm(), &count, ScriptCallback);
     return count;
 }
 
 bool
 testPreserveJitCode(bool preserveJitCode, unsigned remainingIonScripts)
 {
     cx->options().setBaseline(true);
     cx->options().setIon(true);
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -7,16 +7,18 @@
 #include "js/UbiNodeDominatorTree.h"
 #include "js/UbiNodePostOrder.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "jsapi-tests/tests.h"
 #include "util/Text.h"
 #include "vm/Realm.h"
 #include "vm/SavedFrame.h"
 
+#include "vm/JSObject-inl.h"
+
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using namespace js;
 
 // A helper JS::ubi::Node concrete implementation that can be used to make mock
 // graphs for testing traversals with.
 struct FakeNode
@@ -123,18 +125,18 @@ BEGIN_TEST(test_ubiNodeCompartment)
 
     JS::RealmOptions globalOptions;
     RootedObject global2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                                                 JS::FireOnNewGlobalHook, globalOptions));
     CHECK(global2);
     CHECK(global1->compartment() != global2->compartment());
     CHECK(JS::ubi::Node(global2).compartment() == global2->compartment());
     CHECK(JS::ubi::Node(global2).compartment() != global1->compartment());
-    CHECK(JS::ubi::Node(global2).realm() == global2->realm());
-    CHECK(JS::ubi::Node(global2).realm() != global1->realm());
+    CHECK(JS::ubi::Node(global2).realm() == global2->nonCCWRealm());
+    CHECK(JS::ubi::Node(global2).realm() != global1->nonCCWRealm());
 
     JS::CompileOptions options(cx);
 
     // Create a script in the original realm...
     RootedScript script1(cx);
     CHECK(JS::Compile(cx, options, "", 0, &script1));
 
     {
@@ -142,18 +144,18 @@ BEGIN_TEST(test_ubiNodeCompartment)
         // there, too.
         JSAutoRealm ar(cx, global2);
 
         RootedScript script2(cx);
         CHECK(JS::Compile(cx, options, "", 0, &script2));
 
         CHECK(JS::ubi::Node(script1).compartment() == global1->compartment());
         CHECK(JS::ubi::Node(script2).compartment() == global2->compartment());
-        CHECK(JS::ubi::Node(script1).realm() == global1->realm());
-        CHECK(JS::ubi::Node(script2).realm() == global2->realm());
+        CHECK(JS::ubi::Node(script1).realm() == global1->nonCCWRealm());
+        CHECK(JS::ubi::Node(script2).realm() == global2->nonCCWRealm());
 
         // Now create a wrapper for global1 in global2's compartment.
         RootedObject wrappedGlobal1(cx, global1);
         CHECK(cx->compartment()->wrap(cx, &wrappedGlobal1));
 
         // Cross-compartment wrappers have a compartment() but not a realm().
         CHECK(JS::ubi::Node(wrappedGlobal1).zone() == cx->zone());
         CHECK(JS::ubi::Node(wrappedGlobal1).compartment() == cx->compartment());
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -880,17 +880,17 @@ JS_TransplantObject(JSContext* cx, Handl
 
     JS::Compartment* destination = target->compartment();
 
     if (origobj->compartment() == destination) {
         // If the original object is in the same compartment as the
         // destination, then we know that we won't find a wrapper in the
         // destination's cross compartment map and that the same
         // object will continue to work.
-        AutoRealmUnchecked ar(cx, origobj->realm());
+        AutoRealmUnchecked ar(cx, origobj->deprecatedRealm());
         if (!JSObject::swap(cx, origobj, target))
             MOZ_CRASH();
         newIdentity = origobj;
     } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
         // There might already be a wrapper for the original object in
         // the new compartment. If there is, we use its identity and swap
         // in the contents of |target|.
         newIdentity = &p->value().get().toObject();
@@ -914,17 +914,17 @@ JS_TransplantObject(JSContext* cx, Handl
     // `newIdentity == origobj`, because this process also clears out any
     // cached wrapper state.
     if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
         MOZ_CRASH();
 
     // Lastly, update the original object to point to the new one.
     if (origobj->compartment() != destination) {
         RootedObject newIdentityWrapper(cx, newIdentity);
-        AutoRealmUnchecked ar(cx, origobj->realm());
+        AutoRealmUnchecked ar(cx, origobj->deprecatedRealm());
         if (!JS_WrapObject(cx, &newIdentityWrapper))
             MOZ_CRASH();
         MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
         if (!JSObject::swap(cx, origobj, newIdentityWrapper))
             MOZ_CRASH();
         if (!origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv))
             MOZ_CRASH();
     }
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -609,17 +609,17 @@ js::RemapWrapper(JSContext* cx, JSObject
     RootedObject newTarget(cx, newTargetArg);
     MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
     MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
     JSObject* origTarget = Wrapper::wrappedObject(wobj);
     MOZ_ASSERT(origTarget);
     MOZ_ASSERT(!JS_IsDeadWrapper(origTarget),
                "We don't want a dead proxy in the wrapper map");
     Value origv = ObjectValue(*origTarget);
-    Realm* wrealm = wobj->realm();
+    Realm* wrealm = wobj->deprecatedRealm();
     JS::Compartment* wcompartment = wobj->compartment();
 
     AutoDisableProxyCheck adpc;
 
     // If we're mapping to a different target (as opposed to just recomputing
     // for the same target), we must not have an existing wrapper for the new
     // target, otherwise this will break.
     MOZ_ASSERT_IF(origTarget != newTarget,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -10664,17 +10664,17 @@ DebuggerObject::unwrap(JSContext* cx, Ha
     if (!unwrapped) {
         result.set(nullptr);
         return true;
     }
 
     // Don't allow unwrapping to create a D.O whose referent is in an
     // invisible-to-Debugger global. (If our referent is a *wrapper* to such,
     // and the wrapper is in a visible realm, that's fine.)
-    if (unwrapped->realm()->creationOptions().invisibleToDebugger()) {
+    if (unwrapped->deprecatedRealm()->creationOptions().invisibleToDebugger()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
         return false;
     }
 
     return dbg->wrapDebuggeeObject(cx, unwrapped, result);
 }
 
 /* static */ bool
@@ -11540,17 +11540,37 @@ Debugger::isDebuggerCrossCompartmentEdge
     } else if (obj->is<DebuggerObject>()) {
         referent = static_cast<gc::Cell*>(obj->as<DebuggerObject>().getPrivate());
     } else if (obj->is<DebuggerEnvironment>()) {
         referent = static_cast<gc::Cell*>(obj->as<DebuggerEnvironment>().getPrivate());
     }
 
     return referent == target;
 }
-#endif
+
+static void
+CheckDebuggeeThingRealm(Realm* realm, bool invisibleOk)
+{
+    MOZ_ASSERT(!realm->creationOptions().mergeable());
+    MOZ_ASSERT_IF(!invisibleOk, !realm->creationOptions().invisibleToDebugger());
+}
+
+void
+js::CheckDebuggeeThing(JSScript* script, bool invisibleOk)
+{
+    CheckDebuggeeThingRealm(script->realm(), invisibleOk);
+}
+
+void
+js::CheckDebuggeeThing(JSObject* obj, bool invisibleOk)
+{
+    if (Realm* realm = JS::GetObjectRealmOrNull(obj))
+        CheckDebuggeeThingRealm(realm, invisibleOk);
+}
+#endif // DEBUG
 
 
 /*** JS::dbg::GarbageCollectionEvent **************************************************************/
 
 namespace JS {
 namespace dbg {
 
 /* static */ GarbageCollectionEvent::Ptr
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -84,16 +84,24 @@ class DebuggerMemory;
 class ScriptedOnStepHandler;
 class ScriptedOnPopHandler;
 class WasmInstanceObject;
 
 typedef HashSet<ReadBarrieredGlobalObject,
                 MovableCellHasher<ReadBarrieredGlobalObject>,
                 ZoneAllocPolicy> WeakGlobalObjectSet;
 
+#ifdef DEBUG
+extern void
+CheckDebuggeeThing(JSScript* script, bool invisibleOk);
+
+extern void
+CheckDebuggeeThing(JSObject* obj, bool invisibleOk);
+#endif
+
 /*
  * A weakmap from GC thing keys to JSObject values that supports the keys being
  * in different compartments to the values. All values must be in the same
  * compartment.
  *
  * The purpose of this is to allow the garbage collector to easily find edges
  * from debuggee object compartments to debugger compartments when calculating
  * the compartment groups.  Note that these edges are the inverse of the edges
@@ -156,19 +164,19 @@ class DebuggerWeakMap : private WeakMap<
 
     MOZ_MUST_USE bool init(uint32_t len = 16) {
         return Base::init(len) && zoneCounts.init();
     }
 
     template<typename KeyInput, typename ValueInput>
     bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
         MOZ_ASSERT(v->compartment() == this->compartment);
-        MOZ_ASSERT(!k->realm()->creationOptions().mergeable());
-        MOZ_ASSERT_IF(!InvisibleKeysOk,
-                      !k->realm()->creationOptions().invisibleToDebugger());
+#ifdef DEBUG
+        CheckDebuggeeThing(k, InvisibleKeysOk);
+#endif
         MOZ_ASSERT(!Base::has(k));
         if (!incZoneCount(k->zone()))
             return false;
         bool ok = Base::relookupOrAdd(p, k, v);
         if (!ok)
             decZoneCount(k->zone());
         return ok;
     }
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2547,17 +2547,17 @@ DebugEnvironments::hasDebugEnvironment(J
     return nullptr;
 }
 
 /* static */ bool
 DebugEnvironments::addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
                                        Handle<DebugEnvironmentProxy*> debugEnv)
 {
     MOZ_ASSERT(cx->realm() == env->realm());
-    MOZ_ASSERT(cx->realm() == debugEnv->realm());
+    MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm());
 
     if (!CanUseDebugEnvironmentMaps(cx))
         return true;
 
     DebugEnvironments* envs = ensureRealmData(cx);
     if (!envs)
         return false;
 
@@ -2580,17 +2580,17 @@ DebugEnvironments::hasDebugEnvironment(J
     return nullptr;
 }
 
 /* static */ bool
 DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
                                        Handle<DebugEnvironmentProxy*> debugEnv)
 {
     MOZ_ASSERT(!ei.hasSyntacticEnvironment());
-    MOZ_ASSERT(cx->realm() == debugEnv->realm());
+    MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm());
     // Generators should always have environments.
     MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
                   !ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator() &&
                   !ei.scope().as<FunctionScope>().canonicalFunction()->isAsync());
 
     if (!CanUseDebugEnvironmentMaps(cx))
         return true;
 
@@ -2862,17 +2862,17 @@ DebugEnvironments::updateLiveEnvironment
      * fp, simply popping fp effectively clears the flag for us, at exactly
      * the time when execution resumes fp->prev().
      */
     for (AllFramesIter i(cx); !i.done(); ++i) {
         if (!i.hasUsableAbstractFramePtr())
             continue;
 
         AbstractFramePtr frame = i.abstractFramePtr();
-        if (frame.environmentChain()->realm() != cx->realm())
+        if (frame.realm() != cx->realm())
             continue;
 
         if (frame.isFunctionFrame()) {
             if (frame.callee()->isGenerator() || frame.callee()->isAsync())
                 continue;
         }
 
         if (!frame.isDebuggee())
@@ -2891,17 +2891,17 @@ DebugEnvironments::updateLiveEnvironment
                     return false;
                 if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
                     return false;
             }
         }
 
         if (frame.prevUpToDate())
             return true;
-        MOZ_ASSERT(frame.environmentChain()->realm()->isDebuggee());
+        MOZ_ASSERT(frame.realm()->isDebuggee());
         frame.setPrevUpToDate();
     }
 
     return true;
 }
 
 LiveEnvironmentVal*
 DebugEnvironments::hasLiveEnvironment(EnvironmentObject& env)
@@ -2930,17 +2930,17 @@ DebugEnvironments::unsetPrevUpToDateUnti
     for (AllFramesIter i(cx); !i.done(); ++i) {
         if (!i.hasUsableAbstractFramePtr())
             continue;
 
         AbstractFramePtr frame = i.abstractFramePtr();
         if (frame == until)
             return;
 
-        if (frame.environmentChain()->realm() != cx->realm())
+        if (frame.realm() != cx->realm())
             continue;
 
         frame.unsetPrevUpToDate();
     }
 }
 
 /* static */ void
 DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to)
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -473,19 +473,32 @@ JSContext::enterAtomsZone(const js::Auto
     // Only one thread can be in the atoms zone at a time.
     MOZ_ASSERT(runtime_->currentThreadHasExclusiveAccess());
 
     realm_ = nullptr;
     zone_ = runtime_->atomsZone(lock);
     arenas_ = &zone_->arenas;
 }
 
-template <typename T>
+inline void
+JSContext::enterRealmOf(JSObject* target)
+{
+    MOZ_ASSERT(JS::CellIsNotGray(target));
+    enterRealm(target->deprecatedRealm());
+}
+
 inline void
-JSContext::enterRealmOf(const T& target)
+JSContext::enterRealmOf(JSScript* target)
+{
+    MOZ_ASSERT(JS::CellIsNotGray(target));
+    enterRealm(target->realm());
+}
+
+inline void
+JSContext::enterRealmOf(js::ObjectGroup* target)
 {
     MOZ_ASSERT(JS::CellIsNotGray(target));
     enterRealm(target->realm());
 }
 
 inline void
 JSContext::enterNullRealm()
 {
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -207,18 +207,19 @@ struct JSContext : public JS::RootingCon
     inline void setRealm(JS::Realm* realm);
     inline void enterRealm(JS::Realm* realm);
     inline void enterAtomsZone(const js::AutoLockForExclusiveAccess& lock);
 
     friend class js::AutoAtomsZone;
     friend class js::AutoRealm;
 
   public:
-    template <typename T>
-    inline void enterRealmOf(const T& target);
+    inline void enterRealmOf(JSObject* target);
+    inline void enterRealmOf(JSScript* target);
+    inline void enterRealmOf(js::ObjectGroup* target);
     inline void enterNullRealm();
 
     inline void leaveRealm(JS::Realm* oldRealm);
     inline void leaveAtomsZone(JS::Realm* oldRealm,
                                const js::AutoLockForExclusiveAccess& lock);
 
     void setHelperThread(js::HelperThread* helperThread);
     js::HelperThread* helperThread() const { return helperThread_; }
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -388,31 +388,29 @@ SetNewObjectMetadata(JSContext* cx, T* o
     return obj;
 }
 
 } // namespace js
 
 inline js::GlobalObject&
 JSObject::deprecatedGlobal() const
 {
-    return *realm()->unsafeUnbarrieredMaybeGlobal();
+    return *deprecatedRealm()->unsafeUnbarrieredMaybeGlobal();
 }
 
 inline js::GlobalObject&
 JSObject::nonCCWGlobal() const
 {
-    MOZ_ASSERT(!js::IsCrossCompartmentWrapper(this));
-
     /*
      * The global is read-barriered so that it is kept live by access through
      * the Realm. When accessed through a JSObject, however, the global will be
      * already kept live by the black JSObject's group pointer, so does not
      * need to be read-barriered.
      */
-    return *realm()->unsafeUnbarrieredMaybeGlobal();
+    return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
 }
 
 inline bool
 JSObject::hasAllFlags(js::BaseShape::Flag flags) const
 {
     MOZ_ASSERT(flags);
     if (js::Shape* shape = maybeShape())
         return shape->hasAllObjectFlags(flags);
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -157,17 +157,16 @@ class JSObject : public js::gc::Cell
      * might have a lazy group, use getGroup() below, otherwise group().
      */
     bool hasLazyGroup() const {
         return group_->lazy();
     }
 
     JS::Compartment* compartment() const { return group_->compartment(); }
     JS::Compartment* maybeCompartment() const { return compartment(); }
-    JS::Realm* realm() const { return group_->realm(); }
 
     inline js::Shape* maybeShape() const;
     inline js::Shape* ensureShape(JSContext* cx);
 
     enum GenerateShape {
         GENERATE_NONE,
         GENERATE_SHAPE
     };
@@ -428,19 +427,37 @@ class JSObject : public js::gc::Cell
      * non-EnvironmentObjects can be on the environment chain).
      */
     inline JSObject* enclosingEnvironment() const;
 
     // Deprecated: call nonCCWGlobal or NativeObject::global() instead!
     inline js::GlobalObject& deprecatedGlobal() const;
 
     // Cross-compartment wrappers are not associated with a single realm/global,
-    // so this method asserts the object is not a CCW.
+    // so these methods assert the object is not a CCW.
     inline js::GlobalObject& nonCCWGlobal() const;
 
+    JS::Realm* nonCCWRealm() const {
+        MOZ_ASSERT(!js::IsCrossCompartmentWrapper(this));
+        return group_->realm();
+    }
+
+    // Returns the object's realm even if the object is a CCW (be careful, in
+    // this case the realm is not very meaningful because wrappers are shared by
+    // all realms in the compartment).
+    JS::Realm* maybeCCWRealm() const {
+        return group_->realm();
+    }
+
+    // Deprecated: call nonCCWRealm(), maybeCCWRealm(), or NativeObject::realm()
+    // instead!
+    JS::Realm* deprecatedRealm() const {
+        return group_->realm();
+    }
+
     /*
      * ES5 meta-object properties and operations.
      */
 
   public:
     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
     // This method really shouldn't exist -- but there are a few internal
     // places that want it (JITs and the like), and it'd be a pain to mark them
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -456,17 +456,17 @@ StatsCellCallback(JSRuntime* rt, void* d
                   size_t thingSize)
 {
     StatsClosure* closure = static_cast<StatsClosure*>(data);
     RuntimeStats* rtStats = closure->rtStats;
     ZoneStats* zStats = rtStats->currZoneStats;
     switch (traceKind) {
       case JS::TraceKind::Object: {
         JSObject* obj = static_cast<JSObject*>(thing);
-        RealmStats& realmStats = obj->realm()->realmStats();
+        RealmStats& realmStats = obj->maybeCCWRealm()->realmStats();
         JS::ClassInfo info;        // This zeroes all the sizes.
         info.objectsGCHeap += thingSize;
 
         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
         // These classes require special handling due to shared resources which
         // we must be careful not to report twice.
         if (obj->is<WasmModuleObject>()) {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1469,18 +1469,20 @@ class NativeObject : public ShapedObject
     inline js::gc::AllocKind allocKindForTenure() const;
 
     void updateShapeAfterMovingGC();
     void sweepDictionaryListPointer();
     void updateDictionaryListPointerAfterMinorGC(NativeObject* old);
 
     // Native objects are never wrappers, so a native object always has a realm
     // and global.
+    JS::Realm* realm() const {
+        return nonCCWRealm();
+    }
     inline js::GlobalObject& global() const;
-    JS::Realm* realm() const { return JSObject::realm(); }
 
     /* JIT Accessors */
     static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
     static size_t offsetOfFixedElements() {
         return sizeof(NativeObject) + sizeof(ObjectElements);
     }
 
     static size_t getFixedSlotOffset(size_t slot) {
--- a/js/src/vm/Realm-inl.h
+++ b/js/src/vm/Realm-inl.h
@@ -41,17 +41,20 @@ JS::Realm::globalIsAboutToBeFinalized()
 {
     MOZ_ASSERT(zone_->isGCSweeping());
     return global_ && js::gc::IsAboutToBeFinalizedUnbarriered(global_.unsafeGet());
 }
 
 /* static */ inline js::ObjectRealm&
 js::ObjectRealm::get(const JSObject* obj)
 {
-    return obj->realm()->objects_;
+    // Note: obj might be a CCW if we're accessing ObjectRealm::enumerators.
+    // CCWs here are fine because we always return the same ObjectRealm for a
+    // particular (CCW) object.
+    return obj->maybeCCWRealm()->objects_;
 }
 
 template <typename T>
 js::AutoRealm::AutoRealm(JSContext* cx, const T& target)
   : cx_(cx),
     origin_(cx->realm())
 {
     cx_->enterRealmOf(target);
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -637,22 +637,22 @@ Realm::forgetAllocationMetadataBuilder()
     CancelOffThreadIonCompile(this);
 
     allocationMetadataBuilder_ = nullptr;
 }
 
 void
 Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
 {
-    MOZ_ASSERT(obj->realm() == this);
+    MOZ_ASSERT(obj->maybeCCWRealm() == this);
     assertSameCompartment(cx, compartment(), obj);
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
-        MOZ_ASSERT(metadata->realm() == obj->realm());
+        MOZ_ASSERT(metadata->maybeCCWRealm() == obj->maybeCCWRealm());
         assertSameCompartment(cx, metadata);
 
         if (!objects_.objectMetadataTable) {
             auto table = cx->make_unique<ObjectWeakMap>(cx);
             if (!table || !table->init())
                 oomUnsafe.crash("setNewObjectMetadata");
 
             objects_.objectMetadataTable = std::move(table);
@@ -1011,17 +1011,17 @@ JS_PUBLIC_API(JS::Realm*)
 JS::GetCurrentRealmOrNull(JSContext* cx)
 {
     return cx->realm();
 }
 
 JS_PUBLIC_API(JS::Realm*)
 JS::GetObjectRealmOrNull(JSObject* obj)
 {
-    return IsCrossCompartmentWrapper(obj) ? nullptr : obj->realm();
+    return IsCrossCompartmentWrapper(obj) ? nullptr : obj->nonCCWRealm();
 }
 
 JS_PUBLIC_API(void*)
 JS::GetRealmPrivate(JS::Realm* realm)
 {
     return realm->realmPrivate();
 }
 
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -779,25 +779,25 @@ public:
     AutoMaybeEnterFrameRealm(JSContext* cx,
                              HandleObject obj
                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
         MOZ_RELEASE_ASSERT(cx->realm());
         if (obj)
-            MOZ_RELEASE_ASSERT(obj->realm());
+            MOZ_RELEASE_ASSERT(obj->deprecatedRealm());
 
         // Note that obj might be null here, since we're doing this before
         // UnwrapSavedFrame.
-        if (obj && cx->realm() != obj->realm())
+        if (obj && cx->realm() != obj->deprecatedRealm())
         {
             JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
             if (subsumes && subsumes(cx->realm()->principals(),
-                                     obj->realm()->principals()))
+                                     obj->deprecatedRealm()->principals()))
             {
                 ar_.emplace(cx, obj);
             }
         }
     }
 
  private:
     Maybe<JSAutoRealm> ar_;
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -492,17 +492,17 @@ inline bool
 AbstractFramePtr::pushVarEnvironment(JSContext* cx, HandleScope scope)
 {
     return js::PushVarEnvironmentObject(cx, scope, *this);
 }
 
 inline JS::Realm*
 AbstractFramePtr::realm() const
 {
-    return environmentChain()->realm();
+    return environmentChain()->nonCCWRealm();
 }
 
 inline unsigned
 AbstractFramePtr::numActualArgs() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->numActualArgs();
     if (isBaselineFrame())