Bug 1294404 - Merge PerThreadDataFriendFields and ContextFriendFields, clean up APIs. r=terrence,bz
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 12 Aug 2016 11:39:16 +0200
changeset 309096 7e0d9eb7eb0578ce97383958357902c8965f5eeb
parent 309095 31f195c1bca6300f19bcb2ea1fd81b0deeb75564
child 309097 00f781f21da35251e0627c5e6d1bf190ddbd4493
push id80529
push userjandemooij@gmail.com
push dateFri, 12 Aug 2016 09:40:00 +0000
treeherdermozilla-inbound@7e0d9eb7eb05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, bz
bugs1294404
milestone51.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 1294404 - Merge PerThreadDataFriendFields and ContextFriendFields, clean up APIs. r=terrence,bz
dom/base/nsFrameLoader.cpp
dom/base/nsFrameMessageManager.cpp
dom/base/nsFrameMessageManager.h
dom/base/nsInProcessTabChildGlobal.cpp
js/public/RootingAPI.h
js/src/builtin/MapObject.cpp
js/src/builtin/TestingFunctions.cpp
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Zone.cpp
js/src/jit/JitFrames.cpp
js/src/jsapi-tests/testFindSCCs.cpp
js/src/jsapi-tests/testGCExactRooting.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jspubtd.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2730,18 +2730,20 @@ nsFrameLoader::DoLoadMessageManagerScrip
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
                               public Runnable
 {
 public:
-  nsAsyncMessageToChild(JSContext* aCx, JS::Handle<JSObject*> aCpows, nsFrameLoader* aFrameLoader)
-    : nsSameProcessAsyncMessageBase(aCx, aCpows)
+  nsAsyncMessageToChild(JS::RootingContext* aRootingCx,
+                        JS::Handle<JSObject*> aCpows,
+                        nsFrameLoader* aFrameLoader)
+    : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
     , mFrameLoader(aFrameLoader)
   {
   }
 
   NS_IMETHOD Run() override
   {
     nsInProcessTabChildGlobal* tabChild =
       static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
@@ -2781,18 +2783,19 @@ nsFrameLoader::DoSendAsyncMessage(JSCont
                                     IPC::Principal(aPrincipal), data)) {
       return NS_OK;
     } else {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   if (mChildMessageManager) {
-    RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(aCx, aCpows, this);
-    nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
+    JS::RootingContext* rcx = JS::RootingContext::get(aCx);
+    RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(rcx, aCpows, this);
+    nsresult rv = ev->Init(aMessage, aData, aPrincipal);
     if (NS_FAILED(rv)) {
       return rv;
     }
     rv = NS_DispatchToCurrentThread(ev);
     if (NS_FAILED(rv)) {
       return rv;
     }
     return rv;
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1909,18 +1909,19 @@ NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 
 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
                                          public Runnable
 {
 public:
-  nsAsyncMessageToSameProcessChild(JSContext* aCx, JS::Handle<JSObject*> aCpows)
-    : nsSameProcessAsyncMessageBase(aCx, aCpows)
+  nsAsyncMessageToSameProcessChild(JS::RootingContext* aRootingCx,
+                                   JS::Handle<JSObject*> aCpows)
+    : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
   { }
   NS_IMETHOD Run() override
   {
     nsFrameMessageManager* ppm = nsFrameMessageManager::GetChildProcessManager();
     ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), nullptr, ppm);
     return NS_OK;
   }
 };
@@ -1951,20 +1952,21 @@ public:
   }
 
   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
                                       const nsAString& aMessage,
                                       StructuredCloneData& aData,
                                       JS::Handle<JSObject *> aCpows,
                                       nsIPrincipal* aPrincipal) override
   {
+    JS::RootingContext* rcx = JS::RootingContext::get(aCx);
     RefPtr<nsAsyncMessageToSameProcessChild> ev =
-      new nsAsyncMessageToSameProcessChild(aCx, aCpows);
+      new nsAsyncMessageToSameProcessChild(rcx, aCpows);
 
-    nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
+    nsresult rv = ev->Init(aMessage, aData, aPrincipal);
     if (NS_FAILED(rv)) {
       return rv;
     }
     rv = NS_DispatchToCurrentThread(ev);
     if (NS_FAILED(rv)) {
       return rv;
     }
     return NS_OK;
@@ -2068,18 +2070,19 @@ public:
   }
 };
 
 
 class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase,
                                           public SameProcessMessageQueue::Runnable
 {
 public:
-  nsAsyncMessageToSameProcessParent(JSContext* aCx, JS::Handle<JSObject*> aCpows)
-    : nsSameProcessAsyncMessageBase(aCx, aCpows)
+  nsAsyncMessageToSameProcessParent(JS::RootingContext* aRootingCx,
+                                    JS::Handle<JSObject*> aCpows)
+    : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
   { }
   virtual nsresult HandleMessage() override
   {
     nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager;
     ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), nullptr, ppm);
     return NS_OK;
   }
 };
@@ -2106,34 +2109,35 @@ public:
                                      nsIPrincipal* aPrincipal,
                                      nsTArray<StructuredCloneData>* aRetVal,
                                      bool aIsSync) override
   {
     SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
     queue->Flush();
 
     if (nsFrameMessageManager::sSameProcessParentManager) {
-      SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
+      SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
       RefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
       ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, aMessage,
                           true, &aData, &cpows, aPrincipal, aRetVal);
     }
     return true;
   }
 
   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
                                   const nsAString& aMessage,
                                   StructuredCloneData& aData,
                                   JS::Handle<JSObject *> aCpows,
                                   nsIPrincipal* aPrincipal) override
   {
     SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+    JS::RootingContext* rcx = JS::RootingContext::get(aCx);
     RefPtr<nsAsyncMessageToSameProcessParent> ev =
-      new nsAsyncMessageToSameProcessParent(aCx, aCpows);
-    nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
+      new nsAsyncMessageToSameProcessParent(rcx, aCpows);
+    nsresult rv = ev->Init(aMessage, aData, aPrincipal);
 
     if (NS_FAILED(rv)) {
       return rv;
     }
     queue->Push(ev);
     return NS_OK;
   }
 
@@ -2221,47 +2225,52 @@ nsFrameMessageManager::MarkForCC()
   }
 
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
   return true;
 }
 
-nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx, JS::Handle<JSObject*> aCpows)
-  : mRuntime(nullptr)
-  , mCpows(aCx, aCpows)
+nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
+                                                             JS::Handle<JSObject*> aCpows)
+  : mRootingCx(aRootingCx)
+  , mCpows(aRootingCx, aCpows)
+#ifdef DEBUG
+  , mCalledInit(false)
+#endif
 { }
 
 
 nsresult
-nsSameProcessAsyncMessageBase::Init(JSContext* aCx,
-                                    const nsAString& aMessage,
+nsSameProcessAsyncMessageBase::Init(const nsAString& aMessage,
                                     StructuredCloneData& aData,
                                     nsIPrincipal* aPrincipal)
 {
   if (!mData.Copy(aData)) {
     Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB, aData.DataLength());
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  mRuntime = js::GetRuntime(aCx);
   mMessage = aMessage;
   mPrincipal = aPrincipal;
+#ifdef DEBUG
+  mCalledInit = true;
+#endif
 
   return NS_OK;
 }
 
 void
 nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget,
                                               nsIFrameLoader* aTargetFrameLoader,
                                               nsFrameMessageManager* aManager)
 {
   // Make sure that we have called Init() and it has succeeded.
-  MOZ_ASSERT(mRuntime);
+  MOZ_ASSERT(mCalledInit);
   if (aManager) {
-    SameProcessCpowHolder cpows(mRuntime, mCpows);
+    SameProcessCpowHolder cpows(mRootingCx, mCpows);
 
     RefPtr<nsFrameMessageManager> mm = aManager;
     mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
                        &cpows, mPrincipal, nullptr);
   }
 }
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -140,18 +140,18 @@ struct nsMessageListenerInfo
   nsWeakPtr mWeakListener;
   bool mListenWhenClosed;
 };
 
 
 class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder
 {
 public:
-  SameProcessCpowHolder(JSRuntime *aRuntime, JS::Handle<JSObject*> aObj)
-    : mObj(aRuntime, aObj)
+  SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj)
+    : mObj(aRootingCx, aObj)
   {
   }
 
   virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp)
     override;
 
 private:
   JS::Rooted<JSObject*> mObj;
@@ -333,32 +333,35 @@ private:
      NS_DispatchToMainThread(ev);
    }
 */
 class nsSameProcessAsyncMessageBase
 {
 public:
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
-  nsSameProcessAsyncMessageBase(JSContext* aCx, JS::Handle<JSObject*> aCpows);
-  nsresult Init(JSContext* aCx,
-                const nsAString& aMessage,
+  nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
+                                JS::Handle<JSObject*> aCpows);
+  nsresult Init(const nsAString& aMessage,
                 StructuredCloneData& aData,
                 nsIPrincipal* aPrincipal);
 
   void ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                       nsFrameMessageManager* aManager);
 private:
   nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&);
 
-  JSRuntime* mRuntime;
+  JS::RootingContext* mRootingCx;
   nsString mMessage;
   StructuredCloneData mData;
   JS::PersistentRooted<JSObject*> mCpows;
   nsCOMPtr<nsIPrincipal> mPrincipal;
+#ifdef DEBUG
+  bool mCalledInit;
+#endif
 };
 
 class nsScriptCacheCleaner;
 
 struct nsMessageManagerScriptHolder
 {
   nsMessageManagerScriptHolder(JSContext* aCx,
                                JSScript* aScript,
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -31,31 +31,33 @@ nsInProcessTabChildGlobal::DoSendBlockin
                                                  nsIPrincipal* aPrincipal,
                                                  nsTArray<StructuredCloneData>* aRetVal,
                                                  bool aIsSync)
 {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   queue->Flush();
 
   if (mChromeMessageManager) {
-    SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
+    SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
     RefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
     nsCOMPtr<nsIFrameLoader> fl = GetFrameLoader();
     mm->ReceiveMessage(mOwner, fl, aMessage, true, &aData, &cpows, aPrincipal,
                        aRetVal);
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase,
                                public SameProcessMessageQueue::Runnable
 {
 public:
-  nsAsyncMessageToParent(JSContext* aCx, JS::Handle<JSObject*> aCpows, nsInProcessTabChildGlobal* aTabChild)
-    : nsSameProcessAsyncMessageBase(aCx, aCpows)
+  nsAsyncMessageToParent(JS::RootingContext* aRootingCx,
+                         JS::Handle<JSObject*> aCpows,
+                         nsInProcessTabChildGlobal* aTabChild)
+    : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
     , mTabChild(aTabChild)
   { }
 
   virtual nsresult HandleMessage() override
   {
     nsCOMPtr<nsIFrameLoader> fl = mTabChild->GetFrameLoader();
     ReceiveMessage(mTabChild->mOwner, fl, mTabChild->mChromeMessageManager);
     return NS_OK;
@@ -66,20 +68,21 @@ public:
 nsresult
 nsInProcessTabChildGlobal::DoSendAsyncMessage(JSContext* aCx,
                                               const nsAString& aMessage,
                                               StructuredCloneData& aData,
                                               JS::Handle<JSObject *> aCpows,
                                               nsIPrincipal* aPrincipal)
 {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+  JS::RootingContext* rcx = JS::RootingContext::get(aCx);
   RefPtr<nsAsyncMessageToParent> ev =
-    new nsAsyncMessageToParent(aCx, aCpows, this);
+    new nsAsyncMessageToParent(rcx, aCpows, this);
 
-  nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
+  nsresult rv = ev->Init(aMessage, aData, aPrincipal);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   queue->Push(ev);
   return NS_OK;
 }
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -656,28 +656,26 @@ class MOZ_RAII Rooted : public js::Roote
 {
     inline void registerWithRootLists(js::RootedListHeads& roots) {
         this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
     inline js::RootedListHeads& rootLists(JS::RootingContext* cx) {
-        return rootLists(reinterpret_cast<JSContext*>(cx));
+        return rootLists(static_cast<js::ContextFriendFields*>(cx));
+    }
+    inline js::RootedListHeads& rootLists(js::ContextFriendFields* cx) {
+        if (JS::Zone* zone = cx->zone_)
+            return JS::shadow::Zone::asShadowZone(zone)->stackRoots_;
+        MOZ_ASSERT(cx->isJSContext);
+        return cx->roots.stackRoots_;
     }
     inline js::RootedListHeads& rootLists(JSContext* cx) {
-        if (JS::Zone* zone = js::GetContextZone(cx))
-            return JS::shadow::Zone::asShadowZone(zone)->stackRoots_;
-        return rootLists(js::GetRuntime(cx));
-    }
-    inline js::RootedListHeads& rootLists(js::PerThreadDataFriendFields* pt) {
-        return pt->roots.stackRoots_;
-    }
-    inline js::RootedListHeads& rootLists(JSRuntime* rt) {
-        return js::PerThreadDataFriendFields::getMainThread(rt)->roots.stackRoots_;
+        return rootLists(js::ContextFriendFields::get(cx));
     }
 
   public:
     template <typename RootingContext>
     explicit Rooted(const RootingContext& cx)
       : ptr(GCPolicy<T>::initial())
     {
         registerWithRootLists(rootLists(cx));
@@ -982,25 +980,27 @@ class PersistentRooted : public js::Pers
     friend class mozilla::LinkedListElement<PersistentRooted>;
 
     void registerWithRootLists(js::RootLists& roots) {
         MOZ_ASSERT(!initialized());
         JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
         roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this));
     }
 
-    js::RootLists& rootLists(js::PerThreadDataFriendFields* pt) { return pt->roots; }
-    js::RootLists& rootLists(JSRuntime* rt) {
-        return js::PerThreadDataFriendFields::getMainThread(rt)->roots;
+    js::RootLists& rootLists(JSContext* cx) {
+        return rootLists(JS::RootingContext::get(cx));
     }
-    js::RootLists& rootLists(JSContext* cx) { return rootLists(js::GetRuntime(cx)); }
     js::RootLists& rootLists(JS::RootingContext* cx) {
-        return rootLists(reinterpret_cast<JSContext*>(cx));
+        MOZ_ASSERT(cx->isJSContext);
+        return cx->roots;
     }
 
+    // Disallow ExclusiveContext*.
+    js::RootLists& rootLists(js::ContextFriendFields* cx) = delete;
+
   public:
     PersistentRooted() : ptr(GCPolicy<T>::initial()) {}
 
     template <typename RootingContext>
     explicit PersistentRooted(const RootingContext& cx)
       : ptr(GCPolicy<T>::initial())
     {
         registerWithRootLists(rootLists(cx));
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -75,19 +75,19 @@ HashableValue::hash() const
 bool
 HashableValue::operator==(const HashableValue& other) const
 {
     // Two HashableValues are equal if they have equal bits.
     bool b = (value.asRawBits() == other.value.asRawBits());
 
 #ifdef DEBUG
     bool same;
-    PerThreadData* data = TlsPerThreadData.get();
-    RootedValue valueRoot(data, value);
-    RootedValue otherRoot(data, other.value);
+    JS::RootingContext* rcx = GetJSContextFromMainThread();
+    RootedValue valueRoot(rcx, value);
+    RootedValue otherRoot(rcx, other.value);
     MOZ_ASSERT(SameValue(nullptr, valueRoot, otherRoot, &same));
     MOZ_ASSERT(same == b);
 #endif
     return b;
 }
 
 HashableValue
 HashableValue::mark(JSTracer* trc) const
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1037,18 +1037,18 @@ class HasChildTracer : public JS::Callba
     bool found_;
 
     void onChild(const JS::GCCellPtr& thing) override {
         if (thing.asCell() == child_.toGCThing())
             found_ = true;
     }
 
   public:
-    HasChildTracer(JSRuntime* rt, HandleValue child)
-      : JS::CallbackTracer(rt, TraceWeakMapKeysValues), child_(rt, child), found_(false)
+    HasChildTracer(JSContext* cx, HandleValue child)
+      : JS::CallbackTracer(cx, TraceWeakMapKeysValues), child_(cx, child), found_(false)
     {}
 
     bool found() const { return found_; }
 };
 
 static bool
 HasChild(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1056,17 +1056,17 @@ HasChild(JSContext* cx, unsigned argc, V
     RootedValue parent(cx, args.get(0));
     RootedValue child(cx, args.get(1));
 
     if (!parent.isMarkable() || !child.isMarkable()) {
         args.rval().setBoolean(false);
         return true;
     }
 
-    HasChildTracer trc(cx->runtime(), child);
+    HasChildTracer trc(cx, child);
     TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
     args.rval().setBoolean(trc.found());
     return true;
 }
 
 static bool
 SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2711,19 +2711,18 @@ struct UnmarkGrayTracer : public JS::Cal
  * - To prevent any incorrectly gray objects from escaping to live JS outside
  *   of the containers, we must add unmark-graying read barriers to these
  *   containers.
  */
 void
 UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing)
 {
     int stackDummy;
-    if (!JS_CHECK_STACK_SIZE(runtime()->mainThread.nativeStackLimit[StackForSystemCode],
-                             &stackDummy))
-    {
+    JSContext* cx = runtime()->contextFromMainThread();
+    if (!JS_CHECK_STACK_SIZE(cx->nativeStackLimit[StackForSystemCode], &stackDummy)) {
         /*
          * If we run out of stack, we take a more drastic measure: require that
          * we GC again before the next CC.
          */
         runtime()->gc.setGrayBitsInvalid();
         return;
     }
 
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -85,17 +85,17 @@ js::RootLists::traceStackRoots(JSTracer*
     TraceStackRoots(trc, stackRoots_);
 }
 
 static void
 MarkExactStackRoots(JSRuntime* rt, JSTracer* trc)
 {
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next())
         TraceStackRoots(trc, zone->stackRoots_);
-    rt->mainThread.roots.traceStackRoots(trc);
+    rt->contextFromMainThread()->roots.traceStackRoots(trc);
 }
 
 template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
 static inline void
 MarkPersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
                          const char* name)
 {
     for (PersistentRooted<void*>* r : list)
@@ -114,17 +114,17 @@ JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
     MarkPersistentRootedList<ConcreteTraceable,
                              js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(trc,
             heapRoots_[JS::RootKind::Traceable], "persistent-traceable");
 }
 
 static void
 MarkPersistentRooted(JSRuntime* rt, JSTracer* trc)
 {
-    rt->mainThread.roots.tracePersistentRoots(trc);
+    rt->contextFromMainThread()->roots.tracePersistentRoots(trc);
 }
 
 template <typename T>
 static void
 FinishPersistentRootedChain(mozilla::LinkedList<PersistentRooted<void*>>& listArg)
 {
     auto& list = reinterpret_cast<mozilla::LinkedList<PersistentRooted<T>>&>(listArg);
     while (!list.isEmpty())
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -295,17 +295,17 @@ Zone::canCollect()
     return true;
 }
 
 void
 Zone::notifyObservingDebuggers()
 {
     for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
         JSRuntime* rt = runtimeFromAnyThread();
-        RootedGlobalObject global(rt, comps->unsafeUnbarrieredMaybeGlobal());
+        RootedGlobalObject global(rt->contextFromMainThread(), comps->unsafeUnbarrieredMaybeGlobal());
         if (!global)
             continue;
 
         GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
         if (!dbgs)
             continue;
 
         for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront()) {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -2248,19 +2248,19 @@ InlineFrameIterator::InlineFrameIterator
   : calleeTemplate_(cx),
     calleeRVA_(),
     script_(cx)
 {
     resetOn(iter);
 }
 
 InlineFrameIterator::InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter)
-  : calleeTemplate_(rt),
+  : calleeTemplate_(rt->contextFromMainThread()),
     calleeRVA_(),
-    script_(rt)
+    script_(rt->contextFromMainThread())
 {
     resetOn(iter);
 }
 
 InlineFrameIterator::InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter)
   : frame_(iter ? iter->frame_ : nullptr),
     framesRead_(0),
     frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
--- a/js/src/jsapi-tests/testFindSCCs.cpp
+++ b/js/src/jsapi-tests/testFindSCCs.cpp
@@ -152,17 +152,17 @@ void setup(unsigned count)
 
 void edge(unsigned src_index, unsigned dest_index)
 {
     Vertex[src_index].hasEdge[dest_index] = true;
 }
 
 void run()
 {
-    finder = new TestComponentFinder(cx->mainThread().nativeStackLimit[js::StackForSystemCode]);
+    finder = new TestComponentFinder(cx->nativeStackLimit[js::StackForSystemCode]);
     for (unsigned i = 0; i < vertex_count; ++i)
         finder->addNode(&Vertex[i]);
     resultsList = finder->getResultsList();
 }
 
 bool group(int vertex, ...)
 {
     TestNode* v = resultsList;
@@ -251,17 +251,17 @@ BEGIN_TEST(testFindSCCsStackLimit)
      */
     const unsigned max = 1000000;
     const unsigned initial = 10;
 
     TestNode2* vertices = new TestNode2[max]();
     for (unsigned i = initial; i < (max - 10); ++i)
         vertices[i].edge = &vertices[i + 1];
 
-    TestComponentFinder2 finder(cx->mainThread().nativeStackLimit[js::StackForSystemCode]);
+    TestComponentFinder2 finder(cx->nativeStackLimit[js::StackForSystemCode]);
     for (unsigned i = 0; i < max; ++i)
         finder.addNode(&vertices[i]);
 
     TestNode2* r = finder.getResultsList();
     CHECK(r);
     TestNode2* v = r;
 
     unsigned count = 0;
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -13,23 +13,23 @@
 
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 
 BEGIN_TEST(testGCExactRooting)
 {
     JS::RootedObject rootCx(cx, JS_NewPlainObject(cx));
-    JS::RootedObject rootRt(cx->runtime(), JS_NewPlainObject(cx));
+    JS::RootedObject rootRootingCx(JS::RootingContext::get(cx), JS_NewPlainObject(cx));
 
     JS_GC(cx);
 
     /* Use the objects we just created to ensure that they are still alive. */
     JS_DefineProperty(cx, rootCx, "foo", JS::UndefinedHandleValue, 0);
-    JS_DefineProperty(cx, rootRt, "foo", JS::UndefinedHandleValue, 0);
+    JS_DefineProperty(cx, rootRootingCx, "foo", JS::UndefinedHandleValue, 0);
 
     return true;
 }
 END_TEST(testGCExactRooting)
 
 BEGIN_TEST(testGCSuppressions)
 {
     JS::AutoAssertOnGC nogc;
@@ -88,17 +88,17 @@ BEGIN_TEST(testGCRootedStaticStructInter
     obj = nullptr;
     val = UndefinedValue();
 
     {
         JS::RootedString actual(cx);
         bool same;
 
         // Automatic move from stack to heap.
-        JS::PersistentRooted<MyContainer> heap(cx->runtime(), container);
+        JS::PersistentRooted<MyContainer> heap(cx, container);
 
         // clear prior rooting.
         container.obj() = nullptr;
         container.str() = nullptr;
 
         obj = heap.obj();
         CHECK(JS_GetProperty(cx, obj, "foo", &val));
         actual = val.toString();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1498,59 +1498,58 @@ JS_IsExternalString(JSString* str)
 
 extern JS_PUBLIC_API(const JSStringFinalizer*)
 JS_GetExternalStringFinalizer(JSString* str)
 {
     return str->asExternal().externalFinalizer();
 }
 
 static void
-SetNativeStackQuotaAndLimit(JSRuntime* rt, StackKind kind, size_t stackSize)
-{
-    rt->nativeStackQuota[kind] = stackSize;
+SetNativeStackQuotaAndLimit(JSContext* cx, StackKind kind, size_t stackSize)
+{
+    cx->nativeStackQuota[kind] = stackSize;
 
 #if JS_STACK_GROWTH_DIRECTION > 0
     if (stackSize == 0) {
-        rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
+        cx->nativeStackLimit[kind] = UINTPTR_MAX;
     } else {
-        MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
-        rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1;
+        MOZ_ASSERT(cx->nativeStackBase <= size_t(-1) - stackSize);
+        cx->nativeStackLimit[kind] = cx->nativeStackBase + stackSize - 1;
     }
 #else
     if (stackSize == 0) {
-        rt->mainThread.nativeStackLimit[kind] = 0;
+        cx->nativeStackLimit[kind] = 0;
     } else {
-        MOZ_ASSERT(rt->nativeStackBase >= stackSize);
-        rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1);
+        MOZ_ASSERT(cx->nativeStackBase >= stackSize);
+        cx->nativeStackLimit[kind] = cx->nativeStackBase - (stackSize - 1);
     }
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSContext* cx, size_t systemCodeStackSize, size_t trustedScriptStackSize,
                        size_t untrustedScriptStackSize)
 {
-    JSRuntime* rt = cx->runtime();
-    MOZ_ASSERT(rt->requestDepth == 0);
+    MOZ_ASSERT(cx->requestDepth == 0);
 
     if (!trustedScriptStackSize)
         trustedScriptStackSize = systemCodeStackSize;
     else
         MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
 
     if (!untrustedScriptStackSize)
         untrustedScriptStackSize = trustedScriptStackSize;
     else
         MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
 
-    SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize);
-    SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
-    SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
-
-    rt->initJitStackLimit();
+    SetNativeStackQuotaAndLimit(cx, StackForSystemCode, systemCodeStackSize);
+    SetNativeStackQuotaAndLimit(cx, StackForTrustedScript, trustedScriptStackSize);
+    SetNativeStackQuotaAndLimit(cx, StackForUntrustedScript, untrustedScriptStackSize);
+
+    cx->initJitStackLimit();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_ValueToId(JSContext* cx, HandleValue value, MutableHandleId idp)
 {
     AssertHeapIsIdle(cx);
@@ -3783,17 +3782,16 @@ JS::ReadOnlyCompileOptions::copyPODOptio
     column = rhs.column;
     isRunOnce = rhs.isRunOnce;
     forEval = rhs.forEval;
     noScriptRval = rhs.noScriptRval;
 }
 
 JS::OwningCompileOptions::OwningCompileOptions(JSContext* cx)
     : ReadOnlyCompileOptions(),
-      runtime(GetRuntime(cx)),
       elementRoot(cx),
       elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
 {
 }
 
 JS::OwningCompileOptions::~OwningCompileOptions()
 {
@@ -4926,41 +4924,41 @@ JS_IsRunning(JSContext* cx)
 {
     return cx->currentlyRunning();
 }
 
 JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
   JSContext* cx, HandleObject stack, const char* asyncCause,
   JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind)
   : cx(cx),
-    oldAsyncStack(cx, cx->runtime()->asyncStackForNewActivations),
-    oldAsyncCause(cx->runtime()->asyncCauseForNewActivations),
-    oldAsyncCallIsExplicit(cx->runtime()->asyncCallIsExplicit)
+    oldAsyncStack(cx, cx->asyncStackForNewActivations),
+    oldAsyncCause(cx->asyncCauseForNewActivations),
+    oldAsyncCallIsExplicit(cx->asyncCallIsExplicit)
 {
     CHECK_REQUEST(cx);
 
     // The option determines whether we actually use the new values at this
     // point. It will not affect restoring the previous values when the object
     // is destroyed, so if the option changes it won't cause consistency issues.
     if (!cx->options().asyncStack())
         return;
 
     SavedFrame* asyncStack = &stack->as<SavedFrame>();
 
-    cx->runtime()->asyncStackForNewActivations = asyncStack;
-    cx->runtime()->asyncCauseForNewActivations = asyncCause;
-    cx->runtime()->asyncCallIsExplicit = kind == AsyncCallKind::EXPLICIT;
+    cx->asyncStackForNewActivations = asyncStack;
+    cx->asyncCauseForNewActivations = asyncCause;
+    cx->asyncCallIsExplicit = kind == AsyncCallKind::EXPLICIT;
 }
 
 JS::AutoSetAsyncStackForNewCalls::~AutoSetAsyncStackForNewCalls()
 {
-    cx->runtime()->asyncCauseForNewActivations = oldAsyncCause;
-    cx->runtime()->asyncStackForNewActivations =
+    cx->asyncCauseForNewActivations = oldAsyncCause;
+    cx->asyncStackForNewActivations =
       oldAsyncStack ? &oldAsyncStack->as<SavedFrame>() : nullptr;
-    cx->runtime()->asyncCallIsExplicit = oldAsyncCallIsExplicit;
+    cx->asyncCallIsExplicit = oldAsyncCallIsExplicit;
 }
 
 /************************************************************************/
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyN(JSContext* cx, const char* s, size_t n)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3819,17 +3819,16 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
  * Note that this *roots* any JS values it refers to - they're live
  * unconditionally. Thus, instances of this type can't be owned, directly
  * or indirectly, by a JavaScript object: if any value that this roots ever
  * comes to refer to the object that owns this, then the whole cycle, and
  * anything else it entrains, will never be freed.
  */
 class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
 {
-    JSRuntime* runtime;
     PersistentRootedObject elementRoot;
     PersistentRootedString elementAttributeNameRoot;
     PersistentRootedScript introductionScriptRoot;
 
   public:
     // A minimal constructor, for use with OwningCompileOptions::copy. This
     // leaves |this.version| set to JSVERSION_UNKNOWN; the instance
     // shouldn't be used until we've set that to something real (as |copy|
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -120,17 +120,16 @@ void
 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->roots.checkNoGCRooters();
-    cx->roots.finishPersistentRoots();
 
     /*
      * Dump remaining type inference results while we still have a context.
      * This printing depends on atoms still existing.
      */
     for (CompartmentsIter c(cx, SkipAtoms); !c.done(); c.next())
         PrintTypes(cx, c, false);
 
@@ -878,17 +877,18 @@ js::GetErrorMessage(void* userRef, const
 {
     if (errorNumber > 0 && errorNumber < JSErr_Limit)
         return &js_ErrorFormatString[errorNumber];
     return nullptr;
 }
 
 ExclusiveContext::ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind kind,
                                    const JS::ContextOptions& options)
-  : ContextFriendFields(rt),
+  : ContextFriendFields(kind == Context_JS),
+    runtime_(rt),
     helperThread_(nullptr),
     contextKind_(kind),
     options_(options),
     perThreadData(pt),
     arenas_(nullptr),
     enterCompartmentDepth_(0)
 {
 }
@@ -916,17 +916,20 @@ JSContext::JSContext(JSRuntime* parentRu
     overRecursed_(false),
     propagatingForcedReturn_(false),
     liveVolatileJitFrameIterators_(nullptr),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     resolvingList(nullptr),
     generatingError(false),
     data(nullptr),
     outstandingRequests(0),
-    jitIsBroken(false)
+    jitIsBroken(false),
+    asyncStackForNewActivations(this),
+    asyncCauseForNewActivations(nullptr),
+    asyncCallIsExplicit(false)
 {
     MOZ_ASSERT(static_cast<ContextFriendFields*>(this) ==
                ContextFriendFields::get(this));
 }
 
 JSContext::~JSContext()
 {
     destroyRuntime();
@@ -1092,16 +1095,36 @@ ExclusiveContext::stackLimitForJitCode(S
 {
 #ifdef JS_SIMULATOR
     return runtime_->simulator()->stackLimit();
 #else
     return stackLimit(kind);
 #endif
 }
 
+void
+JSContext::resetJitStackLimit()
+{
+    // Note that, for now, we use the untrusted limit for ion. This is fine,
+    // because it's the most conservative limit, and if we hit it, we'll bail
+    // out of ion into the interpreter, which will do a proper recursion check.
+#ifdef JS_SIMULATOR
+    jitStackLimit_ = jit::Simulator::StackLimit();
+#else
+    jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
+#endif
+    jitStackLimitNoInterrupt_ = jitStackLimit_;
+}
+
+void
+JSContext::initJitStackLimit()
+{
+    resetJitStackLimit();
+}
+
 JSVersion
 JSContext::findVersion() const
 {
     if (JSScript* script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
         return script->getVersion();
 
     if (compartment() && compartment()->behaviors().version() != JSVERSION_UNKNOWN)
         return compartment()->behaviors().version();
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -94,25 +94,24 @@ class ExclusiveContext : public ContextF
                          public MallocProvider<ExclusiveContext>
 {
     friend class gc::ArenaLists;
     friend class AutoCompartment;
     friend class AutoLockForExclusiveAccess;
     friend struct StackBaseShape;
     friend void JSScript::initCompartment(ExclusiveContext* cx);
     friend class jit::JitContext;
-    friend class Activation;
+
+    // runtime_ is private to hide it from JSContext. JSContext inherits from
+    // JSRuntime, so it's more efficient to use the base class.
+    JSRuntime* const runtime_;
 
     // The thread on which this context is running, if this is not a JSContext.
     HelperThread* helperThread_;
 
-    // Hide runtime_ from JSContext. JSContext inherits from JSRuntime, so it's
-    // more efficient to use |this|.
-    using ContextFriendFields::runtime_;
-
   public:
     enum ContextKind {
         Context_JS,
         Context_Exclusive
     };
 
   private:
     ContextKind contextKind_;
@@ -209,19 +208,19 @@ class ExclusiveContext : public ContextF
     FrozenAtomSet& permanentAtoms() { return *runtime_->permanentAtoms; }
     WellKnownSymbols& wellKnownSymbols() { return *runtime_->wellKnownSymbols; }
     JS::BuildIdOp buildIdOp() { return runtime_->buildIdOp; }
     const JS::AsmJSCacheOps& asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName* emptyString() { return runtime_->emptyString; }
     FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void* contextAddressForJit() { return runtime_->unsafeContextFromAnyThread(); }
     void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
-    void* stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
+    void* stackLimitAddress(StackKind kind) { return &nativeStackLimit[kind]; }
     void* stackLimitAddressForJitCode(StackKind kind);
-    uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; }
+    uintptr_t stackLimit(StackKind kind) { return nativeStackLimit[kind]; }
     uintptr_t stackLimitForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
     bool jitSupportsUnalignedAccesses() const { return runtime_->jitSupportsUnalignedAccesses; }
     bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
     bool lcovEnabled() const { return runtime_->lcovOutput.isEnabled(); }
 
     // Thread local data that may be accessed freely.
@@ -388,42 +387,68 @@ struct JSContext : public js::ExclusiveC
     bool                generatingError;
 
     /* State for object and array toSource conversion. */
     js::AutoCycleDetector::Set cycleDetectorSet;
 
     /* Client opaque pointer. */
     void* data;
 
+    void resetJitStackLimit();
+
   public:
 
     /*
      * Return:
      * - The newest scripted frame's version, if there is such a frame.
      * - The version from the compartment.
      * - The default version.
      *
      * Note: if this ever shows up in a profile, just add caching!
      */
     JSVersion findVersion() const;
 
+    void initJitStackLimit();
+
     JS::ContextOptions& options() {
         return options_;
     }
 
     js::LifoAlloc& tempLifoAlloc() { return runtime()->tempLifoAlloc; }
 
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
 
     bool jitIsBroken;
 
     void updateJITEnabled();
 
+    /*
+     * Youngest frame of a saved stack that will be picked up as an async stack
+     * by any new Activation, and is nullptr when no async stack should be used.
+     *
+     * The JS::AutoSetAsyncStackForNewCalls class can be used to set this.
+     *
+     * New activations will reset this to nullptr on construction after getting
+     * the current value, and will restore the previous value on destruction.
+     */
+    JS::PersistentRooted<js::SavedFrame*> asyncStackForNewActivations;
+
+    /*
+     * Value of asyncCause to be attached to asyncStackForNewActivations.
+     */
+    const char* asyncCauseForNewActivations;
+
+    /*
+     * True if the async call was explicitly requested, e.g. via
+     * callFunctionWithAsyncStack.
+     */
+    bool asyncCallIsExplicit;
+
     /* Whether this context has JS frames on the stack. */
     bool currentlyRunning() const;
 
     bool currentlyRunningInInterpreter() const {
         return activation()->isInterpreter();
     }
     bool currentlyRunningInJit() const {
         return activation()->isJit();
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -348,17 +348,17 @@ GetNativeStackLimit(ExclusiveContext* cx
     if (cx->isJSContext()) {
         kind = cx->asJSContext()->runningWithTrustedPrincipals()
                  ? StackForTrustedScript : StackForUntrustedScript;
     } else {
         // For other threads, we just use the trusted stack depth, since it's
         // unlikely that we'll be mixing trusted and untrusted code together.
         kind = StackForTrustedScript;
     }
-    return cx->perThreadData->nativeStackLimit[kind];
+    return cx->nativeStackLimit[kind];
 }
 
 inline LifoAlloc&
 ExclusiveContext::typeLifoAlloc()
 {
     return zone()->types.typeLifoAlloc;
 }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -33,21 +33,19 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 
 using mozilla::Move;
 using mozilla::PodArrayZero;
 
-// Required by PerThreadDataFriendFields::getMainThread()
-JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) ==
-                 PerThreadDataFriendFields::RuntimeMainThreadOffset);
-
-PerThreadDataFriendFields::PerThreadDataFriendFields()
+js::ContextFriendFields::ContextFriendFields(bool isJSContext)
+  : JS::RootingContext(isJSContext),
+    compartment_(nullptr), zone_(nullptr)
 {
     PodArrayZero(nativeStackLimit);
 #if JS_STACK_GROWTH_DIRECTION > 0
     for (int i=0; i<StackKindCount; i++)
         nativeStackLimit[i] = UINTPTR_MAX;
 #endif
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -994,19 +994,17 @@ IsObjectInContextCompartment(JSObject* o
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
 
 JS_FRIEND_API(bool)
 RunningWithTrustedPrincipals(JSContext* cx);
 
 inline uintptr_t
 GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0)
 {
-    PerThreadDataFriendFields* mainThread =
-      PerThreadDataFriendFields::getMainThread(GetRuntime(cx));
-    uintptr_t limit = mainThread->nativeStackLimit[kind];
+    uintptr_t limit = ContextFriendFields::get(cx)->nativeStackLimit[kind];
 #if JS_STACK_GROWTH_DIRECTION > 0
     limit += extraAllowance;
 #else
     limit -= extraAllowance;
 #endif
     return limit;
 }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1116,17 +1116,17 @@ GCRuntime::finish()
 }
 
 void
 GCRuntime::finishRoots()
 {
     if (rootsHash.initialized())
         rootsHash.clear();
 
-    rt->mainThread.roots.finishPersistentRoots();
+    rt->contextFromMainThread()->roots.finishPersistentRoots();
 }
 
 bool
 GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 {
     switch (key) {
       case JSGC_MAX_MALLOC_BYTES:
         setMaxMallocBytes(value);
@@ -4445,17 +4445,18 @@ GCRuntime::findZoneEdgesForWeakMaps()
     }
 
     return true;
 }
 
 void
 GCRuntime::findZoneGroups(AutoLockForExclusiveAccess& lock)
 {
-    ZoneComponentFinder finder(rt->mainThread.nativeStackLimit[StackForSystemCode], lock);
+    JSContext* cx = rt->contextFromMainThread();
+    ZoneComponentFinder finder(cx->nativeStackLimit[StackForSystemCode], lock);
     if (!isIncremental || !findZoneEdgesForWeakMaps())
         finder.useOneComponent();
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarking());
         finder.addNode(zone);
     }
     zoneGroups = finder.getResultsList();
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -329,134 +329,86 @@ namespace JS {
  * JS::RootingContext is a base class of ContextFriendFields and JSContext.
  * This class can be used to let code construct a Rooted<> or PersistentRooted<>
  * instance, without giving it full access to the JSContext.
  */
 struct RootingContext
 {
     js::RootLists roots;
 
+#ifdef DEBUG
+    // Whether the derived class is a JSContext or an ExclusiveContext.
+    bool isJSContext;
+#endif
+
+    explicit RootingContext(bool isJSContextArg)
+#ifdef DEBUG
+      : isJSContext(isJSContextArg)
+#endif
+    {}
+
     static RootingContext* get(JSContext* cx) {
         return reinterpret_cast<RootingContext*>(cx);
     }
 };
 
 } // namespace JS
 
 namespace js {
 
 struct ContextFriendFields : public JS::RootingContext
 {
   protected:
-    JSRuntime* const     runtime_;
-
     /* The current compartment. */
     JSCompartment*      compartment_;
 
     /* The current zone. */
     JS::Zone*           zone_;
 
   public:
-    explicit ContextFriendFields(JSRuntime* rt)
-      : runtime_(rt), compartment_(nullptr), zone_(nullptr)
-    {}
+    /* Limit pointer for checking native stack consumption. */
+    uintptr_t nativeStackLimit[js::StackKindCount];
+
+    explicit ContextFriendFields(bool isJSContext);
 
     static const ContextFriendFields* get(const JSContext* cx) {
         return reinterpret_cast<const ContextFriendFields*>(cx);
     }
 
     static ContextFriendFields* get(JSContext* cx) {
         return reinterpret_cast<ContextFriendFields*>(cx);
     }
 
-    friend JSRuntime* GetRuntime(const JSContext* cx);
     friend JSCompartment* GetContextCompartment(const JSContext* cx);
     friend JS::Zone* GetContextZone(const JSContext* cx);
     template <typename T> friend class JS::Rooted;
 };
 
 /*
  * Inlinable accessors for JSContext.
  *
  * - These must not be available on the more restricted superclasses of
  *   JSContext, so we can't simply define them on ContextFriendFields.
  *
  * - They're perfectly ordinary JSContext functionality, so ought to be
  *   usable without resorting to jsfriendapi.h, and when JSContext is an
  *   incomplete type.
  */
-inline JSRuntime*
-GetRuntime(const JSContext* cx)
-{
-    return ContextFriendFields::get(cx)->runtime_;
-}
-
 inline JSCompartment*
 GetContextCompartment(const JSContext* cx)
 {
     return ContextFriendFields::get(cx)->compartment_;
 }
 
 inline JS::Zone*
 GetContextZone(const JSContext* cx)
 {
     return ContextFriendFields::get(cx)->zone_;
 }
 
-class PerThreadData;
-
-struct PerThreadDataFriendFields
-{
-  private:
-    // Note: this type only exists to permit us to derive the offset of
-    // the perThread data within the real JSRuntime* type in a portable
-    // way.
-    struct RuntimeDummy : JS::shadow::Runtime
-    {
-        struct PerThreadDummy {
-            void* field1;
-            uintptr_t field2;
-#ifdef JS_DEBUG
-            uint64_t field3;
-#endif
-        } mainThread;
-    };
-
-  public:
-    /* Rooting structures. */
-    RootLists roots;
-
-    PerThreadDataFriendFields();
-
-    /* Limit pointer for checking native stack consumption. */
-    uintptr_t nativeStackLimit[js::StackKindCount];
-
-    static const size_t RuntimeMainThreadOffset = offsetof(RuntimeDummy, mainThread);
-
-    static inline PerThreadDataFriendFields* get(js::PerThreadData* pt) {
-        return reinterpret_cast<PerThreadDataFriendFields*>(pt);
-    }
-
-    static inline PerThreadDataFriendFields* getMainThread(JSRuntime* rt) {
-        // mainThread must always appear directly after |JS::shadow::Runtime|.
-        // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
-        return reinterpret_cast<PerThreadDataFriendFields*>(
-            reinterpret_cast<char*>(rt) + RuntimeMainThreadOffset);
-    }
-
-    static inline const PerThreadDataFriendFields* getMainThread(const JSRuntime* rt) {
-        // mainThread must always appear directly after |JS::shadow::Runtime|.
-        // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
-        return reinterpret_cast<const PerThreadDataFriendFields*>(
-            reinterpret_cast<const char*>(rt) + RuntimeMainThreadOffset);
-    }
-
-    template <typename T> friend class JS::Rooted;
-};
-
 } /* namespace js */
 
 MOZ_BEGIN_EXTERN_C
 
 // Defined in NSPR prio.h.
 typedef struct PRFileDesc PRFileDesc;
 
 MOZ_END_EXTERN_C
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -202,19 +202,19 @@ static const JSClass parseTaskGlobalClas
     &parseTaskGlobalClassOps
 };
 
 ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                      JSContext* initCx, const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind), cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
+    exclusiveContextGlobal(initCx, exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
-    script(initCx->runtime()), sourceObject(initCx->runtime()),
+    script(initCx), sourceObject(initCx),
     errors(cx), overRecursed(false), outOfMemory(false)
 {
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
@@ -1475,25 +1475,28 @@ void
 ExclusiveContext::addPendingOutOfMemory()
 {
     // Keep in sync with recoverFromOutOfMemory.
     if (helperThread()->parseTask())
         helperThread()->parseTask()->outOfMemory = true;
 }
 
 void
-HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked)
+HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked, uintptr_t stackLimit)
 {
     MOZ_ASSERT(HelperThreadState().canStartParseTask(locked));
     MOZ_ASSERT(idle());
 
     currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
     ParseTask* task = parseTask();
     task->cx->setHelperThread(this);
 
+    for (size_t i = 0; i < ArrayLength(task->cx->nativeStackLimit); i++)
+        task->cx->nativeStackLimit[i] = stackLimit;
+
     {
         AutoUnlockHelperThreadState unlock(locked);
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               task->exclusiveContextGlobal->runtimeFromAnyThread());
         task->parse();
     }
 
     // The callback is invoked while we are still off the main thread.
@@ -1642,18 +1645,16 @@ HelperThread::threadLoop()
 
     // Compute the thread's stack limit, for over-recursed checks.
     uintptr_t stackLimit = GetNativeStackBase();
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackLimit += HELPER_STACK_QUOTA;
 #else
     stackLimit -= HELPER_STACK_QUOTA;
 #endif
-    for (size_t i = 0; i < ArrayLength(threadData->nativeStackLimit); i++)
-        threadData->nativeStackLimit[i] = stackLimit;
 
     while (true) {
         MOZ_ASSERT(idle());
 
         // Block until a task is available. Save the value of whether we are
         // going to do an Ion compile, in case the value returned by the method
         // changes.
         bool ionCompile = false;
@@ -1676,17 +1677,17 @@ HelperThread::threadLoop()
         if (HelperThreadState().canStartWasmCompile(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_ASMJS);
             handleWasmWorkload(lock);
         } else if (ionCompile) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
             handleIonWorkload(lock);
         } else if (HelperThreadState().canStartParseTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_PARSE);
-            handleParseWorkload(lock);
+            handleParseWorkload(lock, stackLimit);
         } else if (HelperThreadState().canStartCompressionTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_COMPRESS);
             handleCompressionWorkload(lock);
         } else if (HelperThreadState().canStartGCHelperTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCHELPER);
             handleGCHelperWorkload(lock);
         } else if (HelperThreadState().canStartGCParallelTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCPARALLEL);
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -343,17 +343,17 @@ struct HelperThread
         if (currentTask.isSome() && currentTask->is<T>())
             return currentTask->as<T>();
 
         return nullptr;
     }
 
     void handleWasmWorkload(AutoLockHelperThreadState& locked);
     void handleIonWorkload(AutoLockHelperThreadState& locked);
-    void handleParseWorkload(AutoLockHelperThreadState& locked);
+    void handleParseWorkload(AutoLockHelperThreadState& locked, uintptr_t stackLimit);
     void handleCompressionWorkload(AutoLockHelperThreadState& locked);
     void handleGCHelperWorkload(AutoLockHelperThreadState& locked);
     void handleGCParallelWorkload(AutoLockHelperThreadState& locked);
 };
 
 /* Methods for interacting with helper threads. */
 
 // Create data structures used by helper threads.
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -79,18 +79,17 @@ void
 js::DisableExtraThreads()
 {
     gCanUseExtraThreads = false;
 }
 
 const JSSecurityCallbacks js::NullSecurityCallbacks = { };
 
 PerThreadData::PerThreadData(JSRuntime* runtime)
-  : PerThreadDataFriendFields(),
-    runtime_(runtime),
+  : runtime_(runtime),
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     autoFlushICache_(nullptr),
     dtoaState(nullptr),
     suppressGC(0),
 #ifdef DEBUG
     ionCompiling(false),
@@ -136,19 +135,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 #ifdef DEBUG
     ionBailAfter_(0),
 #endif
     activation_(nullptr),
     profilingActivation_(nullptr),
     profilerSampleBufferGen_(0),
     profilerSampleBufferLapCount_(1),
     wasmActivationStack_(nullptr),
-    asyncStackForNewActivations(this),
-    asyncCauseForNewActivations(nullptr),
-    asyncCallIsExplicit(false),
     entryMonitor(nullptr),
     noExecuteDebuggerTop(nullptr),
     parentRuntime(parentRuntime),
 #ifdef DEBUG
     updateChildRuntimeCount(parentRuntime),
 #endif
     interrupt_(false),
     telemetryCallback(nullptr),
@@ -597,36 +593,16 @@ InvokeInterruptCallback(JSContext* cx)
         chars = u"(stack not available)";
     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
                                    JSMSG_TERMINATED, chars);
 
     return false;
 }
 
 void
-JSRuntime::resetJitStackLimit()
-{
-    // Note that, for now, we use the untrusted limit for ion. This is fine,
-    // because it's the most conservative limit, and if we hit it, we'll bail
-    // out of ion into the interpreter, which will do a proper recursion check.
-#ifdef JS_SIMULATOR
-    jitStackLimit_ = jit::Simulator::StackLimit();
-#else
-    jitStackLimit_ = mainThread.nativeStackLimit[StackForUntrustedScript];
-#endif
-    jitStackLimitNoInterrupt_ = jitStackLimit_;
-}
-
-void
-JSRuntime::initJitStackLimit()
-{
-    resetJitStackLimit();
-}
-
-void
 JSRuntime::requestInterrupt(InterruptMode mode)
 {
     interrupt_ = true;
     jitStackLimit_ = UINTPTR_MAX;
 
     if (mode == JSRuntime::RequestInterruptUrgent) {
         // If this interrupt is urgent (slow script dialog and garbage
         // collection among others), take additional steps to
@@ -642,17 +618,17 @@ JSRuntime::requestInterrupt(InterruptMod
 }
 
 bool
 JSRuntime::handleInterrupt(JSContext* cx)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
     if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) {
         interrupt_ = false;
-        resetJitStackLimit();
+        cx->resetJitStackLimit();
         return InvokeInterruptCallback(cx);
     }
     return true;
 }
 
 bool
 JSRuntime::setDefaultLocale(const char* locale)
 {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -243,17 +243,17 @@ CanUseExtraThreads()
 void DisableExtraThreads();
 
 /*
  * Encapsulates portions of the runtime/context that are tied to a
  * single active thread.  Instances of this structure can occur for
  * the main thread as |JSRuntime::mainThread|, for select operations
  * performed off thread, such as parsing.
  */
-class PerThreadData : public PerThreadDataFriendFields
+class PerThreadData
 {
     /*
      * Backpointer to the full shared JSRuntime* with which this
      * thread is associated.  This is private because accessing the
      * fields of this runtime can provoke race conditions, so the
      * intention is that access will be mediated through safe
      * functions like |runtimeFromMainThread| and |associatedWith()| below.
      */
@@ -367,26 +367,23 @@ struct JSRuntime : public JS::shadow::Ru
 
      /*
      * Points to the most recent JitActivation pushed on the thread.
      * See JitActivation constructor in vm/Stack.cpp
      */
     js::jit::JitActivation* jitActivation;
 
     /* See comment for JSRuntime::interrupt_. */
-  private:
+  protected:
     mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
-    void resetJitStackLimit();
 
     // Like jitStackLimit_, but not reset to trigger interrupts.
     uintptr_t jitStackLimitNoInterrupt_;
 
   public:
-    void initJitStackLimit();
-
     uintptr_t jitStackLimit() const { return jitStackLimit_; }
 
     // For read-only JIT use:
     void* addressOfJitStackLimit() { return &jitStackLimit_; }
     static size_t offsetOfJitStackLimit() { return offsetof(JSRuntime, jitStackLimit_); }
 
     void* addressOfJitStackLimitNoInterrupt() { return &jitStackLimitNoInterrupt_; }
 
@@ -443,38 +440,16 @@ struct JSRuntime : public JS::shadow::Ru
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferGen_;
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferLapCount_;
 
     /* See WasmActivation comment. */
     js::WasmActivation * volatile wasmActivationStack_;
 
   public:
-    /*
-     * Youngest frame of a saved stack that will be picked up as an async stack
-     * by any new Activation, and is nullptr when no async stack should be used.
-     *
-     * The JS::AutoSetAsyncStackForNewCalls class can be used to set this.
-     *
-     * New activations will reset this to nullptr on construction after getting
-     * the current value, and will restore the previous value on destruction.
-     */
-    JS::PersistentRooted<js::SavedFrame*> asyncStackForNewActivations;
-
-    /*
-     * Value of asyncCause to be attached to asyncStackForNewActivations.
-     */
-    const char* asyncCauseForNewActivations;
-
-    /*
-     * True if the async call was explicitly requested, e.g. via
-     * callFunctionWithAsyncStack.
-     */
-    bool asyncCallIsExplicit;
-
     /* If non-null, report JavaScript entry points to this monitor. */
     JS::dbg::AutoEntryMonitor* entryMonitor;
 
     /*
      * Stack of debuggers that currently disallow debuggee execution.
      *
      * When we check for NX we are inside the debuggee compartment, and thus a
      * stack of Debuggers that have prevented execution need to be tracked to
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -866,40 +866,40 @@ ActivationEntryMonitor::~ActivationEntry
         entryMonitor_->Exit(cx_);
 
     cx_->runtime()->entryMonitor = entryMonitor_;
 }
 
 Activation::Activation(JSContext* cx, Kind kind)
   : cx_(cx),
     compartment_(cx->compartment()),
-    prev_(cx->runtime_->activation_),
+    prev_(cx->activation_),
     prevProfiling_(prev_ ? prev_->mostRecentProfiling() : nullptr),
     hideScriptedCallerCount_(0),
     frameCache_(cx),
-    asyncStack_(cx, cx->runtime_->asyncStackForNewActivations),
-    asyncCause_(cx->runtime_->asyncCauseForNewActivations),
-    asyncCallIsExplicit_(cx->runtime_->asyncCallIsExplicit),
+    asyncStack_(cx, cx->asyncStackForNewActivations),
+    asyncCause_(cx->asyncCauseForNewActivations),
+    asyncCallIsExplicit_(cx->asyncCallIsExplicit),
     kind_(kind)
 {
-    cx->runtime_->asyncStackForNewActivations = nullptr;
-    cx->runtime_->asyncCauseForNewActivations = nullptr;
-    cx->runtime_->asyncCallIsExplicit = false;
-    cx->runtime_->activation_ = this;
+    cx->asyncStackForNewActivations = nullptr;
+    cx->asyncCauseForNewActivations = nullptr;
+    cx->asyncCallIsExplicit = false;
+    cx->activation_ = this;
 }
 
 Activation::~Activation()
 {
-    MOZ_ASSERT_IF(isProfiling(), this != cx_->runtime()->profilingActivation_);
-    MOZ_ASSERT(cx_->runtime_->activation_ == this);
+    MOZ_ASSERT_IF(isProfiling(), this != cx_->profilingActivation_);
+    MOZ_ASSERT(cx_->activation_ == this);
     MOZ_ASSERT(hideScriptedCallerCount_ == 0);
-    cx_->runtime_->activation_ = prev_;
-    cx_->runtime_->asyncCauseForNewActivations = asyncCause_;
-    cx_->runtime_->asyncStackForNewActivations = asyncStack_;
-    cx_->runtime_->asyncCallIsExplicit = asyncCallIsExplicit_;
+    cx_->activation_ = prev_;
+    cx_->asyncCauseForNewActivations = asyncCause_;
+    cx_->asyncStackForNewActivations = asyncStack_;
+    cx_->asyncCallIsExplicit = asyncCallIsExplicit_;
 }
 
 bool
 Activation::isProfiling() const
 {
     if (isInterpreter())
         return asInterpreter()->isProfiling();
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1336,49 +1336,49 @@ ActivationEntryMonitor::ActivationEntryM
   : cx_(cx), entryMonitor_(cx->runtime()->entryMonitor)
 {
     cx->runtime()->entryMonitor = nullptr;
 }
 
 Value
 ActivationEntryMonitor::asyncStack(JSContext* cx)
 {
-    RootedValue stack(cx, ObjectOrNullValue(cx->runtime()->asyncStackForNewActivations));
+    RootedValue stack(cx, ObjectOrNullValue(cx->asyncStackForNewActivations));
     if (!cx->compartment()->wrap(cx, &stack)) {
         cx->clearPendingException();
         return UndefinedValue();
     }
     return stack;
 }
 
 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, InterpreterFrame* entryFrame)
   : ActivationEntryMonitor(cx)
 {
     if (entryMonitor_) {
         // The InterpreterFrame is not yet part of an Activation, so it won't
         // be traced if we trigger GC here. Suppress GC to avoid this.
         gc::AutoSuppressGC suppressGC(cx);
         RootedValue stack(cx, asyncStack(cx));
-        const char* asyncCause = cx->runtime()->asyncCauseForNewActivations;
+        const char* asyncCause = cx->asyncCauseForNewActivations;
         if (entryFrame->isFunctionFrame())
             entryMonitor_->Entry(cx, &entryFrame->callee(), stack, asyncCause);
         else
             entryMonitor_->Entry(cx, entryFrame->script(), stack, asyncCause);
     }
 }
 
 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, jit::CalleeToken entryToken)
   : ActivationEntryMonitor(cx)
 {
     if (entryMonitor_) {
         // The CalleeToken is not traced at this point and we also don't want
         // a GC to discard the code we're about to enter, so we suppress GC.
         gc::AutoSuppressGC suppressGC(cx);
         RootedValue stack(cx, asyncStack(cx));
-        const char* asyncCause = cx->runtime()->asyncCauseForNewActivations;
+        const char* asyncCause = cx->asyncCauseForNewActivations;
         if (jit::CalleeTokenIsFunction(entryToken))
             entryMonitor_->Entry(cx_, jit::CalleeTokenToFunction(entryToken), stack, asyncCause);
         else
             entryMonitor_->Entry(cx_, jit::CalleeTokenToScript(entryToken), stack, asyncCause);
     }
 }
 
 /*****************************************************************************/