Bug 1337112 - Remove links from JSRuntime to its single context and zone group, r=jandem,jonco.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 10 Feb 2017 16:47:50 -0700
changeset 371429 920d5dfea5de9b79d5e11e7e9384738270dec833
parent 371428 8a2f028e6943d68259be6e3082f07acd67c3b085
child 371430 6eb7c1487c1f927e9215cdec61ea0ef26498d96d
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, jonco
bugs1337112
milestone54.0a1
Bug 1337112 - Remove links from JSRuntime to its single context and zone group, r=jandem,jonco.
dom/base/nsFrameMessageManager.cpp
dom/base/nsGlobalWindow.cpp
dom/bindings/SimpleGlobalObject.cpp
dom/workers/RuntimeService.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/builtin/TestingFunctions.cpp
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/Zone.h
js/src/gc/ZoneGroup.cpp
js/src/gc/ZoneGroup.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/shell/js.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/SelfHosting.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCShellImpl.cpp
netwerk/base/ProxyAutoConfig.cpp
xpcom/base/CycleCollectedJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1650,17 +1650,17 @@ nsMessageManagerScriptExecutor::InitChil
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
 
   JS::CompartmentOptions options;
-  options.creationOptions().setZone(JS::SystemZone);
+  options.creationOptions().setSystemZone();
   options.behaviors().setVersion(JSVERSION_LATEST);
 
   if (xpc::SharedMemoryEnabled()) {
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
   }
 
   nsresult rv =
     xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2731,17 +2731,17 @@ CreateNativeGlobalForInner(JSContext* aC
   // Sometimes add-ons load their own XUL windows, either as separate top-level
   // windows or inside a browser element. In such cases we want to tag the
   // window's compartment with the add-on ID. See bug 1092156.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     options.creationOptions().setAddonId(MapURIToAddonID(aURI));
   }
 
   if (top && top->GetGlobalJSObject()) {
-    options.creationOptions().setSameZoneAs(top->GetGlobalJSObject());
+    options.creationOptions().setExistingZone(top->GetGlobalJSObject());
   }
 
   options.creationOptions().setSecureContext(aIsSecureContext);
 
   xpc::InitGlobalObjectOptions(options, aPrincipal);
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -105,17 +105,17 @@ SimpleGlobalObject::Create(GlobalType gl
 
     JS::CompartmentOptions options;
     options.creationOptions()
            .setInvisibleToDebugger(true)
            // Put our SimpleGlobalObjects in the system zone, so we won't create
            // lots of zones for what are probably very short-lived
            // compartments.  This should help them be GCed quicker and take up
            // less memory before they're GCed.
-          .setZone(JS::SystemZone);
+           .setSystemZone();
 
     if (NS_IsMainThread()) {
       nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
       options.creationOptions().setTrace(xpc::TraceXPCGlobal);
       global = xpc::CreateGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
                                        nsJSPrincipals::get(principal),
                                        options);
     } else {
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1070,20 +1070,20 @@ public:
     // in cycles that were broken during CC shutdown.
     nsCycleCollector_shutdown();
 
     // The CC is shut down, and the superclass destructor will GC, so make sure
     // we don't try to CC again.
     mWorkerPrivate = nullptr;
   }
 
-  nsresult Initialize(JSContext* aParentContext)
+  nsresult Initialize(JSRuntime* aParentRuntime)
   {
     nsresult rv =
-      CycleCollectedJSContext::Initialize(aParentContext,
+      CycleCollectedJSContext::Initialize(aParentRuntime,
                                           WORKER_DEFAULT_RUNTIME_HEAPSIZE,
                                           WORKER_DEFAULT_NURSERY_SIZE);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
 
     JSContext* cx = Context();
 
@@ -1182,17 +1182,17 @@ public:
 private:
   WorkerPrivate* mWorkerPrivate;
 };
 
 class WorkerThreadPrimaryRunnable final : public Runnable
 {
   WorkerPrivate* mWorkerPrivate;
   RefPtr<WorkerThread> mThread;
-  JSContext* mParentContext;
+  JSRuntime* mParentRuntime;
 
   class FinishedRunnable final : public Runnable
   {
     RefPtr<WorkerThread> mThread;
 
   public:
     explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
     : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable")
@@ -1208,18 +1208,18 @@ class WorkerThreadPrimaryRunnable final 
     { }
 
     NS_DECL_NSIRUNNABLE
   };
 
 public:
   WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
                               WorkerThread* aThread,
-                              JSContext* aParentContext)
-  : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentContext(aParentContext)
+                              JSRuntime* aParentRuntime)
+  : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aThread);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
@@ -1888,17 +1888,17 @@ RuntimeService::ScheduleWorker(WorkerPri
 
   if (NS_FAILED(thread->SetPriority(priority))) {
     NS_WARNING("Could not set the thread's priority!");
   }
 
   JSContext* cx = CycleCollectedJSContext::Get()->Context();
   nsCOMPtr<nsIRunnable> runnable =
     new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread,
-                                    JS_GetParentContext(cx));
+                                    JS_GetParentRuntime(cx));
   if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
     UnregisterWorker(aWorkerPrivate);
     return false;
   }
 
   return true;
 }
 
@@ -2856,17 +2856,17 @@ WorkerThreadPrimaryRunnable::Run()
   SetThreadHelper threadHelper(mWorkerPrivate, mThread);
 
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   {
     nsCycleCollector_startup();
 
     WorkerJSContext context(mWorkerPrivate);
-    nsresult rv = context.Initialize(mParentContext);
+    nsresult rv = context.Initialize(mParentRuntime);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     JSContext* cx = context.Context();
 
     if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
       // XXX need to fire an error at parent.
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -446,17 +446,17 @@ XPCShellEnvironment::Init()
     RefPtr<BackstagePass> backstagePass;
     rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
     if (NS_FAILED(rv)) {
         NS_ERROR("Failed to create backstage pass!");
         return false;
     }
 
     JS::CompartmentOptions options;
-    options.creationOptions().setZone(JS::SystemZone);
+    options.creationOptions().setSystemZone();
     options.behaviors().setVersion(JSVERSION_LATEST);
     if (xpc::SharedMemoryEnabled())
         options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = xpc->InitClassesWithNewWrappedGlobal(cx,
                                               static_cast<nsIGlobalObject *>(backstagePass),
                                               principal, 0,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -342,16 +342,17 @@ MinorGC(JSContext* cx, unsigned argc, Va
     cx->minorGC(JS::gcreason::API);
     args.rval().setUndefined();
     return true;
 }
 
 #define FOR_EACH_GC_PARAM(_)                                                    \
     _("maxBytes",                   JSGC_MAX_BYTES,                      true)  \
     _("maxMallocBytes",             JSGC_MAX_MALLOC_BYTES,               true)  \
+    _("maxNurseryBytes",            JSGC_MAX_NURSERY_BYTES,              true)  \
     _("gcBytes",                    JSGC_BYTES,                          false) \
     _("gcNumber",                   JSGC_NUMBER,                         false) \
     _("mode",                       JSGC_MODE,                           true)  \
     _("unusedChunks",               JSGC_UNUSED_CHUNKS,                  false) \
     _("totalChunks",                JSGC_TOTAL_CHUNKS,                   false) \
     _("sliceTimeBudget",            JSGC_SLICE_TIME_BUDGET,              true)  \
     _("markStackLimit",             JSGC_MARK_STACK_LIMIT,               true)  \
     _("highFrequencyTimeLimit",     JSGC_HIGH_FREQUENCY_TIME_LIMIT,      true)  \
@@ -416,19 +417,26 @@ GCParameter(JSContext* cx, unsigned argc
         return true;
     }
 
     if (!info.writable) {
         JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s", info.name);
         return false;
     }
 
-    if (disableOOMFunctions && (param == JSGC_MAX_BYTES || param == JSGC_MAX_MALLOC_BYTES)) {
-        args.rval().setUndefined();
-        return true;
+    if (disableOOMFunctions) {
+        switch (param) {
+          case JSGC_MAX_BYTES:
+          case JSGC_MAX_MALLOC_BYTES:
+          case JSGC_MAX_NURSERY_BYTES:
+            args.rval().setUndefined();
+            return true;
+          default:
+            break;
+        }
     }
 
     double d;
     if (!ToNumber(cx, args[1], &d))
         return false;
 
     if (d < 0 || d > UINT32_MAX) {
         JS_ReportErrorASCII(cx, "Parameter value out of range");
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -975,18 +975,19 @@ class GCRuntime
 
     void callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const;
     void callWeakPointerZoneGroupCallbacks() const;
     void callWeakPointerCompartmentCallbacks(JSCompartment* comp) const;
 
   public:
     JSRuntime* const rt;
 
-    /* Embedders can use this zone however they wish. */
+    /* Embedders can use this zone and group however they wish. */
     UnprotectedData<JS::Zone*> systemZone;
+    UnprotectedData<ZoneGroup*> systemZoneGroup;
 
     // List of all zone groups (protected by the GC lock).
     ActiveThreadOrGCTaskData<ZoneGroupVector> groups;
 
     // The unique atoms zone, which has no zone group.
     WriteOnceData<Zone*> atomsZone;
 
   private:
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -548,17 +548,16 @@ js::Nursery::maybeEndProfile(ProfileKey 
     if (enableProfiling_)
         endProfile(key);
 }
 
 void
 js::Nursery::collect(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
-    MOZ_RELEASE_ASSERT(zoneGroup()->ownedByCurrentThread());
 
     if (!isEnabled() || isEmpty()) {
         // Our barriers are not always exact, and there may be entries in the
         // storebuffer even when the nursery is disabled or empty. It's not safe
         // to keep these entries as they may refer to tenured cells which may be
         // freed after this point.
         zoneGroup()->storeBuffer().clear();
     }
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -557,20 +557,16 @@ struct Zone : public JS::shadow::Zone,
         for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) {
             MOZ_ASSERT(!uniqueIds().has(e.front().key()));
             if (!uniqueIds().put(e.front().key(), e.front().value()))
                 oomUnsafe.crash("failed to transfer unique ids from off-main-thread");
         }
         source->uniqueIds().clear();
     }
 
-    JSContext* contextFromMainThread() {
-        return runtime_->contextFromMainThread();
-    }
-
 #ifdef JSGC_HASH_TABLE_CHECKS
     // Assert that the UniqueId table has been redirected successfully.
     void checkUniqueIdTableAfterMovingGC();
 #endif
 
     bool keepShapeTables() const {
         return keepShapeTables_;
     }
--- a/js/src/gc/ZoneGroup.cpp
+++ b/js/src/gc/ZoneGroup.cpp
@@ -43,28 +43,33 @@ ZoneGroup::init(size_t maxNurseryBytes)
         return false;
 
     return true;
 }
 
 ZoneGroup::~ZoneGroup()
 {
     js_delete(jitZoneGroup.ref());
+
+    if (this == runtime->gc.systemZoneGroup)
+        runtime->gc.systemZoneGroup = nullptr;
 }
 
 void
 ZoneGroup::enter()
 {
     JSContext* cx = TlsContext.get();
     if (ownerContext().context() == cx) {
         MOZ_ASSERT(enterCount);
     } else {
         MOZ_ASSERT(ownerContext().context() == nullptr);
         MOZ_ASSERT(enterCount == 0);
         ownerContext_ = CooperatingContext(cx);
+        if (cx->generationalDisabled)
+            nursery().disable();
     }
     enterCount++;
 }
 
 void
 ZoneGroup::leave()
 {
     MOZ_ASSERT(ownedByCurrentThread());
--- a/js/src/gc/ZoneGroup.h
+++ b/js/src/gc/ZoneGroup.h
@@ -125,47 +125,11 @@ class ZoneGroup
 
   private:
     /* Linked list of all Debugger objects in the group. */
     ZoneGroupData<mozilla::LinkedList<js::Debugger>> debuggerList_;
   public:
     mozilla::LinkedList<js::Debugger>& debuggerList() { return debuggerList_.ref(); }
 };
 
-class MOZ_RAII AutoAccessZoneGroup
-{
-    ZoneGroup* group;
-
-  public:
-    explicit AutoAccessZoneGroup(ZoneGroup* group)
-      : group(group)
-    {
-        group->enter();
-    }
-
-    ~AutoAccessZoneGroup() {
-        group->leave();
-    }
-};
-
-class MOZ_RAII AutoAccessZoneGroups
-{
-    Vector<ZoneGroup*, 4, SystemAllocPolicy> acquiredGroups;
-
-  public:
-    AutoAccessZoneGroups() {}
-
-    ~AutoAccessZoneGroups() {
-        for (size_t i = 0; i < acquiredGroups.length(); i++)
-            acquiredGroups[i]->leave();
-    }
-
-    void access(ZoneGroup* group) {
-        group->enter();
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!acquiredGroups.append(group))
-            oomUnsafe.crash("acquiredGroups.append failed");
-    }
-};
-
 } // namespace js
 
 #endif // gc_Zone_h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -458,28 +458,24 @@ JS::isGCEnabled()
 {
     return !TlsContext.get()->suppressGC;
 }
 #else
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 #endif
 
 JS_PUBLIC_API(JSContext*)
-JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSContext* parentContext)
+JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
                "must call JS_Init prior to creating any JSContexts");
 
     // Make sure that all parent runtimes are the topmost parent.
-    JSRuntime* parentRuntime = nullptr;
-    if (parentContext) {
-        parentRuntime = parentContext->runtime();
-        while (parentRuntime && parentRuntime->parentRuntime)
-            parentRuntime = parentRuntime->parentRuntime;
-    }
+    while (parentRuntime && parentRuntime->parentRuntime)
+        parentRuntime = parentRuntime->parentRuntime;
 
     return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx)
 {
     DestroyContext(cx);
@@ -541,20 +537,20 @@ JS_BeginRequest(JSContext* cx)
 JS_PUBLIC_API(void)
 JS_EndRequest(JSContext* cx)
 {
     MOZ_ASSERT(cx->outstandingRequests != 0);
     cx->outstandingRequests--;
     StopRequest(cx);
 }
 
-JS_PUBLIC_API(JSContext*)
-JS_GetParentContext(JSContext* cx)
-{
-    return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime->unsafeContextFromAnyThread() : cx;
+JS_PUBLIC_API(JSRuntime*)
+JS_GetParentRuntime(JSContext* cx)
+{
+    return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime : cx->runtime();
 }
 
 JS_PUBLIC_API(JSVersion)
 JS_GetVersion(JSContext* cx)
 {
     return VersionNumber(cx->findVersion());
 }
 
@@ -1738,26 +1734,52 @@ JS_GetConstructor(JSContext* cx, HandleO
 
 bool
 JS::CompartmentBehaviors::extraWarnings(JSContext* cx) const
 {
     return extraWarningsOverride_.get(cx->options().extraWarnings());
 }
 
 JS::CompartmentCreationOptions&
-JS::CompartmentCreationOptions::setZone(ZoneSpecifier spec)
-{
-    zone_.spec = spec;
+JS::CompartmentCreationOptions::setSystemZone()
+{
+    zoneSpec_ = JS::SystemZone;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setExistingZone(JSObject* obj)
+{
+    zoneSpec_ = JS::ExistingZone;
+    zonePointer_ = obj->zone();
     return *this;
 }
 
 JS::CompartmentCreationOptions&
-JS::CompartmentCreationOptions::setSameZoneAs(JSObject* obj)
-{
-    zone_.pointer = static_cast<void*>(obj->zone());
+JS::CompartmentCreationOptions::setNewZoneInNewZoneGroup()
+{
+    zoneSpec_ = JS::NewZoneInNewZoneGroup;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setNewZoneInSystemZoneGroup()
+{
+    zoneSpec_ = JS::NewZoneInSystemZoneGroup;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setNewZoneInExistingZoneGroup(JSObject* obj)
+{
+    zoneSpec_ = JS::NewZoneInExistingZoneGroup;
+    zonePointer_ = obj->zone()->group();
     return *this;
 }
 
 const JS::CompartmentCreationOptions&
 JS::CompartmentCreationOptionsRef(JSCompartment* compartment)
 {
     return compartment->creationOptions();
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -976,29 +976,29 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * be created, in a single-threaded fashion.  Otherwise the behavior of the
  * library is undefined.
  * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
  */
 
 extern JS_PUBLIC_API(JSContext*)
 JS_NewContext(uint32_t maxbytes,
               uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
-              JSContext* parentContext = nullptr);
+              JSRuntime* parentRuntime = nullptr);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx);
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx);
 
 JS_PUBLIC_API(void)
 JS_SetContextPrivate(JSContext* cx, void* data);
 
-extern JS_PUBLIC_API(JSContext*)
-JS_GetParentContext(JSContext* cx);
+extern JS_PUBLIC_API(JSRuntime*)
+JS_GetParentRuntime(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_BeginRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
@@ -1705,16 +1705,19 @@ JS_UpdateWeakPointerAfterGCUnbarriered(J
 
 typedef enum JSGCParamKey {
     /** Maximum nominal heap before last ditch GC. */
     JSGC_MAX_BYTES          = 0,
 
     /** Number of JS_malloc bytes before last ditch GC. */
     JSGC_MAX_MALLOC_BYTES   = 1,
 
+    /** Maximum size of the generational GC nurseries. */
+    JSGC_MAX_NURSERY_BYTES  = 2,
+
     /** Amount of bytes allocated by the GC. */
     JSGC_BYTES = 3,
 
     /** Number of times GC has been invoked. Includes both major and minor GC. */
     JSGC_NUMBER = 4,
 
     /** Select GC mode. */
     JSGC_MODE = 6,
@@ -2173,45 +2176,59 @@ extern JS_PUBLIC_API(void*)
 JS_GetInstancePrivate(JSContext* cx, JS::Handle<JSObject*> obj, const JSClass* clasp,
                       JS::CallArgs* args);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_GetConstructor(JSContext* cx, JS::Handle<JSObject*> proto);
 
 namespace JS {
 
+// Specification for which zone a newly created compartment should use.
 enum ZoneSpecifier {
-    FreshZone = 0,
-    SystemZone = 1
+    // Use the single runtime wide system zone. The meaning of this zone is
+    // left to the embedder.
+    SystemZone,
+
+    // Use a particular existing zone.
+    ExistingZone,
+
+    // Create a new zone with its own new zone group.
+    NewZoneInNewZoneGroup,
+
+    // Create a new zone in the same zone group as the system zone.
+    NewZoneInSystemZoneGroup,
+
+    // Create a new zone in the same zone group as another existing zone.
+    NewZoneInExistingZoneGroup
 };
 
 /**
  * CompartmentCreationOptions specifies options relevant to creating a new
  * compartment, that are either immutable characteristics of that compartment
  * or that are discarded after the compartment has been created.
  *
  * Access to these options on an existing compartment is read-only: if you
  * need particular selections, make them before you create the compartment.
  */
 class JS_PUBLIC_API(CompartmentCreationOptions)
 {
   public:
     CompartmentCreationOptions()
       : addonId_(nullptr),
         traceGlobal_(nullptr),
+        zoneSpec_(NewZoneInSystemZoneGroup),
+        zonePointer_(nullptr),
         invisibleToDebugger_(false),
         mergeable_(false),
         preserveJitCode_(false),
         cloneSingletons_(false),
         experimentalNumberFormatFormatToPartsEnabled_(false),
         sharedMemoryAndAtomics_(false),
         secureContext_(false)
-    {
-        zone_.spec = JS::FreshZone;
-    }
+    {}
 
     // A null add-on ID means that the compartment is not associated with an
     // add-on.
     JSAddonId* addonIdOrNull() const { return addonId_; }
     CompartmentCreationOptions& setAddonId(JSAddonId* id) {
         addonId_ = id;
         return *this;
     }
@@ -2219,23 +2236,25 @@ class JS_PUBLIC_API(CompartmentCreationO
     JSTraceOp getTrace() const {
         return traceGlobal_;
     }
     CompartmentCreationOptions& setTrace(JSTraceOp op) {
         traceGlobal_ = op;
         return *this;
     }
 
-    void* zonePointer() const {
-        MOZ_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
-        return zone_.pointer;
-    }
-    ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
-    CompartmentCreationOptions& setZone(ZoneSpecifier spec);
-    CompartmentCreationOptions& setSameZoneAs(JSObject* obj);
+    void* zonePointer() const { return zonePointer_; }
+    ZoneSpecifier zoneSpecifier() const { return zoneSpec_; }
+
+    // Set the zone to use for the compartment. See ZoneSpecifier above.
+    CompartmentCreationOptions& setSystemZone();
+    CompartmentCreationOptions& setExistingZone(JSObject* obj);
+    CompartmentCreationOptions& setNewZoneInNewZoneGroup();
+    CompartmentCreationOptions& setNewZoneInSystemZoneGroup();
+    CompartmentCreationOptions& setNewZoneInExistingZoneGroup(JSObject* obj);
 
     // Certain scopes (i.e. XBL compilation scopes) are implementation details
     // of the embedding, and references to them should never leak out to script.
     // This flag causes the this compartment to skip firing onNewGlobalObject
     // and makes addDebuggee a no-op for this global.
     bool invisibleToDebugger() const { return invisibleToDebugger_; }
     CompartmentCreationOptions& setInvisibleToDebugger(bool flag) {
         invisibleToDebugger_ = flag;
@@ -2294,20 +2313,18 @@ class JS_PUBLIC_API(CompartmentCreationO
     CompartmentCreationOptions& setSecureContext(bool flag) {
         secureContext_ = flag;
         return *this;
     }
 
   private:
     JSAddonId* addonId_;
     JSTraceOp traceGlobal_;
-    union {
-        ZoneSpecifier spec;
-        void* pointer; // js::Zone* is not exposed in the API.
-    } zone_;
+    ZoneSpecifier zoneSpec_;
+    void* zonePointer_; // Per zoneSpec_, either a Zone, ZoneGroup, or null.
     bool invisibleToDebugger_;
     bool mergeable_;
     bool preserveJitCode_;
     bool cloneSingletons_;
     bool experimentalNumberFormatFormatToPartsEnabled_;
     bool sharedMemoryAndAtomics_;
     bool secureContext_;
 };
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -171,20 +171,38 @@ js::DestroyContext(JSContext* cx)
 {
     JS_AbortIfWrongThread(cx);
 
     if (cx->outstandingRequests != 0)
         MOZ_CRASH("Attempted to destroy a context while it is in a request.");
 
     cx->checkNoGCRooters();
 
-    js_delete(cx->ionPcScriptCache.ref());
+    // Cancel all off thread Ion compiles before destroying a cooperative
+    // context. Completed Ion compiles may try to interrupt arbitrary
+    // cooperative contexts which they have read off the owner context of a
+    // zone group. See HelperThread::handleIonWorkload.
+    CancelOffThreadIonCompile(cx->runtime());
 
-    cx->runtime()->destroyRuntime();
-    js_delete(cx->runtime());
+    if (cx->runtime()->cooperatingContexts().length() == 1) {
+        // Destroy the runtime along with its last context.
+        cx->runtime()->destroyRuntime();
+        js_delete(cx->runtime());
+    } else {
+        DebugOnly<bool> found = false;
+        for (size_t i = 0; i < cx->runtime()->cooperatingContexts().length(); i++) {
+            CooperatingContext& target = cx->runtime()->cooperatingContexts()[i];
+            if (cx == target.context()) {
+                cx->runtime()->cooperatingContexts().erase(&target);
+                found = true;
+                break;
+            }
+        }
+        MOZ_ASSERT(found);
+    }
 
     js_delete_poison(cx);
 }
 
 void
 JS::RootingContext::checkNoGCRooters() {
 #ifdef DEBUG
     for (auto const& stackRootPtr : stackRoots_)
@@ -1029,16 +1047,18 @@ JSContext::~JSContext()
 #ifdef XP_WIN
     if (threadNative_)
         CloseHandle((HANDLE)threadNative_.ref());
 #endif
 
     /* Free the stuff hanging off of cx. */
     MOZ_ASSERT(!resolvingList);
 
+    js_delete(ionPcScriptCache.ref());
+
     if (dtoaState)
         DestroyDtoaState(dtoaState);
 
     fx.destroyInstance();
     freeOsrTempData();
 
 #ifdef JS_SIMULATOR
     js::jit::Simulator::Destroy(simulator_);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -401,20 +401,16 @@ struct JSCompartment
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     JSRuntime* runtimeFromAnyThread() const {
         return runtime_;
     }
 
-    JSContext* contextFromMainThread() const {
-        return runtime_->contextFromMainThread();
-    }
-
     /*
      * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
      * (b) the compartment's global has been collected.  The latter can happen
      * if e.g. a string in a compartment is rooted but no object is, and thus
      * the global isn't rooted, and thus the global can be finalized while the
      * compartment lives on.
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -805,16 +805,17 @@ GCRuntime::releaseArena(Arena* arena, co
     if (isBackgroundSweeping())
         arena->zone->threshold.updateForRemovedArena(tunables);
     return arena->chunk()->releaseArena(rt, arena, lock);
 }
 
 GCRuntime::GCRuntime(JSRuntime* rt) :
     rt(rt),
     systemZone(nullptr),
+    systemZoneGroup(nullptr),
     atomsZone(nullptr),
     stats_(rt),
     marker(rt),
     usage(nullptr),
     mMemProfiler(rt),
     maxMallocBytes(0),
     nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
     numArenasFreeCommitted(0),
@@ -1039,16 +1040,17 @@ GCRuntime::init(uint32_t maxbytes, uint3
     {
         AutoLockGC lock(rt);
 
         /*
          * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
          * for default backward API compatibility.
          */
         MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock));
+        MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_NURSERY_BYTES, maxNurseryBytes, lock));
         setMaxMallocBytes(maxbytes);
 
         const char* size = getenv("JSGC_MARK_STACK_LIMIT");
         if (size)
             setMarkStackLimit(atoi(size), lock);
 
         jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD;
     }
@@ -1160,16 +1162,19 @@ GCSchedulingTunables::setParameter(JSGCP
 {
     // Limit heap growth factor to one hundred times size of current heap.
     const double MaxHeapGrowthFactor = 100;
 
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = value;
         break;
+      case JSGC_MAX_NURSERY_BYTES:
+        gcMaxNurseryBytes_ = value;
+        break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT: {
         uint64_t newLimit = (uint64_t)value * 1024 * 1024;
         if (newLimit == UINT64_MAX)
             return false;
         highFrequencyLowLimitBytes_ = newLimit;
@@ -3545,19 +3550,17 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, 
     ZoneGroup** read = groups.ref().begin();
     ZoneGroup** end = groups.ref().end();
     ZoneGroup** write = read;
 
     while (read < end) {
         ZoneGroup* group = *read++;
         sweepZones(fop, group, destroyingRuntime);
 
-        // For now, the singleton zone group is not destroyed until the runtime
-        // itself is, bug 1323066.
-        if (group->zones().empty() && group != rt->zoneGroupFromMainThread()) {
+        if (group->zones().empty()) {
             MOZ_ASSERT(numActiveZoneIters == 0);
             fop->delete_(group);
         } else {
             *write++ = group;
         }
     }
     groups.ref().shrinkTo(write - groups.ref().begin());
 }
@@ -6164,17 +6167,16 @@ GCRuntime::budgetIncrementalGC(bool noni
     return IncrementalResult::Ok;
 }
 
 namespace {
 
 class AutoScheduleZonesForGC
 {
     JSRuntime* rt_;
-    AutoAccessZoneGroups aazg;
 
   public:
     explicit AutoScheduleZonesForGC(JSRuntime* rt) : rt_(rt) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             if (rt->gc.gcMode() == JSGC_MODE_GLOBAL)
                 zone->scheduleGC();
 
             /* This is a heuristic to avoid resets. */
@@ -6182,19 +6184,16 @@ class AutoScheduleZonesForGC
                 zone->scheduleGC();
 
             /* This is a heuristic to reduce the total number of collections. */
             if (zone->usage.gcBytes() >=
                 zone->threshold.allocTrigger(rt->gc.schedulingState.inHighFrequencyGCMode()))
             {
                 zone->scheduleGC();
             }
-
-            if (zone->isGCScheduled() && !zone->isAtomsZone())
-                aazg.access(zone->group());
         }
     }
 
     ~AutoScheduleZonesForGC() {
         for (ZonesIter zone(rt_, WithAtoms); !zone.done(); zone.next())
             zone->unscheduleGC();
     }
 };
@@ -6748,25 +6747,71 @@ js::gc::FinishGC(JSContext* cx)
 
 AutoPrepareForTracing::AutoPrepareForTracing(JSContext* cx, ZoneSelector selector)
 {
     js::gc::FinishGC(cx);
     session_.emplace(cx->runtime());
 }
 
 JSCompartment*
-js::NewCompartment(JSContext* cx, Zone* zone, JSPrincipals* principals,
+js::NewCompartment(JSContext* cx, JSPrincipals* principals,
                    const JS::CompartmentOptions& options)
 {
     JSRuntime* rt = cx->runtime();
     JS_AbortIfWrongThread(cx);
 
+    ScopedJSDeletePtr<ZoneGroup> groupHolder;
     ScopedJSDeletePtr<Zone> zoneHolder;
+
+    Zone* zone = nullptr;
+    ZoneGroup* group = nullptr;
+    JS::ZoneSpecifier zoneSpec = options.creationOptions().zoneSpecifier();
+    switch (zoneSpec) {
+      case JS::SystemZone:
+        // systemZone and possibly systemZoneGroup might be null here, in which
+        // case we'll make a zone/group and set these fields below.
+        zone = rt->gc.systemZone;
+        group = rt->gc.systemZoneGroup;
+        break;
+      case JS::ExistingZone:
+        zone = static_cast<Zone*>(options.creationOptions().zonePointer());
+        MOZ_ASSERT(zone);
+        group = zone->group();
+        break;
+      case JS::NewZoneInNewZoneGroup:
+        break;
+      case JS::NewZoneInSystemZoneGroup:
+        // As above, systemZoneGroup might be null here.
+        group = rt->gc.systemZoneGroup;
+        break;
+      case JS::NewZoneInExistingZoneGroup:
+        group = static_cast<ZoneGroup*>(options.creationOptions().zonePointer());
+        MOZ_ASSERT(group);
+        break;
+    }
+
+    if (!group) {
+        MOZ_ASSERT(!zone);
+        group = cx->new_<ZoneGroup>(rt);
+        if (!group)
+            return nullptr;
+
+        groupHolder.reset(group);
+
+        if (!group->init(rt->gc.tunables.gcMaxNurseryBytes())) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        if (cx->generationalDisabled)
+            group->nursery().disable();
+    }
+
     if (!zone) {
-        zone = cx->new_<Zone>(cx->runtime(), rt->zoneGroupFromMainThread());
+        zone = cx->new_<Zone>(cx->runtime(), group);
         if (!zone)
             return nullptr;
 
         zoneHolder.reset(zone);
 
         const JSPrincipals* trusted = rt->trustedPrincipals();
         bool isSystem = principals && principals == trusted;
         if (!zone->init(isSystem)) {
@@ -6784,22 +6829,45 @@ js::NewCompartment(JSContext* cx, Zone* 
 
     AutoLockGC lock(rt);
 
     if (!zone->compartments().append(compartment.get())) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    if (zoneHolder && zoneHolder->group() && !zoneHolder->group()->zones().append(zone)) {
-        ReportOutOfMemory(cx);
-        return nullptr;
+    if (zoneHolder) {
+        if (!group->zones().append(zone)) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        // Lazily set the runtime's sytem zone.
+        if (zoneSpec == JS::SystemZone) {
+            MOZ_RELEASE_ASSERT(!rt->gc.systemZone);
+            rt->gc.systemZone = zone;
+            zone->isSystem = true;
+        }
+    }
+
+    if (groupHolder) {
+        if (!rt->gc.groups.ref().append(group)) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        // Lazily set the runtime's system zone group.
+        if (zoneSpec == JS::SystemZone || zoneSpec == JS::NewZoneInSystemZoneGroup) {
+            MOZ_RELEASE_ASSERT(!rt->gc.systemZoneGroup);
+            rt->gc.systemZoneGroup = group;
+        }
     }
 
     zoneHolder.forget();
+    groupHolder.forget();
     return compartment.forget();
 }
 
 void
 gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
 {
     // The source compartment must be specifically flagged as mergable.  This
     // also implies that the compartment is not visible to the debugger.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1052,17 +1052,17 @@ typedef void (*IterateScriptCallback)(JS
 extern void
 IterateScripts(JSContext* cx, JSCompartment* compartment,
                void* data, IterateScriptCallback scriptCallback);
 
 extern void
 FinalizeStringRT(JSRuntime* rt, JSString* str);
 
 JSCompartment*
-NewCompartment(JSContext* cx, JS::Zone* zone, JSPrincipals* principals,
+NewCompartment(JSContext* cx, JSPrincipals* principals,
                const JS::CompartmentOptions& options);
 
 namespace gc {
 
 /*
  * Merge all contents of source into target. This can only be used if source is
  * the only compartment in its zone.
  */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3359,37 +3359,37 @@ EvalInContext(JSContext* cx, unsigned ar
     if (!cx->compartment()->wrap(cx, args.rval()))
         return false;
 
     return true;
 }
 
 struct WorkerInput
 {
-    JSContext* context;
+    JSRuntime* parentRuntime;
     char16_t* chars;
     size_t length;
 
-    WorkerInput(JSContext* context, char16_t* chars, size_t length)
-      : context(context), chars(chars), length(length)
+    WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
+      : parentRuntime(parentRuntime), chars(chars), length(length)
     {}
 
     ~WorkerInput() {
         js_free(chars);
     }
 };
 
 static void SetWorkerContextOptions(JSContext* cx);
 
 static void
 WorkerMain(void* arg)
 {
     WorkerInput* input = (WorkerInput*) arg;
 
-    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->context);
+    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime);
     if (!cx) {
         js_delete(input);
         return;
     }
 
     UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
     if (!sc) {
         JS_DestroyContext(cx);
@@ -3508,17 +3508,17 @@ EvalInWorker(JSContext* cx, unsigned arg
     char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t));
     if (!chars) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     CopyChars(chars, *str);
 
-    WorkerInput* input = js_new<WorkerInput>(JS_GetParentContext(cx), chars, str->length());
+    WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
     if (!input) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     auto thread = js_new<Thread>(Thread::Options().setStackSize(gMaxStackSize + 128 * 1024));
     if (!thread || !thread->init(WorkerMain, input)) {
         ReportOutOfMemory(cx);
@@ -4841,17 +4841,17 @@ NewGlobal(JSContext* cx, unsigned argc, 
         if (!JS_GetProperty(cx, opts, "experimentalNumberFormatFormatToPartsEnabled", &v))
             return false;
         if (v.isBoolean())
             creationOptions.setExperimentalNumberFormatFormatToPartsEnabled(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
             return false;
         if (v.isObject())
-            creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
+            creationOptions.setExistingZone(UncheckedUnwrap(&v.toObject()));
 
         if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v))
             return false;
         if (v.isBoolean())
             behaviors.setDisableLazyParsing(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "principal", &v))
             return false;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -341,37 +341,20 @@ GlobalObject::createInternal(JSContext* 
 /* static */ GlobalObject*
 GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::CompartmentOptions& options)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
-    JSRuntime* rt = cx->runtime();
-
-    auto zoneSpecifier = options.creationOptions().zoneSpecifier();
-    Zone* zone;
-    if (zoneSpecifier == JS::SystemZone)
-        zone = rt->gc.systemZone;
-    else if (zoneSpecifier == JS::FreshZone)
-        zone = nullptr;
-    else
-        zone = static_cast<Zone*>(options.creationOptions().zonePointer());
-
-    JSCompartment* compartment = NewCompartment(cx, zone, principals, options);
+    JSCompartment* compartment = NewCompartment(cx, principals, options);
     if (!compartment)
         return nullptr;
 
-    // Lazily create the system zone.
-    if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) {
-        rt->gc.systemZone = compartment->zone();
-        rt->gc.systemZone->isSystem = true;
-    }
-
     Rooted<GlobalObject*> global(cx);
     {
         AutoCompartment ac(cx, compartment);
         global = GlobalObject::createInternal(cx, clasp);
         if (!global)
             return nullptr;
 
         if (hookOption == JS::FireOnNewGlobalHook)
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -536,17 +536,17 @@ CreateGlobalForOffThreadParse(JSContext*
 
     JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
                                               currentCompartment->behaviors());
 
     auto& creationOptions = compartmentOptions.creationOptions();
 
     creationOptions.setInvisibleToDebugger(true)
                    .setMergeable(true)
-                   .setZone(JS::FreshZone);
+                   .setNewZoneInSystemZoneGroup();
 
     // Don't falsely inherit the host's global trace hook.
     creationOptions.setTrace(nullptr);
 
     JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
                                           JS::FireOnNewGlobalHook, compartmentOptions);
     if (!global)
         return nullptr;
@@ -1560,25 +1560,34 @@ HelperThread::handleIonWorkload(AutoLock
         AutoSetContextRuntime ascr(rt);
         jit::JitContext jctx(jit::CompileRuntime::get(rt),
                              jit::CompileCompartment::get(builder->script()->compartment()),
                              &builder->alloc());
         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
     }
 
     FinishOffThreadIonCompile(builder, locked);
+
+    // Ping any thread currently operating on the compiled script's zone group
+    // so that the compiled code can be incorporated at the next interrupt
+    // callback. Don't interrupt Ion code for this, as this incorporation can
+    // be delayed indefinitely without affecting performance as long as the
+    // main thread is actually executing Ion code.
+    //
+    // This must happen before the current task is reset. DestroyContext
+    // cancels in progress Ion compilations before destroying its target
+    // context, and after we reset the current task we are no longer considered
+    // to be Ion compiling.
+    JSContext* target = builder->script()->zoneFromAnyThread()->group()->ownerContext().context();
+    if (target)
+        target->requestInterrupt(JSContext::RequestInterruptCanWait);
+
     currentTask.reset();
     pause = false;
 
-    // Ping the main thread so that the compiled code can be incorporated
-    // at the next interrupt callback. Don't interrupt Ion code for this, as
-    // this incorporation can be delayed indefinitely without affecting
-    // performance as long as the main thread is actually executing Ion code.
-    rt->unsafeContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
-
     // Notify the main thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 
     // When finishing Ion compilation jobs, we can start unpausing compilation
     // threads that were paused to restrict the number of active compilations.
     // Only unpause one at a time, to make sure we don't exceed the restriction.
     // Since threads are currently only paused for Ion compilations, this
     // strategy will eventually unpause all paused threads, regardless of how
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -138,18 +138,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
     profilingScripts(false),
     scriptAndCountsVector(nullptr),
     lcovOutput_(),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
-    singletonContext(nullptr),
-    singletonZoneGroup(nullptr),
     gc(thisFromCtor()),
     gcInitialized(false),
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
     defaultFreeOp_(nullptr),
 #if !EXPOSE_INTL_API
@@ -194,34 +192,23 @@ JSRuntime::init(JSContext* cx, uint32_t 
 {
     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
         return false;
 
     activeContext_ = cx;
     if (!cooperatingContexts().append(cx))
         return false;
 
-    singletonContext = cx;
-
     defaultFreeOp_ = js_new<js::FreeOp>(this);
     if (!defaultFreeOp_)
         return false;
 
-    ScopedJSDeletePtr<ZoneGroup> zoneGroup(js_new<ZoneGroup>(this));
-    if (!zoneGroup)
-        return false;
-    singletonZoneGroup = zoneGroup;
-
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
-    if (!zoneGroup->init(maxNurseryBytes) || !gc.groups.ref().append(zoneGroup))
-        return false;
-    zoneGroup.forget();
-
     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this, nullptr));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
     JS::CompartmentOptions options;
     ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
     if (!atomsCompartment || !atomsCompartment->init(nullptr))
         return false;
@@ -337,18 +324,16 @@ JSRuntime::destroyRuntime()
 
     js_delete(defaultFreeOp_.ref());
 
     js_free(defaultLocale);
     js_delete(jitRuntime_.ref());
 
     DebugOnly<size_t> oldCount = liveRuntimesCount--;
     MOZ_ASSERT(oldCount > 0);
-
-    js_delete(zoneGroupFromMainThread());
 }
 
 void
 JSRuntime::setActiveContext(JSContext* cx)
 {
     MOZ_ASSERT_IF(cx, isCooperatingContext(cx));
     MOZ_RELEASE_ASSERT(!activeContextChangeProhibited());
     MOZ_RELEASE_ASSERT(gc.canChangeActiveContext(cx));
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -611,34 +611,16 @@ struct JSRuntime : public js::MallocProv
     }
     js::jit::JitRuntime* jitRuntime() const {
         return jitRuntime_.ref();
     }
     bool hasJitRuntime() const {
         return !!jitRuntime_;
     }
 
-    // These will be removed soon.
-  private:
-    JSContext* singletonContext;
-    js::ZoneGroup* singletonZoneGroup;
-  public:
-
-    JSContext* unsafeContextFromAnyThread() const { return singletonContext; }
-    JSContext* contextFromMainThread() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
-        return singletonContext;
-    }
-
-    js::ZoneGroup* zoneGroupFromAnyThread() const { return singletonZoneGroup; }
-    js::ZoneGroup* zoneGroupFromMainThread() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
-        return singletonZoneGroup;
-    }
-
   private:
     // Used to generate random keys for hash tables.
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
     mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
 
   public:
     mozilla::HashCodeScrambler randomHashCodeScrambler();
     mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2740,20 +2740,20 @@ js::FillSelfHostingCompileOptions(Compil
 
 GlobalObject*
 JSRuntime::createSelfHostingGlobal(JSContext* cx)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     JS::CompartmentOptions options;
-    options.creationOptions().setZone(JS::FreshZone);
+    options.creationOptions().setNewZoneInSystemZoneGroup();
     options.behaviors().setDiscardSource(true);
 
-    JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options);
+    JSCompartment* compartment = NewCompartment(cx, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const ClassOps shgClassOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -557,17 +557,17 @@ mozJSComponentLoader::PrepareObjectForLo
     if (!mLoaderGlobal) {
         RefPtr<BackstagePass> backstagePass;
         rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         CompartmentOptions options;
 
         options.creationOptions()
-               .setZone(SystemZone)
+               .setSystemZone()
                .setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
 
         options.behaviors().setVersion(JSVERSION_LATEST);
 
         if (xpc::SharedMemoryEnabled())
             options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
         // Defer firing OnNewGlobalObject until after the __URI__ property has
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1062,21 +1062,21 @@ xpc::CreateSandboxObject(JSContext* cx, 
     // XXXjwatt: Consider whether/when sandboxes should be able to see
     // [SecureContext] API (bug 1273687).  In that case we'd call
     // creationOptions.setSecureContext(true).
 
     if (xpc::SharedMemoryEnabled())
         creationOptions.setSharedMemoryAndAtomicsEnabled(true);
 
     if (options.sameZoneAs)
-        creationOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
+        creationOptions.setExistingZone(js::UncheckedUnwrap(options.sameZoneAs));
     else if (options.freshZone)
-        creationOptions.setZone(JS::FreshZone);
+        creationOptions.setNewZoneInSystemZoneGroup();
     else
-        creationOptions.setZone(JS::SystemZone);
+        creationOptions.setSystemZone();
 
     creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
                    .setTrace(TraceXPCGlobal);
 
     // Try to figure out any addon this sandbox should be associated with.
     // The addon could have been passed in directly, as part of the metadata,
     // or by being constructed from an addon's code.
     JSAddonId* addonId = nullptr;
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1517,17 +1517,17 @@ XRE_XPCShellMain(int argc, char** argv, 
             fprintf(gErrFile, "+++ Failed to create BackstagePass: %8x\n",
                     static_cast<uint32_t>(rv));
             return 1;
         }
 
         // Make the default XPCShell global use a fresh zone (rather than the
         // System Zone) to improve cross-zone test coverage.
         JS::CompartmentOptions options;
-        options.creationOptions().setZone(JS::FreshZone);
+        options.creationOptions().setNewZoneInSystemZoneGroup();
         if (xpc::SharedMemoryEnabled())
             options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
         options.behaviors().setVersion(JSVERSION_LATEST);
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
         rv = xpc->InitClassesWithNewWrappedGlobal(cx,
                                                   static_cast<nsIGlobalObject*>(backstagePass),
                                                   systemprincipal,
                                                   0,
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -623,17 +623,17 @@ private:
 
     if (!JS::InitSelfHostedCode(mContext)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     JSAutoRequest ar(mContext);
 
     JS::CompartmentOptions options;
-    options.creationOptions().setZone(JS::SystemZone);
+    options.creationOptions().setSystemZone();
     options.behaviors().setVersion(JSVERSION_LATEST);
     mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
                                  JS::DontFireOnNewGlobalHook, options);
     if (!mGlobal) {
       JS_ClearPendingException(mContext);
       return NS_ERROR_OUT_OF_MEMORY;
     }
     JS::Rooted<JSObject*> global(mContext, mGlobal);
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -488,28 +488,28 @@ CycleCollectedJSContext::~CycleCollected
 
 static void
 MozCrashWarningReporter(JSContext*, JSErrorReport*)
 {
   MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
 }
 
 nsresult
-CycleCollectedJSContext::Initialize(JSContext* aParentContext,
+CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
                                     uint32_t aMaxBytes,
                                     uint32_t aMaxNurseryBytes)
 {
   MOZ_ASSERT(!mJSContext);
 
   mOwningThread->SetScriptObserver(this);
   // The main thread has a base recursion depth of 0, workers of 1.
   mBaseRecursionDepth = RecursionDepth();
 
   mozilla::dom::InitScriptSettings();
-  mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentContext);
+  mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
   if (!mJSContext) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   NS_GetCurrentThread()->SetCanInvokeJS(true);
 
   if (!JS_AddExtraGCRootsTracer(mJSContext, TraceBlackJS, this)) {
     MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -142,17 +142,17 @@ class CycleCollectedJSContext
   friend class JSGCThingParticipant;
   friend class JSZoneParticipant;
   friend class IncrementalFinalizeRunnable;
 protected:
   CycleCollectedJSContext();
   virtual ~CycleCollectedJSContext();
 
   MOZ_IS_CLASS_INIT
-  nsresult Initialize(JSContext* aParentContext,
+  nsresult Initialize(JSRuntime* aParentRuntime,
                       uint32_t aMaxBytes,
                       uint32_t aMaxNurseryBytes);
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   void UnmarkSkippableJSHolders();
 
   virtual void
   TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}