From e379bd0e94aa235c26c7e24095265a84e4924c5f Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Wed, 14 Feb 2024 02:01:37 +0000 Subject: [PATCH] Bug 1803810 - Part 4: Add mozJSModuleLoader for non-shared global. r=jonco On the main thread, single mozJSModuleLoader instance is shared across all loads in non-shared global, with resetting the internal state after importing a module graph. NonSharedGlobalSyncModuleLoaderScope manages the lifetime of each usage. Import into the same non-shared global can be nested, but import into the different non-shared gloobal is not allowed while other import for non-shared global is ongoing. Differential Revision: https://phabricator.services.mozilla.com/D199456 --- js/loader/ModuleLoaderBase.cpp | 4 + js/loader/ModuleLoaderBase.h | 2 + js/xpconnect/loader/mozJSModuleLoader.cpp | 91 ++++++++++++++++++++++- js/xpconnect/loader/mozJSModuleLoader.h | 72 +++++++++++++++++- 4 files changed, 165 insertions(+), 4 deletions(-) diff --git a/js/loader/ModuleLoaderBase.cpp b/js/loader/ModuleLoaderBase.cpp index cc5284702979..59e77b2d9c35 100644 --- a/js/loader/ModuleLoaderBase.cpp +++ b/js/loader/ModuleLoaderBase.cpp @@ -1047,6 +1047,10 @@ void ModuleLoaderBase::Shutdown() { mLoader = nullptr; } +bool ModuleLoaderBase::HasFetchingModules() const { + return !mFetchingModules.IsEmpty(); +} + bool ModuleLoaderBase::HasPendingDynamicImports() const { return !mDynamicImportRequests.isEmpty(); } diff --git a/js/loader/ModuleLoaderBase.h b/js/loader/ModuleLoaderBase.h index 86879db84bb9..2c2c385a3081 100644 --- a/js/loader/ModuleLoaderBase.h +++ b/js/loader/ModuleLoaderBase.h @@ -282,6 +282,8 @@ class ModuleLoaderBase : public nsISupports { nsIGlobalObject* GetGlobalObject() const { return mGlobalObject; } + bool HasFetchingModules() const; + bool HasPendingDynamicImports() const; void CancelDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult); #ifdef DEBUG diff --git a/js/xpconnect/loader/mozJSModuleLoader.cpp b/js/xpconnect/loader/mozJSModuleLoader.cpp index 625025bda106..bc30d6825338 100644 --- a/js/xpconnect/loader/mozJSModuleLoader.cpp +++ b/js/xpconnect/loader/mozJSModuleLoader.cpp @@ -5,8 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ScriptLoadRequest.h" +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF #include "mozilla/Attributes.h" #include "mozilla/ArrayUtils.h" // mozilla::ArrayLength +#include "mozilla/RefPtr.h" // RefPtr, mozilla::StaticRefPtr #include "mozilla/Utf8.h" // mozilla::Utf8Unit #include @@ -488,6 +490,35 @@ mozJSModuleLoader* mozJSModuleLoader::GetOrCreateDevToolsLoader() { return sDevToolsLoader; } +void mozJSModuleLoader::InitSyncModuleLoaderForGlobal( + nsIGlobalObject* aGlobal) { + MOZ_ASSERT(!mLoaderGlobal); + MOZ_ASSERT(!mModuleLoader); + + RefPtr scriptLoader = new SyncScriptLoader; + mModuleLoader = new SyncModuleLoader(scriptLoader, aGlobal); + mLoaderGlobal = aGlobal->GetGlobalJSObject(); +} + +void mozJSModuleLoader::DisconnectSyncModuleLoaderFromGlobal() { + MOZ_ASSERT(mLoaderGlobal); + MOZ_ASSERT(mModuleLoader); + + mLoaderGlobal = nullptr; + Unload(); +} + +/* static */ +bool mozJSModuleLoader::IsSharedSystemGlobal(nsIGlobalObject* aGlobal) { + return sSelf->IsLoaderGlobal(aGlobal->GetGlobalJSObject()); +} + +/* static */ +bool mozJSModuleLoader::IsDevToolsLoaderGlobal(nsIGlobalObject* aGlobal) { + return sDevToolsLoader && + sDevToolsLoader->IsLoaderGlobal(aGlobal->GetGlobalJSObject()); +} + // This requires that the keys be strings and the values be pointers. template static size_t SizeOfTableExcludingThis( @@ -695,10 +726,12 @@ nsresult mozJSModuleLoader::LoadSingleModuleScript( #ifdef STARTUP_RECORDER_ENABLED if (aModuleLoader == sSelf->mModuleLoader) { sSelf->RecordImportStack(aCx, aRequest); - } else { - MOZ_ASSERT(sDevToolsLoader); - MOZ_ASSERT(aModuleLoader == sDevToolsLoader->mModuleLoader); + } else if (sDevToolsLoader && + aModuleLoader == sDevToolsLoader->mModuleLoader) { sDevToolsLoader->RecordImportStack(aCx, aRequest); + } else { + // NOTE: Do not record import stack for non-shared globals, given the + // loader is associated with the global only while importing. } #endif @@ -1927,6 +1960,58 @@ size_t mozJSModuleLoader::ModuleEntry::SizeOfIncludingThis( //---------------------------------------------------------------------- +/* static */ +mozJSModuleLoader* NonSharedGlobalSyncModuleLoaderScope::sActiveLoader = + nullptr; + +NonSharedGlobalSyncModuleLoaderScope::NonSharedGlobalSyncModuleLoaderScope( + JSContext* aCx, nsIGlobalObject* aGlobal) { + // Only the main thread is supported for now. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mozJSModuleLoader::IsSharedSystemGlobal(aGlobal)); + MOZ_ASSERT(!mozJSModuleLoader::IsDevToolsLoaderGlobal(aGlobal)); + + mAsyncModuleLoader = aGlobal->GetModuleLoader(aCx); + MOZ_ASSERT(mAsyncModuleLoader, + "The consumer should guarantee the global returns non-null module " + "loader"); + + mLoader = new mozJSModuleLoader(); + RegisterWeakMemoryReporter(mLoader); + mLoader->InitSyncModuleLoaderForGlobal(aGlobal); + + mAsyncModuleLoader->CopyModulesTo(mLoader->mModuleLoader); + + mMaybeOverride.emplace(mAsyncModuleLoader, mLoader->mModuleLoader); + + MOZ_ASSERT(!sActiveLoader); + sActiveLoader = mLoader; +} + +NonSharedGlobalSyncModuleLoaderScope::~NonSharedGlobalSyncModuleLoaderScope() { + MOZ_ASSERT(sActiveLoader == mLoader); + sActiveLoader = nullptr; + + mLoader->DisconnectSyncModuleLoaderFromGlobal(); + UnregisterWeakMemoryReporter(mLoader); +} + +void NonSharedGlobalSyncModuleLoaderScope::Finish() { + mLoader->mModuleLoader->MoveModulesTo(mAsyncModuleLoader); +} + +/* static */ +bool NonSharedGlobalSyncModuleLoaderScope::IsActive() { + return !!sActiveLoader; +} + +/* static */ +mozJSModuleLoader* NonSharedGlobalSyncModuleLoaderScope::ActiveLoader() { + return sActiveLoader; +} + +//---------------------------------------------------------------------- + JSCLContextHelper::JSCLContextHelper(JSContext* aCx) : mContext(aCx), mBuf(nullptr) {} diff --git a/js/xpconnect/loader/mozJSModuleLoader.h b/js/xpconnect/loader/mozJSModuleLoader.h index 39b9a476faa3..6a93c5a529aa 100644 --- a/js/xpconnect/loader/mozJSModuleLoader.h +++ b/js/xpconnect/loader/mozJSModuleLoader.h @@ -8,9 +8,12 @@ #define mozJSModuleLoader_h #include "SyncModuleLoader.h" +#include "mozilla/Attributes.h" // MOZ_STACK_CLASS #include "mozilla/dom/ScriptSettings.h" #include "mozilla/FileLocation.h" +#include "mozilla/Maybe.h" // mozilla::Maybe #include "mozilla/MemoryReporting.h" +#include "mozilla/RefPtr.h" // RefPtr, mozilla::StaticRefPtr #include "mozilla/StaticPtr.h" #include "nsIMemoryReporter.h" #include "nsISupports.h" @@ -37,6 +40,12 @@ class ModuleLoadRequest; # define STARTUP_RECORDER_ENABLED #endif +namespace mozilla::loader { + +class NonSharedGlobalSyncModuleLoaderScope; + +} // namespace mozilla::loader + class mozJSModuleLoader final : public nsIMemoryReporter { public: NS_DECL_ISUPPORTS @@ -67,6 +76,13 @@ class mozJSModuleLoader final : public nsIMemoryReporter { JSObject* GetSharedGlobal(JSContext* aCx); + private: + void InitSyncModuleLoaderForGlobal(nsIGlobalObject* aGlobal); + void DisconnectSyncModuleLoaderFromGlobal(); + + friend class mozilla::loader::NonSharedGlobalSyncModuleLoaderScope; + + public: static mozJSModuleLoader* GetDevToolsLoader() { return sDevToolsLoader; } static mozJSModuleLoader* GetOrCreateDevToolsLoader(); @@ -117,6 +133,9 @@ class mozJSModuleLoader final : public nsIMemoryReporter { bool IsLoaderGlobal(JSObject* aObj) { return mLoaderGlobal == aObj; } bool IsDevToolsLoader() const { return this == sDevToolsLoader; } + static bool IsSharedSystemGlobal(nsIGlobalObject* aGlobal); + static bool IsDevToolsLoaderGlobal(nsIGlobalObject* aGlobal); + // Public methods for use from SyncModuleLoader. static bool IsTrustedScheme(nsIURI* aURI); static nsresult LoadSingleModuleScript( @@ -261,4 +280,55 @@ class mozJSModuleLoader final : public nsIMemoryReporter { RefPtr mModuleLoader; }; -#endif +namespace mozilla::loader { + +// Automatically allocate and initialize a sync module loader for given +// non-shared global, and override the module loader for the global with sync +// module loader. +// +// This is not re-entrant, and the consumer must check IsActive method before +// allocating this on the stack. +// +// The consumer should ensure the target global's module loader has no +// ongoing fetching modules (ModuleLoaderBase::HasFetchingModules). +// If there's any fetching modules, the consumer should wait for them before +// allocating this class on the stack. +// +// The consumer should also verify that the target global has module loader, +// as a part of the above step. +// +// The loader returned by ActiveLoader can be reused only when +// ActiveLoader's global matches the global the consumer wants to use. +class MOZ_STACK_CLASS NonSharedGlobalSyncModuleLoaderScope { + public: + NonSharedGlobalSyncModuleLoaderScope(JSContext* aCx, + nsIGlobalObject* aGlobal); + ~NonSharedGlobalSyncModuleLoaderScope(); + + // After successfully importing a module graph, move all imported modules to + // the target global's module loader. + void Finish(); + + // Returns true if another instance of NonSharedGlobalSyncModuleLoaderScope + // is on stack. + static bool IsActive(); + + static mozJSModuleLoader* ActiveLoader(); + + private: + RefPtr mLoader; + + // The module loader on the stack. + // This is used by another sync module load during a sync module load is + // ongoing. + static mozJSModuleLoader* sActiveLoader; + + // The module loader of the target global. + RefPtr mAsyncModuleLoader; + + mozilla::Maybe mMaybeOverride; +}; + +} // namespace mozilla::loader + +#endif // mozJSModuleLoader_h