bug 1442776 make CycleCollectedJSContext accessible from JSContext private r=peterv draft
authorKarl Tomlinson <karlt+@karlt.net>
Thu, 10 May 2018 17:04:12 +1200
changeset 804498 3c2accb71faf3f017a44c405ae0484e57aaf039c
parent 804497 522ccf7dbf28bbfb881a4c13dfb62f369d7791d7
child 804503 a8be75ac9f0dbdce831566853e26c663cd9b0691
child 804504 90f3bd1e6fe022977dff17bd67dfec05d5c94777
push id112390
push userktomlinson@mozilla.com
push dateWed, 06 Jun 2018 03:40:45 +0000
reviewerspeterv
bugs1442776
milestone62.0a1
bug 1442776 make CycleCollectedJSContext accessible from JSContext private r=peterv Inheriting PerThreadAtomCache on CycleCollectedJSContext permits use of static_cast, avoiding one level of indirection compared to adding a CycleCollectedJSContext* to PerThreadAtomCache. PerThreadAtomCache is over 18kB, and so WorkerJSContext and WorkletJSContext are moved from the stack to the heap. MozReview-Commit-ID: 6jdJeZcviK4
dom/base/CustomElementRegistry.h
dom/bindings/BindingUtils.h
dom/geolocation/PositionError.cpp
dom/geolocation/nsGeolocation.cpp
dom/plugins/base/nsNPAPIPlugin.cpp
dom/serviceworkers/ServiceWorkerPrivate.cpp
dom/workers/RuntimeService.cpp
dom/worklet/WorkletThread.cpp
js/xpconnect/src/XPCJSContext.cpp
xpcom/base/CycleCollectedJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
xpcom/base/nsTraceRefcnt.cpp
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_CustomElementRegistry_h
 #define mozilla_dom_CustomElementRegistry_h
 
 #include "js/GCHashTable.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsGenericHTMLElement.h"
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -9,17 +9,16 @@
 
 #include "jsfriendapi.h"
 #include "js/Wrapper.h"
 #include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/Nullable.h"
--- a/dom/geolocation/PositionError.cpp
+++ b/dom/geolocation/PositionError.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/PositionError.h"
 #include "mozilla/dom/PositionErrorBinding.h"
+#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
 #include "nsGeolocation.h"
 
 namespace mozilla {
 namespace dom {
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PositionError, mParent)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PositionError, AddRef)
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGeolocation.h"
 
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/PositionError.h"
 #include "mozilla/dom/PositionErrorBinding.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtr.h"
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -15,16 +15,17 @@
 
 #include "nsPluginHost.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsNPAPIPluginStreamListener.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "nsIServiceManager.h"
 #include "nsThreadUtils.h"
+#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
 #include "mozilla/Preferences.h"
 #include "nsPluginInstanceOwner.h"
 
 #include "nsPluginsDir.h"
 #include "nsPluginLogging.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -18,16 +18,17 @@
 #include "nsITimedChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable
 #include "mozilla/JSObjectHolder.h"
 #include "mozilla/dom/Client.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/NotificationEvent.h"
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -815,60 +815,16 @@ ConsumeStream(JSContext* aCx,
     JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                               JSMSG_ERROR_CONSUMING_RESPONSE);
     return false;
   }
 
   return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
 }
 
-class WorkerJSContext;
-
-class WorkerThreadContextPrivate : private PerThreadAtomCache
-{
-  friend class WorkerJSContext;
-
-  WorkerPrivate* mWorkerPrivate;
-
-public:
-  // This can't return null, but we can't lose the "Get" prefix in the name or
-  // it will be ambiguous with the WorkerPrivate class name.
-  WorkerPrivate*
-  GetWorkerPrivate() const
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(mWorkerPrivate);
-
-    return mWorkerPrivate;
-  }
-
-private:
-  explicit
-  WorkerThreadContextPrivate(WorkerPrivate* aWorkerPrivate)
-    : mWorkerPrivate(aWorkerPrivate)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    // Zero out the base class members.
-    memset(this, 0, sizeof(PerThreadAtomCache));
-
-    MOZ_ASSERT(mWorkerPrivate);
-  }
-
-  ~WorkerThreadContextPrivate()
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  WorkerThreadContextPrivate(const WorkerThreadContextPrivate&) = delete;
-
-  WorkerThreadContextPrivate&
-  operator=(const WorkerThreadContextPrivate&) = delete;
-};
-
 bool
 InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
 
   JSSettings settings;
   aWorkerPrivate->CopyJSSettings(settings);
@@ -1040,17 +996,21 @@ public:
       nsCycleCollector_collect(nullptr);
     }
   }
 
 private:
   WorkerPrivate* mWorkerPrivate;
 };
 
-class MOZ_STACK_CLASS WorkerJSContext final : public mozilla::CycleCollectedJSContext
+} // anonymous namespace
+
+} // workerinternals namespace
+
+class WorkerJSContext final : public mozilla::CycleCollectedJSContext
 {
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside InitJSContextForWorker.
   explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate)
   {
     MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
@@ -1064,30 +1024,29 @@ public:
   ~WorkerJSContext()
   {
     MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
     JSContext* cx = MaybeContext();
     if (!cx) {
       return;   // Initialize() must have failed
     }
 
-    delete static_cast<WorkerThreadContextPrivate*>(JS_GetContextPrivate(cx));
-    JS_SetContextPrivate(cx, nullptr);
-
     // The worker global should be unrooted and the shutdown cycle collection
     // should break all remaining cycles. The superclass destructor will run
     // the GC one final time and finalize any JSObjects that were participating
     // 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;
   }
 
+  WorkerJSContext* GetAsWorkerJSContext() override { return this; }
+
   CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override
   {
     return new WorkerJSRuntime(aCx, mWorkerPrivate);
   }
 
   nsresult Initialize(JSRuntime* aParentRuntime)
   {
     nsresult rv =
@@ -1095,18 +1054,16 @@ public:
                                           WORKER_DEFAULT_RUNTIME_HEAPSIZE,
                                           WORKER_DEFAULT_NURSERY_SIZE);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
 
     JSContext* cx = Context();
 
-    JS_SetContextPrivate(cx, new WorkerThreadContextPrivate(mWorkerPrivate));
-
     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
     JS_InitDestroyPrincipalsCallback(cx, DestroyWorkerPrincipals);
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     if (mWorkerPrivate->IsDedicatedWorker()) {
       JS_SetFutexCanWait(cx);
     }
 
     return NS_OK;
@@ -1143,20 +1100,29 @@ public:
     microTaskQueue->push(runnable.forget());
   }
 
   bool IsSystemCaller() const override
   {
     return mWorkerPrivate->UsesSystemPrincipal();
   }
 
+  WorkerPrivate* GetWorkerPrivate() const
+  {
+    return mWorkerPrivate;
+  }
+
 private:
   WorkerPrivate* mWorkerPrivate;
 };
 
+namespace workerinternals {
+
+namespace {
+
 class WorkerThreadPrimaryRunnable final : public Runnable
 {
   WorkerPrivate* mWorkerPrivate;
   RefPtr<WorkerThread> mThread;
   JSRuntime* mParentRuntime;
 
   class FinishedRunnable final : public Runnable
   {
@@ -2713,23 +2679,23 @@ WorkerThreadPrimaryRunnable::Run()
 
   SetThreadHelper threadHelper(mWorkerPrivate, mThread);
 
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   {
     nsCycleCollector_startup();
 
-    WorkerJSContext context(mWorkerPrivate);
-    nsresult rv = context.Initialize(mParentRuntime);
+    auto context = MakeUnique<WorkerJSContext>(mWorkerPrivate);
+    nsresult rv = context->Initialize(mParentRuntime);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    JSContext* cx = context.Context();
+    JSContext* cx = context->Context();
 
     if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
       // XXX need to fire an error at parent.
       NS_ERROR("Failed to create context!");
       return NS_ERROR_FAILURE;
     }
 
     {
@@ -2862,43 +2828,49 @@ ResumeWorkersForWindow(nsPIDOMWindowInne
 }
 
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aCx);
 
-  void* cxPrivate = JS_GetContextPrivate(aCx);
-  if (!cxPrivate) {
+  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(aCx);
+  if (!ccjscx) {
     return nullptr;
   }
 
-  return
-    static_cast<WorkerThreadContextPrivate*>(cxPrivate)->GetWorkerPrivate();
+  WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
+  // GetWorkerPrivateFromContext is called only for worker contexts.  The
+  // context private is cleared early in ~CycleCollectedJSContext() and so
+  // GetFor() returns null above if called after ccjscx is no longer a
+  // WorkerJSContext.
+  MOZ_ASSERT(workerjscx);
+  return workerjscx->GetWorkerPrivate();
 }
 
 WorkerPrivate*
 GetCurrentThreadWorkerPrivate()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
   if (!ccjscx) {
     return nullptr;
   }
 
-  JSContext* cx = ccjscx->Context();
-  MOZ_ASSERT(cx);
-
-  // Note that we can return nullptr if the nsCycleCollector_shutdown() in
-  // ~WorkerJSContext() triggers any calls to GetCurrentThreadWorkerPrivate().
-  // At this stage CycleCollectedJSContext::Get() will still return a context,
-  // but the context private has already been cleared.
-  return GetWorkerPrivateFromContext(cx);
+  WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
+  // Although GetCurrentThreadWorkerPrivate() is called only for worker
+  // threads, the ccjscx will no longer be a WorkerJSContext if called from
+  // stable state events during ~CycleCollectedJSContext().
+  if (!workerjscx) {
+    return nullptr;
+  }
+
+  return workerjscx->GetWorkerPrivate();
 }
 
 bool
 IsCurrentThreadRunningWorker()
 {
   return !NS_IsMainThread() && !!GetCurrentThreadWorkerPrivate();
 }
 
--- a/dom/worklet/WorkletThread.cpp
+++ b/dom/worklet/WorkletThread.cpp
@@ -22,53 +22,16 @@ namespace {
 
 // The size of the generational GC nursery for worklet, in bytes.
 #define WORKLET_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
 
 // The C stack size. We use the same stack size on all platforms for
 // consistency.
 const uint32_t kWorkletStackSize = 256 * sizeof(size_t) * 1024;
 
-// This class is allocated per thread and can be retrieved from CC.
-// It's used to get the current WorkletThread object.
-class WorkletThreadContextPrivate : private PerThreadAtomCache
-{
-public:
-  explicit
-  WorkletThreadContextPrivate(WorkletThread* aWorkletThread)
-    : mWorkletThread(aWorkletThread)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    // Zero out the base class members.
-    memset(this, 0, sizeof(PerThreadAtomCache));
-
-    MOZ_ASSERT(mWorkletThread);
-  }
-
-  ~WorkletThreadContextPrivate()
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  WorkletThread*
-  GetWorkletThread() const
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(mWorkletThread);
-    return mWorkletThread;
-  }
-
-private:
-  WorkletThreadContextPrivate(const WorkletThreadContextPrivate&) = delete;
-  WorkletThreadContextPrivate& operator=(const WorkletThreadContextPrivate&) = delete;
-
-  RefPtr<WorkletThread> mWorkletThread;
-};
-
 // Helper functions
 
 bool
 PreserveWrapper(JSContext* aCx, JSObject* aObj)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aObj);
   MOZ_ASSERT(mozilla::dom::IsDOMObject(aObj));
@@ -165,19 +128,16 @@ public:
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     JSContext* cx = MaybeContext();
     if (!cx) {
       return;   // Initialize() must have failed
     }
 
-    delete static_cast<WorkletThreadContextPrivate*>(JS_GetContextPrivate(cx));
-    JS_SetContextPrivate(cx, nullptr);
-
     nsCycleCollector_shutdown();
   }
 
   WorkletJSContext* GetAsWorkletJSContext() override { return this; }
 
   CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override
   {
     return new WorkletJSRuntime(aCx);
@@ -193,18 +153,16 @@ public:
                                           WORKLET_DEFAULT_RUNTIME_HEAPSIZE,
                                           WORKLET_DEFAULT_NURSERY_SIZE);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
 
     JSContext* cx = Context();
 
-    JS_SetContextPrivate(cx, new WorkletThreadContextPrivate(mWorkletThread));
-
     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
     JS_InitDestroyPrincipalsCallback(cx, DestroyWorkletPrincipals);
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     JS_SetFutexCanWait(cx);
 
     return NS_OK;
   }
 
@@ -225,16 +183,21 @@ public:
 
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     MOZ_ASSERT(global);
 #endif
 
     GetMicroTaskQueue().push(runnable.forget());
   }
 
+  WorkletThread* GetWorkletThread() const
+  {
+    return mWorkletThread;
+  }
+
   bool IsSystemCaller() const override
   {
     // Currently no support for special system worklet privileges.
     return false;
   }
 
 private:
   RefPtr<WorkletThread> mWorkletThread;
@@ -368,39 +331,39 @@ WorkletThread::DelayedDispatch(already_A
 
 void
 WorkletThread::RunEventLoop(JSRuntime* aParentRuntime)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   PR_SetCurrentThreadName("worklet");
 
-  WorkletJSContext context(this);
-  nsresult rv = context.Initialize(aParentRuntime);
+  auto context = MakeUnique<WorkletJSContext>(this);
+  nsresult rv = context->Initialize(aParentRuntime);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // TODO: error propagation
     return;
   }
 
   // FIXME: JS_SetDefaultLocale
   // FIXME: JSSettings
   // FIXME: JS_SetNativeStackQuota
   // FIXME: JS_SetSecurityCallbacks
   // FIXME: JS::SetAsmJSCacheOps
   // FIXME: JS::SetAsyncTaskCallbacks
   // FIXME: JS_AddInterruptCallback
   // FIXME: JS::SetCTypesActivityCallback
   // FIXME: JS_SetGCZeal
 
-  if (!JS::InitSelfHostedCode(context.Context())) {
+  if (!JS::InitSelfHostedCode(context->Context())) {
     // TODO: error propagation
     return;
   }
 
-  mJSContext = context.Context();
+  mJSContext = context->Context();
 
   while (mJSContext) {
     MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(this, /* wait: */ true));
   }
 
   MOZ_ASSERT(mJSContext == nullptr);
 }
 
@@ -466,21 +429,19 @@ WorkletThread::AssertIsOnWorkletThread()
 /* static */ WorkletThread*
 WorkletThread::Get()
 {
   AssertIsOnWorkletThread();
 
   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
   MOZ_ASSERT(ccjscx);
 
-  void* cxPrivate = JS_GetContextPrivate(ccjscx->Context());
-  MOZ_ASSERT(cxPrivate);
-
-  return
-    static_cast<WorkletThreadContextPrivate*>(cxPrivate)->GetWorkletThread();
+  WorkletJSContext* workletjscx = ccjscx->GetAsWorkletJSContext();
+  MOZ_ASSERT(workletjscx);
+  return workletjscx->GetWorkletThread();
 }
 
 // nsIObserver
 NS_IMETHODIMP
 WorkletThread::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t*)
 {
   MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -33,17 +33,16 @@
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "nsContentUtils.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsCycleCollectionNoteRootCallback.h"
 #include "nsCycleCollector.h"
 #include "jsapi.h"
 #include "js/MemoryMetrics.h"
-#include "mozilla/dom/GeneratedAtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/extensions/WebExtensionPolicy.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
@@ -73,17 +72,16 @@
 #include <windows.h>
 #endif
 
 static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
 
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
-using mozilla::dom::PerThreadAtomCache;
 using mozilla::dom::AutoEntryScript;
 
 static void WatchdogMain(void* arg);
 class Watchdog;
 class WatchdogManager;
 class MOZ_RAII AutoLockWatchdog final
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -900,20 +898,16 @@ XPCJSContext::~XPCJSContext()
     } else {
         // Otherwise, simply remove ourselves from the list.
         mWatchdogManager->UnregisterContext(this);
     }
 
     if (mCallContext)
         mCallContext->SystemIsBeingShutDown();
 
-    auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(Context()));
-    delete rtPrivate;
-    JS_SetContextPrivate(Context(), nullptr);
-
     PROFILER_CLEAR_JS_CONTEXT();
 
     gTlsContext.set(nullptr);
 }
 
 XPCJSContext::XPCJSContext()
  : mCallContext(nullptr),
    mAutoRoots(nullptr),
@@ -1000,20 +994,16 @@ XPCJSContext::Initialize(XPCJSContext* a
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(Context());
     JSContext* cx = Context();
 
-    auto cxPrivate = new PerThreadAtomCache();
-    memset(cxPrivate, 0, sizeof(PerThreadAtomCache));
-    JS_SetContextPrivate(cx, cxPrivate);
-
     // The JS engine permits us to set different stack limits for system code,
     // trusted script, and untrusted script. We have tests that ensure that
     // we can always execute 10 "heavy" (eval+with) stack frames deeper in
     // privileged code. Our stack sizes vary greatly in different configurations,
     // so satisfying those tests requires some care. Manual measurements of the
     // number of heavy stack frames achievable gives us the following rough data,
     // ordered by the effective categories in which they are grouped in the
     // JS_SetNativeStackQuota call (which predates this analysis).
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -52,29 +52,36 @@ CycleCollectedJSContext::CycleCollectedJ
   , mRuntime(nullptr)
   , mJSContext(nullptr)
   , mDoingStableStates(false)
   , mTargetedMicroTaskRecursionDepth(0)
   , mMicroTaskLevel(0)
   , mMicroTaskRecursionDepth(0)
 {
   MOZ_COUNT_CTOR(CycleCollectedJSContext);
+
+  // Reinitialize PerThreadAtomCache because dom/bindings/Codegen.py compares
+  // against zero rather than JSID_VOID to detect uninitialized jsid members.
+  memset(static_cast<PerThreadAtomCache*>(this), 0, sizeof(PerThreadAtomCache));
+
   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
   mOwningThread = thread.forget().downcast<nsThread>().take();
   MOZ_RELEASE_ASSERT(mOwningThread);
 }
 
 CycleCollectedJSContext::~CycleCollectedJSContext()
 {
   MOZ_COUNT_DTOR(CycleCollectedJSContext);
   // If the allocation failed, here we are.
   if (!mJSContext) {
     return;
   }
 
+  JS_SetContextPrivate(mJSContext, nullptr);
+
   mRuntime->RemoveContext(this);
 
   if (mIsPrimaryContext) {
     mRuntime->Shutdown(mJSContext);
   }
 
   // Last chance to process any events.
   CleanupIDBTransactions(mBaseRecursionDepth);
@@ -124,16 +131,19 @@ CycleCollectedJSContext::InitializeCommo
   NS_GetCurrentThread()->SetCanInvokeJS(true);
 
   JS::SetGetIncumbentGlobalCallback(mJSContext, GetIncumbentGlobalCallback);
 
   JS::SetEnqueuePromiseJobCallback(mJSContext, EnqueuePromiseJobCallback, this);
   JS::SetPromiseRejectionTrackerCallback(mJSContext, PromiseRejectionTrackerCallback, this);
   mUncaughtRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
   mConsumedRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
+
+  // Cast to PerThreadAtomCache for dom::GetAtomCache(JSContext*).
+  JS_SetContextPrivate(mJSContext, static_cast<PerThreadAtomCache*>(this));
 }
 
 nsresult
 CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
                                     uint32_t aMaxBytes,
                                     uint32_t aMaxNurseryBytes)
 {
   MOZ_ASSERT(!mJSContext);
@@ -170,16 +180,25 @@ CycleCollectedJSContext::InitializeNonPr
 
   InitializeCommon();
 
   nsCycleCollector_registerNonPrimaryContext(this);
 
   return NS_OK;
 }
 
+/* static */ CycleCollectedJSContext*
+CycleCollectedJSContext::GetFor(JSContext* aCx)
+{
+  // Cast from void* matching JS_SetContextPrivate.
+  auto atomCache = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(aCx));
+  // Down cast.
+  return static_cast<CycleCollectedJSContext*>(atomCache);
+}
+
 size_t
 CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return 0;
 }
 
 class PromiseJobRunnable final : public MicroTaskRunnable
 {
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -8,16 +8,17 @@
 #define mozilla_CycleCollectedJSContext_h
 
 #include <queue>
 
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/AtomList.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 
 class nsCycleCollectionNoteRootCallback;
@@ -27,16 +28,17 @@ class nsWrapperCache;
 
 namespace mozilla {
 class AutoSlowOperation;
 
 class CycleCollectedJSRuntime;
 
 namespace dom {
 class Exception;
+class WorkerJSContext;
 class WorkletJSContext;
 } // namespace dom
 
 // Contains various stats about the cycle collection.
 struct CycleCollectorResults
 {
   CycleCollectorResults()
   {
@@ -78,17 +80,18 @@ public:
   NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
   virtual void Run(AutoSlowOperation& aAso) = 0;
   virtual bool Suppressed() { return false; }
 protected:
   virtual ~MicroTaskRunnable() {}
 };
 
 class CycleCollectedJSContext
-  : public LinkedListElement<CycleCollectedJSContext>
+  : dom::PerThreadAtomCache
+  , public LinkedListElement<CycleCollectedJSContext>
 {
   friend class CycleCollectedJSRuntime;
 
 protected:
   CycleCollectedJSContext();
   virtual ~CycleCollectedJSContext();
 
   MOZ_IS_CLASS_INIT
@@ -126,16 +129,17 @@ private:
   void CleanupIDBTransactions(uint32_t aRecursionDepth);
 
 public:
   enum DeferredFinalizeType {
     FinalizeIncrementally,
     FinalizeNow,
   };
 
+  virtual dom::WorkerJSContext* GetAsWorkerJSContext() { return nullptr; }
   virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; }
 
   CycleCollectedJSRuntime* Runtime() const
   {
     MOZ_ASSERT(mRuntime);
     return mRuntime;
   }
 
@@ -175,16 +179,21 @@ public:
 
   uint32_t RecursionDepth();
 
   // Run in stable state (call through nsContentUtils)
   void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
 
   void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
 
+  // Get the CycleCollectedJSContext for a JSContext.
+  // Returns null only if Initialize() has not completed on or during
+  // destruction of the CycleCollectedJSContext.
+  static CycleCollectedJSContext* GetFor(JSContext* aCx);
+
   // Get the current thread's CycleCollectedJSContext.  Returns null if there
   // isn't one.
   static CycleCollectedJSContext* Get();
 
   // Queue an async microtask to the current main or worker thread.
   virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable);
 
   // Call EnterMicroTask when you're entering JS execution.
--- a/xpcom/base/nsTraceRefcnt.cpp
+++ b/xpcom/base/nsTraceRefcnt.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTraceRefcnt.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Path.h"
 #include "mozilla/StaticPtr.h"
 #include "nsXPCOMPrivate.h"
 #include "nscore.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"