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
--- 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"