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
This commit is contained in:
Tooru Fujisawa 2024-02-14 02:01:37 +00:00
parent a380811687
commit e379bd0e94
4 changed files with 165 additions and 4 deletions

View File

@ -1047,6 +1047,10 @@ void ModuleLoaderBase::Shutdown() {
mLoader = nullptr;
}
bool ModuleLoaderBase::HasFetchingModules() const {
return !mFetchingModules.IsEmpty();
}
bool ModuleLoaderBase::HasPendingDynamicImports() const {
return !mDynamicImportRequests.isEmpty();
}

View File

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

View File

@ -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 <cstdarg>
@ -488,6 +490,35 @@ mozJSModuleLoader* mozJSModuleLoader::GetOrCreateDevToolsLoader() {
return sDevToolsLoader;
}
void mozJSModuleLoader::InitSyncModuleLoaderForGlobal(
nsIGlobalObject* aGlobal) {
MOZ_ASSERT(!mLoaderGlobal);
MOZ_ASSERT(!mModuleLoader);
RefPtr<SyncScriptLoader> 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 <class Key, class Data, class UserData, class Converter>
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) {}

View File

@ -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<mozilla::loader::SyncModuleLoader> 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<mozJSModuleLoader> 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<JS::loader::ModuleLoaderBase> mAsyncModuleLoader;
mozilla::Maybe<JS::loader::AutoOverrideModuleLoader> mMaybeOverride;
};
} // namespace mozilla::loader
#endif // mozJSModuleLoader_h