Bug 1465472 - Remove the atoms realm/compartment. r=jonco
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 31 May 2018 12:53:26 +0200
changeset 420689 b671b92bea186c1f5659425f6e048d555df415d7
parent 420688 66c01f311f4adfaef3e2412a5b9aa4916b57b5b2
child 420690 5031770d70fd643230cb4caf6a5106616adaf0fd
push id34077
push usernerli@mozilla.com
push dateThu, 31 May 2018 21:51:59 +0000
treeherdermozilla-central@42880a726964 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1465472
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 1465472 - Remove the atoms realm/compartment. r=jonco Some of the less trivial changes: * When we allocated a symbol, we used the atom realm's RNG to create a hash code [0]. Instead of this I added a RNG for this to the runtime. * IsCompilingWasm returned true if the JitContext's realm is nullptr, but that's now also true when we initialize the trampolines stored in the atoms zone. To fix that I added a CompileZone* to JitContext, in addition to the CompileRuntime and CompileRealm, and we now check the zone instead of the realm in IsCompilingWasm. * JSContext::hasEnteredRealm is only called in DEBUG builds so I made hasEnteredRealm and enterRealmDepth_ #ifdef DEBUG.
dom/workers/WorkerPrivate.cpp
js/public/Realm.h
js/src/builtin/Object.cpp
js/src/gc/Allocator.cpp
js/src/gc/AtomMarking.cpp
js/src/gc/GC.cpp
js/src/gc/Nursery.cpp
js/src/gc/PrivateIterators-inl.h
js/src/gc/PublicIterators.cpp
js/src/gc/PublicIterators.h
js/src/gc/RootMarking.cpp
js/src/gc/Tracer.cpp
js/src/jit/BaselineDebugModeOSR.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/shared/Assembler-shared.h
js/src/jsapi.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/GeckoProfiler.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/JSAtom.cpp
js/src/vm/JSCompartment-inl.h
js/src/vm/JSCompartment.cpp
js/src/vm/JSCompartment.h
js/src/vm/JSContext-inl.h
js/src/vm/JSContext.cpp
js/src/vm/JSContext.h
js/src/vm/MemoryMetrics.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Stopwatch.cpp
js/src/vm/StringType-inl.h
js/src/vm/StringType.cpp
js/src/vm/SymbolType.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1201,25 +1201,22 @@ public:
                       override
   {
     MOZ_ASSERT(!aRealmStats->extra);
 
     // ReportJSRuntimeExplicitTreeStats expects that
     // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
     xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
 
-    // This is the |jsPathPrefix|.  Each worker has exactly two realms:
-    // one for atoms, and one for everything else.
+    // This is the |jsPathPrefix|.  Each worker has exactly one realm.
     JSCompartment* compartment = JS::GetCompartmentForRealm(aRealm);
     extras->jsPathPrefix.Assign(mRtPath);
     extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
                                             (void *)js::GetCompartmentZone(compartment));
-    extras->jsPathPrefix += js::IsAtomsRealm(aRealm)
-                            ? NS_LITERAL_CSTRING("realm(web-worker-atoms)/")
-                            : NS_LITERAL_CSTRING("realm(web-worker)/");
+    extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/");
 
     // This should never be used when reporting with workers (hence the "?!").
     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
 
     MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
     MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
 
     extras->location = nullptr;
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -92,18 +92,18 @@ SetDestroyRealmCallback(JSContext* cx, D
 typedef void
 (* RealmNameCallback)(JSContext* cx, Handle<Realm*> realm, char* buf, size_t bufsize);
 
 // Set the callback SpiderMonkey calls to get the name of a realm, for
 // diagnostic output.
 extern JS_PUBLIC_API(void)
 SetRealmNameCallback(JSContext* cx, RealmNameCallback callback);
 
-// Get the global object for the given realm. Returns null only if `realm` is
-// the atoms realm.
+// Get the global object for the given realm. This only returns nullptr during
+// GC, between collecting the global object and destroying the Realm.
 extern JS_PUBLIC_API(JSObject*)
 GetRealmGlobalOrNull(Handle<Realm*> realm);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmObjectPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmFunctionPrototype(JSContext* cx);
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1994,17 +1994,17 @@ CreateObjectConstructor(JSContext* cx, J
 
     fun->setJitInfo(&jit::JitInfo_Object);
     return fun;
 }
 
 static JSObject*
 CreateObjectPrototype(JSContext* cx, JSProtoKey key)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     MOZ_ASSERT(cx->global()->isNative());
 
     /*
      * Create |Object.prototype| first, mirroring CreateBlankProto but for the
      * prototype of the created object.
      */
     RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
                                                                            SingletonObject));
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -81,17 +81,17 @@ template JSObject* js::Allocate<JSObject
 template <AllowGC allowGC>
 JSObject*
 GCRuntime::tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots, const Class* clasp)
 {
     MOZ_RELEASE_ASSERT(!cx->helperThread());
 
     MOZ_ASSERT(cx->isNurseryAllocAllowed());
     MOZ_ASSERT(!cx->isNurseryAllocSuppressed());
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     JSObject* obj = cx->nursery().allocateObject(cx, thingSize, nDynamicSlots, clasp);
     if (obj)
         return obj;
 
     if (allowGC && !cx->suppressGC) {
         cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY);
 
@@ -135,17 +135,17 @@ GCRuntime::tryNewTenuredObject(JSContext
 template <AllowGC allowGC>
 JSString*
 GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize, AllocKind kind)
 {
     MOZ_ASSERT(IsNurseryAllocable(kind));
     MOZ_ASSERT(cx->isNurseryAllocAllowed());
     MOZ_ASSERT(!cx->helperThread());
     MOZ_ASSERT(!cx->isNurseryAllocSuppressed());
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     Cell* cell = cx->nursery().allocateString(cx->zone(), thingSize, kind);
     if (cell)
         return static_cast<JSString*>(cell);
 
     if (allowGC && !cx->suppressGC) {
         cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY);
 
@@ -270,23 +270,23 @@ bool
 GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
 {
     if (allowGC) {
         if (!gcIfNeededAtAllocation(cx))
             return false;
     }
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
-    MOZ_ASSERT_IF(cx->realm()->isAtomsRealm(),
+    MOZ_ASSERT_IF(cx->zone()->isAtomsZone(),
                   kind == AllocKind::ATOM ||
                   kind == AllocKind::FAT_INLINE_ATOM ||
                   kind == AllocKind::SYMBOL ||
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
-    MOZ_ASSERT_IF(!cx->realm()->isAtomsRealm(),
+    MOZ_ASSERT_IF(!cx->zone()->isAtomsZone(),
                   kind != AllocKind::ATOM &&
                   kind != AllocKind::FAT_INLINE_ATOM);
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
     MOZ_ASSERT(cx->isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !cx->suppressGC)
--- a/js/src/gc/AtomMarking.cpp
+++ b/js/src/gc/AtomMarking.cpp
@@ -78,17 +78,17 @@ bool
 AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap)
 {
     MOZ_ASSERT(CurrentThreadIsPerformingGC());
     MOZ_ASSERT(!runtime->hasHelperThreadZones());
 
     if (!bitmap.ensureSpace(allocatedWords))
         return false;
 
-    Zone* atomsZone = runtime->unsafeAtomsRealm()->zone();
+    Zone* atomsZone = runtime->unsafeAtomsZone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
             bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 
@@ -112,17 +112,17 @@ template <typename Bitmap>
 static void
 AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
 {
     // Make sure that by copying the mark bits for one arena in word sizes we
     // do not affect the mark bits for other arenas.
     static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
                   "ArenaBitmapWords must evenly divide ArenaBitmapBits");
 
-    Zone* atomsZone = runtime->unsafeAtomsRealm()->zone();
+    Zone* atomsZone = runtime->unsafeAtomsZone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
             bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -3847,17 +3847,16 @@ Zone::sweepCompartments(FreeOp* fop, boo
 
     JSCompartment** read = compartments().begin();
     JSCompartment** end = compartments().end();
     JSCompartment** write = read;
     bool foundOne = false;
     while (read < end) {
         JSCompartment* comp = *read++;
         Realm* realm = JS::GetRealmForCompartment(comp);
-        MOZ_ASSERT(!realm->isAtomsRealm());
 
         /*
          * Don't delete the last compartment and realm if all the ones before
          * it were deleted and keepAtleastOne is true.
          */
         bool dontDelete = read == end && !foundOne && keepAtleastOne;
         if ((!realm->marked() && !dontDelete) || destroyingRuntime) {
             realm->destroy(fop);
@@ -4121,17 +4120,17 @@ struct MaybeCompartmentFunctor {
     template <typename T> JSCompartment* operator()(T* t) { return t->maybeCompartment(); }
 };
 
 void
 CompartmentCheckTracer::onChild(const JS::GCCellPtr& thing)
 {
     JSCompartment* comp = DispatchTyped(MaybeCompartmentFunctor(), thing);
     if (comp && compartment) {
-        MOZ_ASSERT(comp == compartment || runtime()->isAtomsCompartment(comp) ||
+        MOZ_ASSERT(comp == compartment ||
                    (srcKind == JS::TraceKind::Object &&
                     InCrossCompartmentMap(static_cast<JSObject*>(src), thing)));
     } else {
         TenuredCell* tenured = TenuredCell::fromPointer(thing.asCell());
         Zone* thingZone = tenured->zoneFromAnyThread();
         MOZ_ASSERT(thingZone == zone || thingZone->isAtomsZone());
     }
 }
@@ -4218,17 +4217,17 @@ ShouldCollectZone(Zone* zone, JS::gcreas
 bool
 GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut,
                                      AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     /* Assert that zone state is as we expect */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         MOZ_ASSERT(!zone->isCollecting());
-        MOZ_ASSERT(!zone->compartments().empty());
+        MOZ_ASSERT_IF(!zone->isAtomsZone(), !zone->compartments().empty());
         for (auto i : AllAllocKinds())
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
     }
 #endif
 
     *isFullOut = true;
     bool any = false;
 
@@ -4246,17 +4245,17 @@ GCRuntime::prepareZonesForCollection(JS:
 
         zone->setPreservingCode(false);
     }
 
     // Discard JIT code more aggressively if the process is approaching its
     // executable code limit.
     bool canAllocateMoreCode = jit::CanLikelyAllocateMoreExecutableMemory();
 
-    for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         c->scheduledForDestruction = false;
         c->maybeAlive = false;
         for (RealmsInCompartmentIter r(c); !r.done(); r.next()) {
             r->unmark();
             if (r->shouldTraceGlobal() || !r->zone()->isGCScheduled())
                 c->maybeAlive = true;
             if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
                 r->zone()->setPreservingCode(true);
@@ -4473,17 +4472,17 @@ GCRuntime::markCompartments()
      * for details on this problem. To avoid the problem, we try to avoid
      * allocation and read barriers during JS_TransplantObject and the like.
      */
 
     /* Propagate the maybeAlive flag via cross-compartment edges. */
 
     Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList;
 
-    for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
+    for (CompartmentsIter comp(rt); !comp.done(); comp.next()) {
         if (comp->maybeAlive) {
             if (!workList.append(comp))
                 return;
         }
     }
 
     while (!workList.empty()) {
         JSCompartment* comp = workList.popCopy();
@@ -4890,17 +4889,17 @@ GCRuntime::finishMarkingValidation()
 static void
 DropStringWrappers(JSRuntime* rt)
 {
     /*
      * String "wrappers" are dropped on GC because their presence would require
      * us to sweep the wrappers in all compartments every time we sweep a
      * compartment group.
      */
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         for (JSCompartment::StringWrapperEnum e(c); !e.empty(); e.popFront()) {
             MOZ_ASSERT(e.front().key().is<JSString*>());
             e.removeFront();
         }
     }
 }
 
 /*
@@ -5132,17 +5131,17 @@ AssertNotOnGrayList(JSObject* obj)
                   GetProxyReservedSlot(obj, ProxyObject::grayLinkReservedSlot(obj)).isUndefined());
 }
 #endif
 
 static void
 AssertNoWrappersInGrayList(JSRuntime* rt)
 {
 #ifdef DEBUG
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::NonStringWrapperEnum e(c); !e.empty(); e.popFront())
             AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
     }
 #endif
 }
 
 static JSObject*
@@ -5407,17 +5406,17 @@ UpdateAtomsBitmap(GCParallelTask* task)
         // conservative to just not call it.
     }
 
     runtime->gc.atomMarking.updateChunkMarkBits(runtime);
 
     // For convenience sweep these tables non-incrementally as part of bitmap
     // sweeping; they are likely to be much smaller than the main atoms table.
     runtime->unsafeSymbolRegistry().sweep();
-    for (RealmsIter realm(runtime, SkipAtoms); !realm.done(); realm.next())
+    for (RealmsIter realm(runtime); !realm.done(); realm.next())
         realm->sweepVarNames();
 }
 
 static void
 SweepCCWrappers(GCParallelTask* task)
 {
     JSRuntime* runtime = task->runtime();
     for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next())
@@ -6883,17 +6882,17 @@ GCRuntime::resetIncrementalGC(gc::AbortR
         MOZ_ASSERT(!marker.shouldCheckCompartments());
 
         break;
       }
 
       case State::Sweep: {
         marker.reset();
 
-        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+        for (CompartmentsIter c(rt); !c.done(); c.next())
             c->scheduledForDestruction = false;
 
         /* Finish sweeping the current sweep group, then abort. */
         abortSweepAfterCurrentGroup = true;
 
         /* Don't perform any compaction after sweeping. */
         bool wasCompacting = isCompacting;
         isCompacting = false;
@@ -7547,17 +7546,17 @@ GCRuntime::scanZonesBeforeGC()
 void
 GCRuntime::maybeDoCycleCollection()
 {
     const static double ExcessiveGrayRealms = 0.8;
     const static size_t LimitGrayRealms = 200;
 
     size_t realmsTotal = 0;
     size_t realmsGray = 0;
-    for (RealmsIter realm(rt, SkipAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(rt); !realm.done(); realm.next()) {
         ++realmsTotal;
         GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal();
         if (global && global->isMarkedGray())
             ++realmsGray;
     }
     double grayFraction = double(realmsGray) / double(realmsTotal);
     if (grayFraction > ExcessiveGrayRealms || realmsGray > LimitGrayRealms)
         callDoCycleCollectionCallback(rt->mainContextFromOwnThread());
@@ -7597,17 +7596,17 @@ bool
 GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental);
     MOZ_ASSERT(!isIncrementalGCInProgress());
 
     if (!isIncremental)
         return false;
 
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (c->scheduledForDestruction)
             return true;
     }
 
     return false;
 }
 
 void
@@ -8483,17 +8482,17 @@ js::gc::CheckHashTablesAfterMovingGC(JSR
 
         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()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         c->checkWrapperMapAfterMovingGC();
 
         for (RealmsInCompartmentIter r(c); !r.done(); r.next()) {
             r->checkObjectGroupTablesAfterMovingGC();
             r->dtoaCache.checkCacheAfterMovingGC();
             r->checkScriptMapsAfterMovingGC();
             if (r->debugEnvs())
                 r->debugEnvs()->checkHashTablesAfterMovingGC();
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -1024,17 +1024,17 @@ js::Nursery::sweep(JSTracer* trc)
             obj->zone()->removeUniqueId(obj);
         } else {
             JSObject* dst = Forwarded(obj);
             dst->zone()->transferUniqueId(dst, obj);
         }
     }
     cellsWithUid_.clear();
 
-    for (CompartmentsIter c(runtime(), SkipAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(runtime()); !c.done(); c.next())
         c->sweepAfterMinorGC(trc);
 
     sweepDictionaryModeObjects();
     sweepMapAndSetObjects();
 }
 
 void
 js::Nursery::clear()
--- a/js/src/gc/PrivateIterators-inl.h
+++ b/js/src/gc/PrivateIterators-inl.h
@@ -62,17 +62,17 @@ class GCZonesIter
     ZonesIter zone;
 
   public:
     explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
         MOZ_ASSERT(JS::CurrentThreadIsHeapBusy());
         MOZ_ASSERT_IF(rt->gc.atomsZone->isCollectingFromAnyThread(),
                       !rt->hasHelperThreadZones());
 
-        if (!zone->isCollectingFromAnyThread())
+        if (!done() && !zone->isCollectingFromAnyThread())
             next();
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
         MOZ_ASSERT(!done());
         do {
@@ -90,28 +90,40 @@ class GCZonesIter
 };
 
 using GCCompartmentsIter = CompartmentsOrRealmsIterT<GCZonesIter, CompartmentsInZoneIter>;
 using GCRealmsIter = CompartmentsOrRealmsIterT<GCZonesIter, RealmsInZoneIter>;
 
 /* Iterates over all zones in the current sweep group. */
 class SweepGroupZonesIter {
     JS::Zone* current;
+    ZoneSelector selector;
 
   public:
-    explicit SweepGroupZonesIter(JSRuntime* rt) {
+    explicit SweepGroupZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
+      : selector(selector)
+    {
         MOZ_ASSERT(CurrentThreadIsPerformingGC());
         current = rt->gc.getCurrentSweepGroup();
+        maybeSkipAtomsZone();
+    }
+
+    void maybeSkipAtomsZone() {
+        if (selector == SkipAtoms && current && current->isAtomsZone()) {
+            current = current->nextNodeInGroup();
+            MOZ_ASSERT_IF(current, !current->isAtomsZone());
+        }
     }
 
     bool done() const { return !current; }
 
     void next() {
         MOZ_ASSERT(!done());
         current = current->nextNodeInGroup();
+        maybeSkipAtomsZone();
     }
 
     JS::Zone* get() const {
         MOZ_ASSERT(!done());
         return current;
     }
 
     operator JS::Zone*() const { return get(); }
--- a/js/src/gc/PublicIterators.cpp
+++ b/js/src/gc/PublicIterators.cpp
@@ -136,23 +136,23 @@ js::IterateGrayObjectsUnderCC(Zone* zone
 }
 
 JS_PUBLIC_API(void)
 JS_IterateCompartments(JSContext* cx, void* data,
                        JSIterateCompartmentCallback compartmentCallback)
 {
     AutoTraceSession session(cx->runtime());
 
-    for (CompartmentsIter c(cx->runtime(), WithAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next())
         (*compartmentCallback)(cx, data, c);
 }
 
 JS_PUBLIC_API(void)
 JS::IterateRealms(JSContext* cx, void* data, JS::IterateRealmCallback realmCallback)
 {
     AutoTraceSession session(cx->runtime());
 
     Rooted<Realm*> realm(cx);
-    for (RealmsIter r(cx->runtime(), WithAtoms); !r.done(); r.next()) {
+    for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
         realm = r;
         (*realmCallback)(cx, data, realm);
     }
 }
--- a/js/src/gc/PublicIterators.h
+++ b/js/src/gc/PublicIterators.h
@@ -188,24 +188,17 @@ class CompartmentsOrRealmsIterT
     using T = typename InnerIterT::ItemType;
 
     gc::AutoEnterIteration iterMarker;
     ZonesIterT zone;
     mozilla::Maybe<InnerIterT> inner;
 
   public:
     explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
-      : iterMarker(&rt->gc), zone(rt)
-    {
-        if (!zone.done())
-            inner.emplace(zone);
-    }
-
-    CompartmentsOrRealmsIterT(JSRuntime* rt, ZoneSelector selector)
-      : iterMarker(&rt->gc), zone(rt, selector)
+      : iterMarker(&rt->gc), zone(rt, SkipAtoms)
     {
         if (!zone.done())
             inner.emplace(zone);
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -364,17 +364,17 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
     // Trace the shared Intl data.
     rt->traceSharedIntlData(trc);
 
     // Trace the JSContext.
     rt->mainContextFromOwnThread()->trace(trc);
 
     // Trace all realm roots, but not the realm itself; it is traced via the
     // parent pointer if traceRoots actually traces anything.
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->traceRoots(trc, traceOrMark);
 
     // Trace helper thread roots.
     HelperThreadState().trace(trc, session);
 
     // Trace the embedding's black and gray roots.
     if (!JS::CurrentThreadIsHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING);
@@ -422,17 +422,17 @@ js::gc::GCRuntime::finishRoots()
 
     if (rootsHash.ref().initialized())
         rootsHash.ref().clear();
 
     rt->finishPersistentRoots();
 
     rt->finishSelfHosting();
 
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->finishRoots();
 
 #ifdef DEBUG
     // The nsWrapperCache may not be empty before our shutdown GC, so we have
     // to skip that table when verifying that we are fully unrooted.
     auto prior = grayRootTracer;
     grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
 
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -158,17 +158,17 @@ struct TraceIncomingFunctor {
     // reference.
     void operator()(JSString** tp) {}
 };
 } // namespace (anonymous)
 
 JS_PUBLIC_API(void)
 JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments)
 {
-    for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
+    for (js::CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) {
         if (compartments.has(comp))
             continue;
 
         for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
             mozilla::DebugOnly<const CrossCompartmentKey> prior = e.front().key();
             e.front().mutableKey().applyToWrapped(TraceIncomingFunctor(trc, compartments));
             MOZ_ASSERT(e.front().key() == prior);
         }
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -1038,17 +1038,17 @@ BaselineFrame::deleteDebugModeOSRInfo()
     flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
 }
 
 JitCode*
 JitRuntime::getBaselineDebugModeOSRHandler(JSContext* cx)
 {
     if (!baselineDebugModeOSRHandler_) {
         AutoLockForExclusiveAccess lock(cx);
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         uint32_t offset;
         if (JitCode* code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
             baselineDebugModeOSRHandler_ = code;
             baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
         }
     }
 
     return baselineDebugModeOSRHandler_;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -103,27 +103,29 @@ jit::MaybeGetJitContext()
     return CurrentJitContext();
 }
 
 JitContext::JitContext(CompileRuntime* rt, CompileRealm* realm, TempAllocator* temp)
   : cx(nullptr),
     temp(temp),
     runtime(rt),
     realm(realm),
+    zone(realm ? realm->zone() : nullptr),
     prev_(CurrentJitContext()),
     assemblerCount_(0)
 {
     SetJitContext(this);
 }
 
 JitContext::JitContext(JSContext* cx, TempAllocator* temp)
   : cx(cx),
     temp(temp),
     runtime(CompileRuntime::get(cx->runtime())),
     realm(CompileRealm::get(cx->realm())),
+    zone(CompileZone::get(cx->zone())),
     prev_(CurrentJitContext()),
     assemblerCount_(0)
 {
     SetJitContext(this);
 }
 
 JitContext::JitContext(TempAllocator* temp)
   : JitContext(nullptr, nullptr, temp)
@@ -206,23 +208,20 @@ JitRuntime::startTrampolineCode(MacroAss
     masm.haltingAlign(CodeAlignment);
     masm.setFramePushed(0);
     return masm.currentOffset();
 }
 
 bool
 JitRuntime::initialize(JSContext* cx, AutoLockForExclusiveAccess& lock)
 {
-    AutoAtomsRealm ar(cx, lock);
+    AutoAtomsZone az(cx, lock);
 
     JitContext jctx(cx, nullptr);
 
-    if (!cx->realm()->ensureJitRealmExists(cx))
-        return false;
-
     functionWrappers_ = cx->new_<VMWrapperMap>(cx);
     if (!functionWrappers_ || !functionWrappers_->init())
         return false;
 
     StackMacroAssembler masm;
 
     Label bailoutTail;
     JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub");
@@ -334,17 +333,17 @@ JitRuntime::initialize(JSContext* cx, Au
 
 JitCode*
 JitRuntime::debugTrapHandler(JSContext* cx)
 {
     if (!debugTrapHandler_) {
         // JitRuntime code stubs are shared across compartments and have to
         // be allocated in the atoms zone.
         AutoLockForExclusiveAccess lock(cx);
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         debugTrapHandler_ = generateDebugTrapHandler(cx);
     }
     return debugTrapHandler_;
 }
 
 JitRuntime::IonBuilderList&
 JitRuntime::ionLazyLinkList(JSRuntime* rt)
 {
@@ -587,17 +586,17 @@ JitRuntime::Trace(JSTracer* trc, AutoLoc
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
 
     // Shared stubs are allocated in the atoms zone, so do not iterate
     // them after the atoms heap after it has been "finished."
     if (trc->runtime()->atomsAreFinished())
         return;
 
-    Zone* zone = trc->runtime()->atomsRealm(lock)->zone();
+    Zone* zone = trc->runtime()->atomsZone(lock);
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
 JitRuntime::TraceJitcodeGlobalTableForMinorGC(JSTracer* trc)
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -67,16 +67,17 @@ class JitContext
 
     // Allocator for temporary memory during compilation.
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/realm for use
     // during compilation.
     CompileRuntime* runtime;
     CompileRealm* realm;
+    CompileZone* zone;
 
     int getNextAssemblerId() {
         return assemblerCount_++;
     }
   private:
     JitContext* prev_;
     int assemblerCount_;
 };
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -152,18 +152,18 @@ struct Imm64
     inline Imm32 firstHalf() const;
     inline Imm32 secondHalf() const;
 };
 
 #ifdef DEBUG
 static inline bool
 IsCompilingWasm()
 {
-    // wasm compilation pushes a JitContext with a null Realm.
-    return GetJitContext()->realm == nullptr;
+    // wasm compilation pushes a JitContext with a null Zone.
+    return GetJitContext()->zone == nullptr;
 }
 #endif
 
 // Pointer to be embedded as an immediate in an instruction.
 struct ImmPtr
 {
     void* value;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -792,17 +792,17 @@ JS_WrapValue(JSContext* cx, MutableHandl
     return cx->compartment()->wrap(cx, vp);
 }
 
 static void
 ReleaseAssertObjectHasNoWrappers(JSContext* cx, HandleObject target)
 {
     RootedValue origv(cx, ObjectValue(*target));
 
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         if (c->lookupWrapper(origv))
             MOZ_CRASH("wrapper found for target object");
     }
 }
 
 /*
  * Brain transplants. Not for beginners or the squeamish.
  *
@@ -949,17 +949,17 @@ JS_PUBLIC_API(bool)
 JS_RefreshCrossCompartmentWrappers(JSContext* cx, HandleObject obj)
 {
     return RemapAllWrappersForObject(cx, obj, obj);
 }
 
 JS_PUBLIC_API(bool)
 JS_InitStandardClasses(JSContext* cx, HandleObject obj)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     assertSameCompartment(cx, obj);
 
     Rooted<GlobalObject*> global(cx, &obj->global());
     return GlobalObject::initStandardClasses(cx, global);
 }
@@ -1969,34 +1969,34 @@ JS_FireOnNewGlobalObject(JSContext* cx, 
     assertSameCompartment(cx, global);
     Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
     Debugger::onNewGlobalObject(cx, globalObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObject(JSContext* cx, const JSClass* jsclasp)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     return NewObjectWithClassProto(cx, clasp, nullptr);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectWithGivenProto(JSContext* cx, const JSClass* jsclasp, HandleObject proto)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
@@ -2004,17 +2004,17 @@ JS_NewObjectWithGivenProto(JSContext* cx
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     return NewObjectWithGivenProto(cx, clasp, proto);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewPlainObject(JSContext* cx)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return NewBuiltinClassInstance<PlainObject>(cx);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const CallArgs& args)
@@ -2904,34 +2904,34 @@ JS::IsConstructor(JSObject* obj)
 {
     return obj->isConstructor();
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args,
                      MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fval, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
     RootedValue thisv(cx, ObjectOrNullValue(obj));
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
                 MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fun, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
@@ -2939,17 +2939,17 @@ JS_CallFunction(JSContext* cx, HandleObj
     RootedValue thisv(cx, ObjectOrNullValue(obj));
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args,
                     MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, args);
 
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
@@ -3373,28 +3373,28 @@ JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject* obj, uint32_t index, const Value& value)
 {
     obj->as<NativeObject>().setReservedSlot(index, value);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     assertSameCompartment(cx, contents);
     return NewDenseCopiedArray(cx, contents.length(), contents.begin());
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, size_t length)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return NewDenseFullyAllocatedArray(cx, length);
 }
 
 inline bool
 IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType)
@@ -3507,17 +3507,17 @@ JS_InitReadPrincipalsCallback(JSContext*
     MOZ_ASSERT(!cx->runtime()->readPrincipals);
     cx->runtime()->readPrincipals = read;
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                const char* name)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedAtom atom(cx);
     if (name) {
         atom = Atomize(cx, name, strlen(name));
         if (!atom)
@@ -3527,17 +3527,17 @@ JS_NewFunction(JSContext* cx, JSNative n
     return (flags & JSFUN_CONSTRUCTOR)
            ? NewNativeConstructor(cx, native, nargs, atom)
            : NewNativeFunction(cx, native, nargs, atom);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, id);
 
     RootedAtom name(cx, IdToFunctionName(cx, id));
     if (!name)
         return nullptr;
 
@@ -3783,60 +3783,60 @@ extern JS_PUBLIC_API(bool)
 JS_IsConstructor(JSFunction* fun)
 {
     return fun->isConstructor();
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     return DefineFunctions(cx, obj, fs, NotIntrinsic);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineUCFunction(JSContext* cx, HandleObject obj,
                     const char16_t* name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction*)
 JS_DefineFunctionById(JSContext* cx, HandleObject obj, HandleId id, JSNative call,
                       unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 /* Use the fastest available getc. */
 #if defined(HAVE_GETC_UNLOCKED)
@@ -4077,17 +4077,17 @@ JS::CompileOptions::CompileOptions(JSCon
 }
 
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         SourceBufferHolder& srcBuf, MutableHandleScript script)
 {
     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
 
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf));
     return !!script;
 }
 
 static bool
@@ -4218,17 +4218,17 @@ JS::CompileForNonSyntacticScope(JSContex
 }
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
                  const uint8_t* buf, size_t length)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options, buf, length);
 }
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file)
@@ -4542,17 +4542,17 @@ JS_GetFunctionScript(JSContext* cx, Hand
  */
 static bool
 CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
                 HandleAtom name, bool isInvalidName,
                 SourceBufferHolder& srcBuf, uint32_t parameterListEnd,
                 HandleObject enclosingEnv, HandleScope enclosingScope,
                 MutableHandleFunction fun)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, enclosingEnv);
     RootedAtom funAtom(cx);
 
     fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
                                 isInvalidName ? nullptr : name,
                                 /* proto = */ nullptr,
@@ -4711,17 +4711,17 @@ JS::ExposeScriptToDebugger(JSContext* cx
     MOZ_ASSERT(script->hideScriptFromDebugger());
     script->clearHideScriptFromDebugger();
     Debugger::onNewScript(cx, script);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileScript(JSContext* cx, HandleScript script)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     script->ensureNonLazyCanonicalFunction();
     RootedFunction fun(cx, script->functionNonDelazifying());
     if (fun)
         return JS_DecompileFunction(cx, fun);
     bool haveSource = script->scriptSource()->hasSourceData();
@@ -4729,27 +4729,27 @@ JS_DecompileScript(JSContext* cx, Handle
         return nullptr;
     return haveSource ? JSScript::sourceData(cx, script)
                       : NewStringCopyZ<CanGC>(cx, "[no source]");
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileFunction(JSContext* cx, HandleFunction fun)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, fun);
     return FunctionToString(cx, fun, /* isToSource = */ false);
 }
 
 MOZ_NEVER_INLINE static bool
 ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, scope, script);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(scope), script->hasNonSyntacticScope());
     return Execute(cx, script, *scope, rval);
 }
 
 static bool
@@ -4833,17 +4833,17 @@ JS::CloneAndExecuteScript(JSContext* cx,
 }
 
 static bool
 Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env,
          const ReadOnlyCompileOptions& optionsArg,
          SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     CompileOptions options(cx, optionsArg);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, env);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic);
 
     options.setIsRunOnce(true);
     RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(),
                                                           scopeKind, options, srcBuf));
@@ -4974,17 +4974,17 @@ JS::SetModuleMetadataHook(JSRuntime* rt,
     AssertHeapIsIdle();
     rt->moduleMetadataHook = func;
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf, JS::MutableHandleObject module)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     module.set(frontend::CompileModule(cx, options, srcBuf));
     return !!module;
 }
 
 JS_PUBLIC_API(void)
@@ -5132,17 +5132,17 @@ JS::SetPromiseRejectionTrackerCallback(J
 {
     cx->promiseRejectionTrackerCallback = callback;
     cx->promiseRejectionTrackerCallbackData = data;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, executor, proto);
 
     if (!executor)
         return PromiseObject::createSkippingExecutor(cx);
 
     MOZ_ASSERT(IsCallable(executor));
@@ -5386,17 +5386,17 @@ JS::GetWaitForAllPromise(JSContext* cx, 
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableDefaultStreamObject(JSContext* cx,
                                    JS::HandleObject underlyingSource /* = nullptr */,
                                    JS::HandleFunction size /* = nullptr */,
                                    double highWaterMark /* = 1 */,
                                    JS::HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
@@ -5408,17 +5408,17 @@ JS::NewReadableDefaultStreamObject(JSCon
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableByteStreamObject(JSContext* cx,
                                 JS::HandleObject underlyingSource /* = nullptr */,
                                 double highWaterMark /* = 1 */,
                                 JS::HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
@@ -5467,17 +5467,17 @@ JS::HasReadableStreamCallbacks(JSContext
     return cx->runtime()->readableStreamDataRequestCallback;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
                                           uint8_t flags /* = 0 */,
                                           HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
 #ifdef DEBUG
     JSRuntime* rt = cx->runtime();
     MOZ_ASSERT(rt->readableStreamDataRequestCallback);
     MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
     MOZ_ASSERT(rt->readableStreamCancelCallback);
@@ -7622,19 +7622,18 @@ GetScriptedCallerGlobal(JSContext* cx)
 
     // If the caller is hidden, the embedding wants us to return null here so
     // that it can check its own stack (see HideScriptedCaller).
     if (activation->scriptedCallerIsHidden())
         return nullptr;
 
     GlobalObject* global = realm->maybeGlobal();
 
-    // No one should be running code in the atoms realm or running code in a
-    // realm without any live objects, so there should definitely be a live
-    // global.
+    // No one should be running code in a realm without any live objects, so
+    // there should definitely be a live global.
     MOZ_ASSERT(global);
 
     return global;
 }
 
 JS_PUBLIC_API(void)
 HideScriptedCaller(JSContext* cx)
 {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -246,17 +246,17 @@ DefineHelpProperty(JSContext* cx, Handle
     if (!atom)
         return false;
     return JS_DefineProperty(cx, obj, prop, atom, JSPROP_READONLY | JSPROP_PERMANENT);
 }
 
 JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext* cx, HandleObject obj, const JSFunctionSpecWithHelp* fs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     for (; fs->name; fs++) {
         JSAtom* atom = Atomize(cx, fs->name, strlen(fs->name));
         if (!atom)
             return false;
 
@@ -352,22 +352,16 @@ js::IsSystemCompartment(JSCompartment* c
 
 JS_FRIEND_API(bool)
 js::IsSystemZone(Zone* zone)
 {
     return zone->isSystem;
 }
 
 JS_FRIEND_API(bool)
-js::IsAtomsRealm(JS::Realm* realm)
-{
-    return realm->isAtomsRealm();
-}
-
-JS_FRIEND_API(bool)
 js::IsAtomsZone(JS::Zone* zone)
 {
     return zone->runtimeFromAnyThread()->isAtomsZone(zone);
 }
 
 JS_FRIEND_API(bool)
 js::IsFunctionObject(JSObject* obj)
 {
@@ -433,31 +427,31 @@ js::RunningWithTrustedPrincipals(JSConte
     return cx->runningWithTrustedPrincipals();
 }
 
 JS_FRIEND_API(JSFunction*)
 js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs, gc::AllocKind::FUNCTION_EXTENDED);
 }
 
 JS_FRIEND_API(JSFunction*)
 js::NewFunctionWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                             const char* name)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     CHECK_REQUEST(cx);
 
     RootedAtom atom(cx);
     if (name) {
         atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return nullptr;
@@ -468,17 +462,17 @@ js::NewFunctionWithReserved(JSContext* c
         NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED);
 }
 
 JS_FRIEND_API(JSFunction*)
 js::NewFunctionByIdWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                                 jsid id)
 {
     MOZ_ASSERT(JSID_IS_STRING(id));
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, id);
 
     RootedAtom atom(cx, JSID_TO_ATOM(id));
     return (flags & JSFUN_CONSTRUCTOR) ?
         NewNativeConstructor(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED) :
         NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED);
 }
@@ -1275,16 +1269,19 @@ JS_FRIEND_API(void)
 JS::NotifyGCRootsRemoved(JSContext* cx)
 {
     cx->runtime()->gc.notifyRootsRemoved();
 }
 
 JS_FRIEND_API(JS::Realm*)
 js::GetAnyRealmInZone(JS::Zone* zone)
 {
+    if (zone->isAtomsZone())
+        return nullptr;
+
     RealmsInZoneIter realm(zone);
     MOZ_ASSERT(!realm.done());
     return realm.get();
 }
 
 void
 JS::ObjectPtr::finalize(JSRuntime* rt)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -482,19 +482,16 @@ JS_FRIEND_API(bool) obj_defineSetter(JSC
 
 extern JS_FRIEND_API(bool)
 IsSystemCompartment(JSCompartment* comp);
 
 extern JS_FRIEND_API(bool)
 IsSystemZone(JS::Zone* zone);
 
 extern JS_FRIEND_API(bool)
-IsAtomsRealm(JS::Realm* realm);
-
-extern JS_FRIEND_API(bool)
 IsAtomsZone(JS::Zone* zone);
 
 struct WeakMapTracer
 {
     JSRuntime* runtime;
 
     explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {}
 
@@ -551,16 +548,17 @@ extern JS_FRIEND_API(bool)
 CheckGrayMarkingState(JSRuntime* rt);
 #endif
 
 #ifdef JS_HAS_CTYPES
 extern JS_FRIEND_API(size_t)
 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj);
 #endif
 
+// Note: this returns nullptr iff |zone| is the atoms zone.
 extern JS_FRIEND_API(JS::Realm*)
 GetAnyRealmInZone(JS::Zone* zone);
 
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -532,17 +532,17 @@ js::NukeCrossCompartmentWrappers(JSConte
                                  const CompartmentFilter& sourceFilter,
                                  JSCompartment* target,
                                  js::NukeReferencesToWindow nukeReferencesToWindow,
                                  js::NukeReferencesFromTarget nukeReferencesFromTarget)
 {
     CHECK_REQUEST(cx);
     JSRuntime* rt = cx->runtime();
 
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (!sourceFilter.match(c))
             continue;
 
         // If the compartment matches both the source and target filter, we may
         // want to cut both incoming and outgoing wrappers.
         bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
                         target == c.get());
 
@@ -676,17 +676,17 @@ js::RemapAllWrappersForObject(JSContext*
 
     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     RootedObject newTarget(cx, newTargetArg);
 
     AutoWrapperVector toTransplant(cx);
     if (!toTransplant.reserve(cx->runtime()->numCompartments))
         return false;
 
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
             // We found a wrapper. Remember and root it.
             toTransplant.infallibleAppend(WrapperValue(wp));
         }
     }
 
     for (const WrapperValue& v : toTransplant)
         RemapWrapper(cx, &v.toObject(), newTarget);
@@ -696,17 +696,17 @@ js::RemapAllWrappersForObject(JSContext*
 
 JS_FRIEND_API(bool)
 js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
                       const CompartmentFilter& targetFilter)
 {
     bool evictedNursery = false;
 
     AutoWrapperVector toRecompute(cx);
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         // Filter by source compartment.
         if (!sourceFilter.match(c))
             continue;
 
         if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
             cx->runtime()->gc.evictNursery();
             evictedNursery = true;
         }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -8904,17 +8904,17 @@ Shell(JSContext* cx, OptionParser* op, c
             JS_free(cx, const_cast<char*>(jsCacheDir));
         }
     }
 
     /*
      * Dump remaining type inference results while we still have a context.
      * This printing depends on atoms still existing.
      */
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next())
         PrintTypes(cx, c, false);
 
     return result;
 }
 
 static void
 SetOutputFile(const char* const envVar,
               RCFile* defaultOut,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3059,17 +3059,17 @@ Debugger::markIteratively(GCMarker* mark
 {
     bool markedAny = false;
 
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
     JSRuntime* rt = marker->runtime();
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
+    for (RealmsIter r(rt); !r.done(); r.next()) {
         if (r->isDebuggee()) {
             GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal();
             if (!IsMarkedUnbarriered(rt, &global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
              * getDebuggers can't return nullptr.
@@ -4965,17 +4965,17 @@ Debugger::findAllGlobals(JSContext* cx, 
 
     AutoObjectVector globals(cx);
 
     {
         // Accumulate the list of globals before wrapping them, because
         // wrapping can GC and collect realms from under us, while iterating.
         JS::AutoCheckCannotGC nogc;
 
-        for (RealmsIter r(cx->runtime(), SkipAtoms); !r.done(); r.next()) {
+        for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
             if (r->creationOptions().invisibleToDebugger())
                 continue;
 
             r->compartment()->scheduledForDestruction = false;
 
             GlobalObject* global = r->maybeGlobal();
 
             if (cx->runtime()->isSelfHostingGlobal(global))
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -147,17 +147,17 @@ GeckoProfilerRuntime::enable(bool enable
                 jitActivation = jitActivation->prevJitActivation();
             }
         }
     }
 
     // WebAssembly code does not need to be released, but profiling string
     // labels have to be generated so that they are available during async
     // profiling stack iteration.
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->wasm.ensureProfilingLabels(enabled);
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 GeckoProfilerRuntime::profileString(JSScript* script, JSFunction* maybeFun)
 {
     auto locked = strings.lock();
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -499,17 +499,17 @@ GlobalObject::createInternal(JSContext* 
 }
 
 /* static */ GlobalObject*
 GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::RealmOptions& options)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
-    MOZ_ASSERT_IF(cx->realm(), !cx->realm()->isAtomsRealm());
+    MOZ_ASSERT_IF(cx->zone(), !cx->zone()->isAtomsZone());
 
     Realm* realm = NewRealm(cx, principals, options);
     if (!realm)
         return nullptr;
 
     Rooted<GlobalObject*> global(cx);
     {
         AutoRealmUnchecked ar(cx, realm);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -342,17 +342,17 @@ js::CancelOffThreadIonCompile(const Comp
 }
 
 #ifdef DEBUG
 bool
 js::HasOffThreadIonCompile(Realm* realm)
 {
     AutoLockHelperThreadState lock;
 
-    if (!HelperThreadState().threads || realm->isAtomsRealm())
+    if (!HelperThreadState().threads)
         return false;
 
     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     for (size_t i = 0; i < worklist.length(); i++) {
         jit::IonBuilder* builder = worklist[i];
         if (builder->script()->realm() == realm)
             return true;
     }
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -446,17 +446,17 @@ AtomizeAndCopyCharsInner(JSContext* cx, 
             atom->setPinned();
             p->setPinned(true);
         }
         return atom;
     }
 
     JSAtom* atom;
     {
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
 
         JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
         if (!flat) {
             // Grudgingly forgo last-ditch GC. The alternative would be to release
             // the lock, manually GC here, and retry from the top. If you fix this,
             // please also fix or comment the similar case in Symbol::new_.
             ReportOutOfMemory(cx);
             return nullptr;
--- a/js/src/vm/JSCompartment-inl.h
+++ b/js/src/vm/JSCompartment-inl.h
@@ -47,54 +47,46 @@ JS::Realm::globalIsAboutToBeFinalized()
 js::ObjectRealm::get(const JSObject* obj)
 {
     return obj->realm()->objects_;
 }
 
 template <typename T>
 js::AutoRealm::AutoRealm(JSContext* cx, const T& target)
   : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(nullptr)
+    origin_(cx->realm())
 {
     cx_->enterRealmOf(target);
 }
 
-// Protected constructor that bypasses assertions in enterCompartmentOf. Used
-// only for entering the atoms realm.
-js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target,
-                         js::AutoLockForExclusiveAccess& lock)
-  : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(&lock)
-{
-    MOZ_ASSERT(target->isAtomsRealm());
-    cx_->enterAtomsRealm(target, lock);
-}
-
-// Protected constructor that bypasses assertions in enterCompartmentOf. Should
-// not be used to enter the atoms realm.
+// Protected constructor that bypasses assertions in enterRealmOf.
 js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target)
   : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(nullptr)
+    origin_(cx->realm())
 {
-    MOZ_ASSERT(!target->isAtomsRealm());
-    cx_->enterNonAtomsRealm(target);
+    cx_->enterRealm(target);
 }
 
 js::AutoRealm::~AutoRealm()
 {
-    cx_->leaveRealm(origin_, maybeLock_);
+    cx_->leaveRealm(origin_);
 }
 
-js::AutoAtomsRealm::AutoAtomsRealm(JSContext* cx,
-                                   js::AutoLockForExclusiveAccess& lock)
-  : AutoRealm(cx, cx->atomsRealm(lock), lock)
-{}
+js::AutoAtomsZone::AutoAtomsZone(JSContext* cx, js::AutoLockForExclusiveAccess& lock)
+  : cx_(cx),
+    origin_(cx->realm()),
+    lock_(lock)
+{
+    cx_->enterAtomsZone(lock);
+}
+
+js::AutoAtomsZone::~AutoAtomsZone()
+{
+    cx_->leaveAtomsZone(origin_, lock_);
+}
 
 js::AutoRealmUnchecked::AutoRealmUnchecked(JSContext* cx, JS::Realm* target)
   : AutoRealm(cx, target)
 {}
 
 inline bool
 JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp)
 {
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -87,72 +87,66 @@ Realm::~Realm()
 }
 
 JSCompartment::~JSCompartment()
 {
     runtime_->numCompartments--;
 }
 
 bool
-JSCompartment::init(JSContext* maybecx)
+JSCompartment::init(JSContext* cx)
 {
     if (!crossCompartmentWrappers.init(0)) {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 bool
-ObjectRealm::init(JSContext* maybecx)
+ObjectRealm::init(JSContext* cx)
 {
     if (!iteratorCache.init()) {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
-    NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(maybecx));
+    NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
     if (!sentinel)
         return false;
 
     iteratorSentinel_ = Move(sentinel);
     enumerators = iteratorSentinel_.get();
     return true;
 }
 
 bool
-Realm::init(JSContext* maybecx)
+Realm::init(JSContext* cx)
 {
     // Initialize JSCompartment. This is temporary until Realm and
     // JSCompartment are completely separated.
-    if (!JSCompartment::init(maybecx))
+    if (!JSCompartment::init(cx))
         return false;
 
     /*
-     * maybecx is null when called to create the atoms realm from
-     * JSRuntime::init().
-     *
      * 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();
 
-    if (!objects_.init(maybecx))
+    if (!objects_.init(cx))
         return false;
 
     if (!savedStacks_.init() ||
         !varNames_.init())
     {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 jit::JitRuntime*
 JSRuntime::createJitRuntime(JSContext* cx)
@@ -302,17 +296,16 @@ CopyStringPure(JSContext* cx, JSString* 
         return nullptr;
 
     return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
 }
 
 bool
 JSCompartment::wrap(JSContext* cx, MutableHandleString strp)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
 
     /* If the string is already in this compartment, we are done. */
     JSString* str = strp;
     if (str->zoneFromAnyThread() == zone())
         return true;
 
     /*
@@ -449,17 +442,16 @@ JSCompartment::getOrCreateWrapper(JSCont
 
     obj.set(wrapper);
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext* cx, MutableHandleObject obj)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
 
     if (!obj)
         return true;
 
     AutoDisableProxyCheck adpc;
 
     // Anything we're wrapping has already escaped into script, so must have
@@ -481,17 +473,16 @@ JSCompartment::wrap(JSContext* cx, Mutab
     // Ensure that the wrapper is also exposed.
     ExposeObjectToActiveJS(obj);
     return true;
 }
 
 bool
 JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
     MOZ_ASSERT(obj);
     MOZ_ASSERT(existingArg);
     MOZ_ASSERT(existingArg->compartment() == cx->compartment());
     MOZ_ASSERT(IsDeadProxyObject(existingArg));
 
     AutoDisableProxyCheck adpc;
 
@@ -610,17 +601,16 @@ ObjectRealm::getNonSyntacticLexicalEnvir
         return nullptr;
     return &lexicalEnv->as<LexicalEnvironmentObject>();
 }
 
 bool
 Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
 {
     MOZ_ASSERT(name);
-    MOZ_ASSERT(!isAtomsRealm());
 
     if (varNames_.put(name))
         return true;
 
     ReportOutOfMemory(cx);
     return false;
 }
 
@@ -644,17 +634,17 @@ JSCompartment::traceOutgoingCrossCompart
     }
 }
 
 /* static */ void
 JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
 {
     gcstats::AutoPhase ap(trc->runtime()->gc.stats(), gcstats::PhaseKind::MARK_CCWS);
     MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
-    for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(trc->runtime()); !c.done(); c.next()) {
         if (!c->zone()->isCollecting())
             c->traceOutgoingCrossCompartmentWrappers(trc);
     }
     Debugger::traceIncomingCrossCompartmentEdges(trc);
 }
 
 void
 Realm::traceGlobal(JSTracer* trc)
@@ -914,17 +904,17 @@ Realm::sweepTemplateObjects()
         iterResultTemplate_.set(nullptr);
 }
 
 /* static */ void
 JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
 {
     MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
 
-    for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
+    for (CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) {
         // Sweep the wrapper map to update keys (wrapped values) in other
         // compartments that may have been moved.
         comp->sweepCrossCompartmentWrappers();
         // Trace the wrappers in the map to update their cross-compartment edges
         // to wrapped values in other compartments that may have been moved.
         comp->traceOutgoingCrossCompartmentWrappers(trc);
     }
 }
@@ -1397,22 +1387,16 @@ Realm::addSizeOfIncludingThis(mozilla::M
     if (scriptCountsMap) {
         *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
         for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
             *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
         }
     }
 }
 
-HashNumber
-Realm::randomHashCode()
-{
-    return HashNumber(getOrCreateRandomNumberGenerator().next());
-}
-
 mozilla::HashCodeScrambler
 Realm::randomHashCodeScrambler()
 {
     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                       randomKeyGenerator_.next());
 }
 
 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -615,17 +615,17 @@ struct JSCompartment
   private:
     bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
     bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
 
   protected:
     explicit JSCompartment(JS::Zone* zone);
     ~JSCompartment();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    MOZ_MUST_USE bool init(JSContext* cx);
 
   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
@@ -726,17 +726,17 @@ class ObjectRealm
                                       js::SystemAllocPolicy>;
     IteratorCache iteratorCache;
 
     static inline ObjectRealm& get(const JSObject* obj);
 
     explicit ObjectRealm(JS::Zone* zone);
     ~ObjectRealm();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    MOZ_MUST_USE bool init(JSContext* cx);
 
     void finishRoots();
     void trace(JSTracer* trc);
     void sweepAfterMinorGC();
     void sweepNativeIterators();
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* innerViewsArg,
@@ -827,17 +827,16 @@ class JS::Realm : private JSCompartment
     static const unsigned DebuggerObservesMask = IsDebuggee |
                                                  DebuggerObservesAllExecution |
                                                  DebuggerObservesCoverage |
                                                  DebuggerObservesAsmJS |
                                                  DebuggerObservesBinarySource;
     unsigned debugModeBits_ = 0;
     friend class js::AutoRestoreRealmDebugMode;
 
-    bool isAtomsRealm_ = false;
     bool isSelfHostingRealm_ = false;
     bool marked_ = true;
     bool isSystem_ = false;
 
   public:
     // WebAssembly state for the realm.
     js::wasm::Realm wasm;
 
@@ -885,17 +884,17 @@ class JS::Realm : private JSCompartment
 
     Realm(const Realm&) = delete;
     void operator=(const Realm&) = delete;
 
   public:
     Realm(JS::Zone* zone, const JS::RealmOptions& options);
     ~Realm();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    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,
@@ -935,35 +934,28 @@ class JS::Realm : private JSCompartment
 
     const JS::RealmCreationOptions& creationOptions() const { return creationOptions_; }
     JS::RealmBehaviors& behaviors() { return behaviors_; }
     const JS::RealmBehaviors& behaviors() const { return behaviors_; }
 
     /* Whether to preserve JIT code on non-shrinking GCs. */
     bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
 
-    bool isAtomsRealm() const {
-        return isAtomsRealm_;
-    }
-    void setIsAtomsRealm() {
-        isAtomsRealm_ = true;
-    }
-
     bool isSelfHostingRealm() const {
         return isSelfHostingRealm_;
     }
     void setIsSelfHostingRealm() {
         isSelfHostingRealm_ = true;
     }
 
     /* The global object for this realm.
      *
-     * This returns nullptr if this is the atoms realm.  (The global_ field is
-     * also null briefly during GC, after the global object is collected; but
-     * when that happens the Realm is destroyed during the same GC.)
+     * Note: the global_ field is null briefly during GC, after the global
+     * object is collected; but when that happens the Realm is destroyed during
+     * the same GC.)
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
      * always marks its global as well.
      */
     inline js::GlobalObject* maybeGlobal() const;
 
     /* An unbarriered getter for use while tracing. */
     inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
@@ -1260,18 +1252,16 @@ class JS::Realm : private JSCompartment
 
     // Initializes randomNumberGenerator if needed.
     mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();
 
     const void* addressOfRandomNumberGenerator() const {
         return randomNumberGenerator_.ptr();
     }
 
-    js::HashNumber randomHashCode();
-
     mozilla::HashCodeScrambler randomHashCodeScrambler();
 
     bool isAccessValid() const {
         return validAccessPtr_ ? *validAccessPtr_ : true;
     }
     void setValidAccessPtr(bool* accessp) {
         validAccessPtr_ = accessp;
     }
@@ -1356,42 +1346,45 @@ class MOZ_RAII AssertRealmUnchanged
     JS::Realm* const oldRealm;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoRealm
 {
     JSContext* const cx_;
     JS::Realm* const origin_;
-    const AutoLockForExclusiveAccess* maybeLock_;
 
   public:
     template <typename T>
     inline AutoRealm(JSContext* cx, const T& target);
     inline ~AutoRealm();
 
     JSContext* context() const { return cx_; }
     JS::Realm* origin() const { return origin_; }
 
   protected:
     inline AutoRealm(JSContext* cx, JS::Realm* target);
 
-    // Used only for entering the atoms realm.
-    inline AutoRealm(JSContext* cx, JS::Realm* target,
-                     AutoLockForExclusiveAccess& lock);
-
   private:
     AutoRealm(const AutoRealm&) = delete;
     AutoRealm& operator=(const AutoRealm&) = delete;
 };
 
-class AutoAtomsRealm : protected AutoRealm
+class MOZ_RAII AutoAtomsZone
 {
+    JSContext* const cx_;
+    JS::Realm* const origin_;
+    const AutoLockForExclusiveAccess& lock_;
+
+    AutoAtomsZone(const AutoAtomsZone&) = delete;
+    AutoAtomsZone& operator=(const AutoAtomsZone&) = delete;
+
   public:
-    inline AutoAtomsRealm(JSContext* cx, AutoLockForExclusiveAccess& lock);
+    inline AutoAtomsZone(JSContext* cx, AutoLockForExclusiveAccess& lock);
+    inline ~AutoAtomsZone();
 };
 
 // Enter a realm directly. Only use this where there's no target GC thing
 // to pass to AutoRealm or where you need to avoid the assertions in
 // JS::Compartment::enterCompartmentOf().
 class AutoRealmUnchecked : protected AutoRealm
 {
   public:
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -39,29 +39,24 @@ class CompartmentChecker
         MOZ_CRASH();
     }
 
     static void fail(JS::Zone* z1, JS::Zone* z2) {
         printf("*** Zone mismatch %p vs. %p\n", (void*) z1, (void*) z2);
         MOZ_CRASH();
     }
 
-    /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
     static void check(JSCompartment* c1, JSCompartment* c2) {
-        MOZ_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1));
-        MOZ_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2));
         if (c1 != c2)
             fail(c1, c2);
     }
 
     void check(JSCompartment* c) {
-        if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) {
-            if (c != compartment)
-                fail(compartment, c);
-        }
+        if (c && c != compartment)
+            fail(compartment, c);
     }
 
     void checkZone(JS::Zone* z) {
         if (compartment && z != compartment->zone())
             fail(compartment->zone(), z);
     }
 
     void check(JSObject* obj) {
@@ -458,88 +453,91 @@ JSContext::setPendingException(const js:
 
 inline bool
 JSContext::runningWithTrustedPrincipals()
 {
     return !realm() || realm()->principals() == runtime()->trustedPrincipals();
 }
 
 inline void
-JSContext::enterNonAtomsRealm(JS::Realm* realm)
+JSContext::enterRealm(JS::Realm* realm)
 {
-    enterRealmDepth_++;
+    // We should never enter a realm while in the atoms zone.
+    MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
 
-    MOZ_ASSERT(!realm->zone()->isAtomsZone());
-
+#ifdef DEBUG
+    enterRealmDepth_++;
+#endif
     realm->enter();
-    setRealm(realm, nullptr);
+    setRealm(realm);
 }
 
 inline void
-JSContext::enterAtomsRealm(JS::Realm* realm,
-                           const js::AutoLockForExclusiveAccess& lock)
+JSContext::enterAtomsZone(const js::AutoLockForExclusiveAccess& lock)
 {
-    enterRealmDepth_++;
+    // Only one thread can be in the atoms zone at a time.
+    MOZ_ASSERT(runtime_->currentThreadHasExclusiveAccess());
 
-    MOZ_ASSERT(realm->zone()->isAtomsZone());
-
-    realm->enter();
-    setRealm(realm, &lock);
+    realm_ = nullptr;
+    zone_ = runtime_->atomsZone(lock);
+    arenas_ = &zone_->arenas;
 }
 
 template <typename T>
 inline void
 JSContext::enterRealmOf(const T& target)
 {
     MOZ_ASSERT(JS::CellIsNotGray(target));
-    enterNonAtomsRealm(target->realm());
+    enterRealm(target->realm());
 }
 
 inline void
 JSContext::enterNullRealm()
 {
+    // We should never enter a realm while in the atoms zone.
+    MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
+
+#ifdef DEBUG
     enterRealmDepth_++;
+#endif
     setRealm(nullptr);
 }
 
 inline void
-JSContext::leaveRealm(JS::Realm* oldRealm,
-                      const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
+JSContext::leaveRealm(JS::Realm* oldRealm)
 {
     MOZ_ASSERT(hasEnteredRealm());
+#ifdef DEBUG
     enterRealmDepth_--;
+#endif
 
     // Only call leave() after we've setRealm()-ed away from the current realm.
     JS::Realm* startingRealm = realm_;
-    setRealm(oldRealm, maybeLock);
+    setRealm(oldRealm);
     if (startingRealm)
         startingRealm->leave();
 }
 
 inline void
-JSContext::setRealm(JS::Realm* realm,
-                    const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
+JSContext::leaveAtomsZone(JS::Realm* oldRealm,
+                          const js::AutoLockForExclusiveAccess& lock)
 {
-    // Only one thread can be in the atoms realm at a time.
-    MOZ_ASSERT_IF(realm && realm->isAtomsRealm(), maybeLock != nullptr);
-    MOZ_ASSERT_IF((realm && realm->isAtomsRealm()) || (realm_ && realm_->isAtomsRealm()),
-                  runtime_->currentThreadHasExclusiveAccess());
+    setRealm(oldRealm);
+}
 
-    // Make sure that the atoms realm has its own zone.
-    MOZ_ASSERT_IF(realm && !realm->isAtomsRealm(),
-                  !realm->zone()->isAtomsZone());
-
+inline void
+JSContext::setRealm(JS::Realm* realm)
+{
     // Both the current and the new realm should be properly marked as
     // entered at this point.
     MOZ_ASSERT_IF(realm_, realm_->hasBeenEntered());
     MOZ_ASSERT_IF(realm, realm->hasBeenEntered());
 
     // This thread must have exclusive access to the zone.
-    MOZ_ASSERT_IF(realm && !realm->zone()->isAtomsZone(),
-                  CurrentThreadCanAccessZone(realm->zone()));
+    MOZ_ASSERT_IF(realm, CurrentThreadCanAccessZone(realm->zone()));
 
     realm_ = realm;
     zone_ = realm ? realm->zone() : nullptr;
     arenas_ = zone_ ? &zone_->arenas : nullptr;
 }
 
 inline JSScript*
 JSContext::currentScript(jsbytecode** ppc,
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1214,17 +1214,19 @@ JSContext::alreadyReportedError()
 }
 
 JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
   : runtime_(runtime),
     kind_(ContextKind::HelperThread),
     helperThread_(nullptr),
     options_(options),
     arenas_(nullptr),
+#ifdef DEBUG
     enterRealmDepth_(0),
+#endif
     jitActivation(nullptr),
     activation_(nullptr),
     profilingActivation_(nullptr),
     nativeStackBase(GetNativeStackBase()),
     entryMonitor(nullptr),
     noExecuteDebuggerTop(nullptr),
     activityCallback(nullptr),
     activityCallbackArg(nullptr),
@@ -1350,17 +1352,17 @@ JSContext::setRuntime(JSRuntime* rt)
     runtime_ = rt;
 }
 
 bool
 JSContext::getPendingException(MutableHandleValue rval)
 {
     MOZ_ASSERT(throwing);
     rval.set(unwrappedException());
-    if (realm()->isAtomsRealm())
+    if (zone()->isAtomsZone())
         return true;
     bool wasOverRecursed = overRecursed_;
     clearPendingException();
     if (!compartment()->wrap(this, rval))
         return false;
     assertSameCompartment(this, rval);
     setPendingException(rval);
     overRecursed_ = wasOverRecursed;
@@ -1471,16 +1473,24 @@ JSContext::sizeOfExcludingThis(mozilla::
     /*
      * There are other JSContext members that could be measured; the following
      * ones have been found by DMD to be worth measuring.  More stuff may be
      * added later.
      */
     return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
 }
 
+#ifdef DEBUG
+bool
+JSContext::inAtomsZone() const
+{
+    return zone_->isAtomsZone();
+}
+#endif
+
 void
 JSContext::trace(JSTracer* trc)
 {
     cycleDetectorVector().trace(trc);
     geckoProfiler().trace(trc);
 
     if (trc->isMarkingTracer() && realm_)
         realm_->mark();
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -20,16 +20,17 @@
 #include "vm/ErrorReporting.h"
 #include "vm/MallocProvider.h"
 #include "vm/Runtime.h"
 
 struct DtoaState;
 
 namespace js {
 
+class AutoAtomsZone;
 class AutoRealm;
 
 namespace jit {
 class JitContext;
 class DebugModeOSRVolatileJitFrameIter;
 } // namespace jit
 
 namespace gc {
@@ -194,66 +195,72 @@ struct JSContext : public JS::RootingCon
      * places in the VM cannot know that they were called from script (e.g.,
      * they may have been called through the JSAPI via JS_CallFunction) and thus
      * cannot expect there is a scripted caller.
      *
      * Realms should be entered/left in a LIFO fasion. The depth of this
      * enter/leave stack is maintained by enterRealmDepth_ and queried by
      * hasEnteredRealm.
      *
-     * To enter a compartment, code should prefer using AutoRealm over
+     * To enter a realm, code should prefer using AutoRealm over
      * manually calling cx->enterRealm/leaveRealm.
      */
   protected:
+#ifdef DEBUG
     js::ThreadData<unsigned> enterRealmDepth_;
+#endif
 
-    inline void setRealm(JS::Realm* realm,
-                         const js::AutoLockForExclusiveAccess* maybeLock = nullptr);
+    inline void setRealm(JS::Realm* realm);
   public:
+#ifdef DEBUG
     bool hasEnteredRealm() const {
         return enterRealmDepth_ > 0;
     }
-#ifdef DEBUG
     unsigned getEnterRealmDepth() const {
         return enterRealmDepth_;
     }
 #endif
 
   private:
-    // We distinguish between entering the atoms realm and all other realms.
-    // Entering the atoms realm requires a lock.
-    inline void enterNonAtomsRealm(JS::Realm* realm);
-    inline void enterAtomsRealm(JS::Realm* realm,
-                                const js::AutoLockForExclusiveAccess& lock);
+    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 enterNullRealm();
-    inline void leaveRealm(JS::Realm* oldRealm,
-                           const js::AutoLockForExclusiveAccess* maybeLock = nullptr);
+
+    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_; }
 
     bool isNurseryAllocSuppressed() const {
         return nurserySuppressions_;
     }
 
     // Threads may freely access any data in their compartment and zone.
     JSCompartment* compartment() const {
         return JS::GetCompartmentForRealm(realm_);
     }
     JS::Realm* realm() const {
         return realm_;
     }
+
+#ifdef DEBUG
+    bool inAtomsZone() const;
+#endif
+
     JS::Zone* zone() const {
-        MOZ_ASSERT_IF(!realm(), !zone_);
+        MOZ_ASSERT_IF(!realm() && zone_, inAtomsZone());
         MOZ_ASSERT_IF(realm(), js::GetCompartmentZone(GetCompartmentForRealm(realm())) == zone_);
         return zoneRaw();
     }
 
     // For use when the context's zone is being read by another thread and the
     // compartment and zone pointers might not be in sync.
     JS::Zone* zoneRaw() const {
         return zone_;
@@ -270,18 +277,18 @@ struct JSContext : public JS::RootingCon
     // Current global. This is only safe to use within the scope of the
     // AutoRealm from which it's called.
     inline js::Handle<js::GlobalObject*> global() const;
 
     // Methods to access runtime data that must be protected by locks.
     js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) {
         return runtime_->atoms(lock);
     }
-    JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) {
-        return runtime_->atomsRealm(lock);
+    const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) {
+        return runtime_->atomsZone(lock);
     }
     js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) {
         return runtime_->symbolRegistry(lock);
     }
     js::ScriptDataTable& scriptDataTable(js::AutoLockScriptData& lock) {
         return runtime_->scriptDataTable(lock);
     }
 
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -831,17 +831,17 @@ CollectRuntimeStatsHelper(JSContext* cx,
 #ifdef DEBUG
     // Check that the in-arena measurements look ok.
     size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
                             rtStats->zTotals.unusedGCThings.totalSize() +
                             rtStats->gcHeapGCThings;
     MOZ_ASSERT(totalArenaSize % gc::ArenaSize == 0);
 #endif
 
-    for (RealmsIter realm(rt, WithAtoms); !realm.done(); realm.next())
+    for (RealmsIter realm(rt); !realm.done(); realm.next())
         realm->nullRealmStats();
 
     size_t numDirtyChunks =
         (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
     size_t perChunkAdmin =
         sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
     rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
 
@@ -881,28 +881,28 @@ JS::CollectRuntimeStats(JSContext* cx, R
 {
     return CollectRuntimeStatsHelper(cx, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
 }
 
 JS_PUBLIC_API(size_t)
 JS::SystemRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) {
         if (realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::UserRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) {
         if (!realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::PeakSizeOfTemporary(const JSContext* cx)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -152,17 +152,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     thousandsSeparator(nullptr),
     decimalSeparator(nullptr),
     numGrouping(nullptr),
 #endif
     beingDestroyed_(false),
     allowContentJS_(true),
     atoms_(nullptr),
     atomsAddedWhileSweeping_(nullptr),
-    atomsRealm_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     jitSupportsFloatingPoint(false),
     jitSupportsUnalignedAccesses(false),
     jitSupportsSimd(false),
     offthreadIonCompilationEnabled_(true),
@@ -214,34 +213,18 @@ JSRuntime::init(JSContext* cx, uint32_t 
 
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
     ScopedJSDeletePtr<Zone> atomsZone(js_new<Zone>(this));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
-    JS::RealmOptions options;
-    ScopedJSDeletePtr<Realm> atomsRealm(js_new<Realm>(atomsZone.get(), options));
-    if (!atomsRealm || !atomsRealm->init(nullptr))
-        return false;
-
-    JSCompartment* atomsComp = atomsRealm->compartment();
-    if (!atomsComp->realms().append(atomsRealm))
-        return false;
-
     gc.atomsZone = atomsZone.get();
-    if (!atomsZone->compartments().append(atomsComp))
-        return false;
-
-    atomsRealm->setIsSystem(true);
-    atomsRealm->setIsAtomsRealm();
-
     atomsZone.forget();
-    this->atomsRealm_ = atomsRealm.forget();
 
     if (!symbolRegistry_.ref().init())
         return false;
 
     if (!scriptDataTable_.ref().init())
         return false;
 
     /* The garbage collector depends on everything before this point being initialized. */
@@ -328,17 +311,16 @@ JSRuntime::destroyRuntime()
      */
     FreeScriptData(this);
 
 #if !EXPOSE_INTL_API
     FinishRuntimeNumberState(this);
 #endif
 
     gc.finish();
-    atomsRealm_ = nullptr;
 
     js_delete(defaultFreeOp_.ref());
 
     js_free(defaultLocale);
     js_delete(jitRuntime_.ref());
 
 #ifdef DEBUG
     initialized_ = false;
@@ -432,17 +414,17 @@ JSRuntime::addSizeOfIncludingThis(mozill
 
     rtSizes->wasmRuntime += wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf);
 }
 
 static bool
 HandleInterrupt(JSContext* cx, bool invokeCallback)
 {
     MOZ_ASSERT(cx->requestDepth >= 1);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     cx->runtime()->gc.gcIfRequested();
 
     // A worker thread may have requested an interrupt after finishing an Ion
     // compilation.
     jit::AttachFinishedCompilations(cx);
 
     // Don't call the interrupt callback if we only interrupted for GC or Ion.
@@ -711,16 +693,30 @@ JSRuntime::randomHashCodeScrambler()
 
 mozilla::non_crypto::XorShift128PlusRNG
 JSRuntime::forkRandomKeyGenerator()
 {
     auto& rng = randomKeyGenerator();
     return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
 }
 
+js::HashNumber
+JSRuntime::randomHashCode()
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
+
+    if (randomHashCodeGenerator_.isNothing()) {
+        mozilla::Array<uint64_t, 2> seed;
+        GenerateXorShift128PlusSeed(seed);
+        randomHashCodeGenerator_.emplace(seed[0], seed[1]);
+    }
+
+    return HashNumber(randomHashCodeGenerator_->next());
+}
+
 void
 JSRuntime::updateMallocCounter(size_t nbytes)
 {
     gc.updateMallocCounter(nbytes);
 }
 
 JS_FRIEND_API(void*)
 JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr, JSContext* maybecx)
@@ -765,17 +761,17 @@ JSRuntime::onOutOfMemoryCanGC(AllocFunct
     if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION)
         OnLargeAllocationFailure();
     return onOutOfMemory(allocFunc, bytes, reallocPtr);
 }
 
 bool
 JSRuntime::activeGCInAtomsZone()
 {
-    Zone* zone = atomsRealm_->zone();
+    Zone* zone = unsafeAtomsZone();
     return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) ||
            zone->wasGCStarted();
 }
 
 bool
 JSRuntime::createAtomsAddedWhileSweepingTable()
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -563,20 +563,25 @@ struct JSRuntime : public js::MallocProv
         return !!jitRuntime_;
     }
 
   private:
     // Used to generate random keys for hash tables.
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
     mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
 
+    // Used to generate random hash codes for symbols.
+    mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomHashCodeGenerator_;
+
   public:
     mozilla::HashCodeScrambler randomHashCodeScrambler();
     mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
 
+    js::HashNumber randomHashCode();
+
     //-------------------------------------------------------------------------
     // Self-hosting support
     //-------------------------------------------------------------------------
 
     bool hasInitializedSelfHosting() const {
         return selfHostingGlobal_;
     }
 
@@ -694,21 +699,16 @@ struct JSRuntime : public js::MallocProv
     // Set of all atoms other than those in permanentAtoms and staticStrings.
     // Reading or writing this set requires the calling thread to use
     // AutoLockForExclusiveAccess.
     js::ExclusiveAccessLockOrGCTaskData<js::AtomSet*> atoms_;
 
     // Set of all atoms added while the main atoms table is being swept.
     js::ExclusiveAccessLockData<js::AtomSet*> atomsAddedWhileSweeping_;
 
-    // Realm and associated zone containing all atoms in the runtime, as
-    // well as runtime wide IonCode stubs. Modifying the contents of this
-    // zone requires the calling thread to use AutoLockForExclusiveAccess.
-    js::WriteOnceData<JS::Realm*> atomsRealm_;
-
     // Set of all live symbols produced by Symbol.for(). All such symbols are
     // allocated in the atomsZone. Reading or writing the symbol registry
     // requires the calling thread to use AutoLockForExclusiveAccess.
     js::ExclusiveAccessLockOrGCTaskData<js::SymbolRegistry> symbolRegistry_;
 
   public:
     bool initializeAtoms(JSContext* cx);
     void finishAtoms();
@@ -729,35 +729,26 @@ struct JSRuntime : public js::MallocProv
     }
 
     bool createAtomsAddedWhileSweepingTable();
     void destroyAtomsAddedWhileSweepingTable();
     js::AtomSet* atomsAddedWhileSweeping() {
         return atomsAddedWhileSweeping_;
     }
 
-    JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) {
-        return atomsRealm_;
-    }
-    JS::Realm* unsafeAtomsRealm() {
-        return atomsRealm_;
+    const JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) const {
+        return gc.atomsZone;
     }
-
-    // Note: once JS::Realm and JSCompartment are completely unrelated, the
-    // atoms realm probably won't have a compartment so we can remove this
-    // then.
-    bool isAtomsCompartment(JSCompartment* comp) {
-        return JS::GetRealmForCompartment(comp) == atomsRealm_;
+    JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) {
+        return gc.atomsZone;
     }
-
-    const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) const {
+    JS::Zone* unsafeAtomsZone() {
         return gc.atomsZone;
     }
 
-    // The atoms realm is the only one in its zone.
     bool isAtomsZone(const JS::Zone* zone) const {
         return zone == gc.atomsZone;
     }
 
     bool activeGCInAtomsZone();
 
     js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) {
         return symbolRegistry_.ref();
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -189,17 +189,17 @@ PerformanceMonitoring::monotonicReadTime
     return 0;
 #endif // defined(MOZ_HAVE_RDTSC)
 }
 
 void
 PerformanceMonitoring::dispose(JSRuntime* rt)
 {
     reset();
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->performanceMonitoring.unlink();
 }
 
 PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -224,17 +224,17 @@ MOZ_ALWAYS_INLINE JSFlatString*
 JSFlatString::new_(JSContext* cx, const CharT* chars, size_t length)
 {
     MOZ_ASSERT(chars[length] == CharT(0));
 
     if (!validateLength(cx, length))
         return nullptr;
 
     JSFlatString* str;
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         str = js::Allocate<js::NormalAtom, allowGC>(cx);
     else
         str = js::Allocate<JSFlatString, allowGC>(cx, js::gc::DefaultHeap);
     if (!str)
         return nullptr;
 
     if (!str->isTenured()) {
         // The chars pointer is only considered to be handed over to this
@@ -268,27 +268,27 @@ JSFlatString::toPropertyName(JSContext* 
         return nullptr;
     return atom->asPropertyName();
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSThinInlineString*
 JSThinInlineString::new_(JSContext* cx)
 {
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx));
 
     return js::Allocate<JSThinInlineString, allowGC>(cx, js::gc::DefaultHeap);
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSFatInlineString*
 JSFatInlineString::new_(JSContext* cx)
 {
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx));
 
     return js::Allocate<JSFatInlineString, allowGC>(cx, js::gc::DefaultHeap);
 }
 
 template<>
 MOZ_ALWAYS_INLINE JS::Latin1Char*
 JSThinInlineString::init<JS::Latin1Char>(size_t length)
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1082,17 +1082,17 @@ const StaticStrings::SmallChar StaticStr
 #undef R4
 #undef R6
 #undef R7
 
 bool
 StaticStrings::init(JSContext* cx)
 {
     AutoLockForExclusiveAccess lock(cx);
-    AutoAtomsRealm ar(cx, lock);
+    AutoAtomsZone az(cx, lock);
 
     static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR,
                   "Unit strings must fit in Latin1Char.");
 
     using Latin1Range = mozilla::Range<const Latin1Char>;
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         Latin1Char buffer[] = { Latin1Char(i), '\0' };
--- a/js/src/vm/SymbolType.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -17,17 +17,17 @@
 
 using JS::Symbol;
 using namespace js;
 
 Symbol*
 Symbol::newInternal(JSContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description,
                     AutoLockForExclusiveAccess& lock)
 {
-    MOZ_ASSERT(cx->realm() == cx->atomsRealm(lock));
+    MOZ_ASSERT(cx->zone() == cx->atomsZone(lock));
 
     // Following js::AtomizeString, we grudgingly forgo last-ditch GC here.
     Symbol* p = Allocate<JS::Symbol, NoGC>(cx);
     if (!p) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
     return new (p) Symbol(code, hash, description);
@@ -43,18 +43,18 @@ Symbol::new_(JSContext* cx, JS::SymbolCo
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
     // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     Symbol* sym;
     {
-        AutoAtomsRealm ar(cx, lock);
-        sym = newInternal(cx, code, cx->realm()->randomHashCode(), atom, lock);
+        AutoAtomsZone az(cx, lock);
+        sym = newInternal(cx, code, cx->runtime()->randomHashCode(), atom, lock);
     }
     if (sym)
         cx->markAtom(sym);
     return sym;
 }
 
 Symbol*
 Symbol::for_(JSContext* cx, HandleString description)
@@ -69,17 +69,17 @@ Symbol::for_(JSContext* cx, HandleString
     SymbolRegistry::AddPtr p = registry.lookupForAdd(atom);
     if (p) {
         cx->markAtom(*p);
         return *p;
     }
 
     Symbol* sym;
     {
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         // Rehash the hash of the atom to give the corresponding symbol a hash
         // that is different than the hash of the corresponding atom.
         HashNumber hash = mozilla::HashGeneric(atom->hash());
         sym = newInternal(cx, SymbolCode::InSymbolRegistry, hash, atom, lock);
         if (!sym)
             return nullptr;
 
         // p is still valid here because we have held the lock since the
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1073,19 +1073,17 @@ XPCJSRuntime::~XPCJSRuntime()
 
 // If |*anonymizeID| is non-zero and this is a user compartment, the name will
 // be anonymized.
 static void
 GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
                    bool replaceSlashes)
 {
     JS::Realm* realm = JS::GetRealmForCompartment(c);
-    if (js::IsAtomsRealm(realm)) {
-        name.AssignLiteral("atoms");
-    } else if (*anonymizeID && !js::IsSystemCompartment(c)) {
+    if (*anonymizeID && !js::IsSystemCompartment(c)) {
         name.AppendPrintf("<anonymized-%d>", *anonymizeID);
         *anonymizeID += 1;
     } else if (JSPrincipals* principals = JS::GetRealmPrincipals(realm)) {
         nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
         if (NS_FAILED(rv)) {
             name.AssignLiteral("(unknown)");
         }
 
@@ -2159,30 +2157,33 @@ class XPCJSRuntimeStats : public JS::Run
         for (size_t i = 0; i != realmStatsVector.length(); ++i)
             delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
 
         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
             delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
     }
 
     virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override {
-        // Get some global in this zone.
         AutoSafeJSContext cx;
-        Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone));
         xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
         extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
-        RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
-        if (global) {
-            RefPtr<nsGlobalWindowInner> window;
-            if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
-                // The global is a |window| object.  Use the path prefix that
-                // we should have already created for it.
-                if (mTopWindowPaths->Get(window->WindowID(),
-                                         &extras->pathPrefix))
-                    extras->pathPrefix.AppendLiteral("/js-");
+
+        // Get some global in this zone.
+        Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone));
+        if (realm) {
+            RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
+            if (global) {
+                RefPtr<nsGlobalWindowInner> window;
+                if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
+                    // The global is a |window| object.  Use the path prefix that
+                    // we should have already created for it.
+                    if (mTopWindowPaths->Get(window->WindowID(),
+                                             &extras->pathPrefix))
+                        extras->pathPrefix.AppendLiteral("/js-");
+                }
             }
         }
 
         extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
 
         MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
 
         zStats->extra = extras;