From 19409077f2063050859f498bfe41214311a9129c Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 3 Aug 2023 08:15:29 +0000 Subject: [PATCH] Bug 1842798 - Part 3: Complete dynamic imports asynchronously with a microtask r=smaug According to the spec this should happen as the result of resolving a promise (see ContinueDynamicImport). If this happens synchronously it's possible for the importing module to still be in the evaluating state when trying to instantiate and evaluate the dynamically imported module which breaks the module loader invariants. Differential Revision: https://phabricator.services.mozilla.com/D183876 --- js/loader/ModuleLoaderBase.cpp | 47 ++++++++++++++++++++++++++++++++++++++---- js/loader/ModuleLoaderBase.h | 2 ++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/js/loader/ModuleLoaderBase.cpp b/js/loader/ModuleLoaderBase.cpp index fbc00dde2fbd..78fe5cc86863 100644 --- a/js/loader/ModuleLoaderBase.cpp +++ b/js/loader/ModuleLoaderBase.cpp @@ -30,6 +30,7 @@ #include "nsNetUtil.h" // NS_NewURI #include "xpcpublic.h" +using mozilla::CycleCollectedJSContext; using mozilla::Err; using mozilla::Preferences; using mozilla::UniquePtr; @@ -1195,12 +1196,50 @@ nsresult ModuleLoaderBase::InitDebuggerDataForModuleGraph( } void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) { - MOZ_ASSERT(aRequest->mLoader == this); + // Instantiate and evaluate the imported module. + // See: https://tc39.es/ecma262/#sec-ContinueDynamicImport + // + // Since this is specced as happening on promise resolution (step 8) this must + // at least run as part of a microtask. We don't create the unobservable + // promise. + + class DynamicImportMicroTask : public mozilla::MicroTaskRunnable { + public: + explicit DynamicImportMicroTask(ModuleLoadRequest* aRequest) + : MicroTaskRunnable(), mRequest(aRequest) {} + + virtual void Run(mozilla::AutoSlowOperation& aAso) override { + mRequest->mLoader->InstantiateAndEvaluateDynamicImport(mRequest); + mRequest = nullptr; + } - if (aRequest->mModuleScript) { - if (!InstantiateModuleGraph(aRequest)) { - aRequest->mModuleScript = nullptr; + virtual bool Suppressed() override { + return mRequest->mLoader->mGlobalObject->IsInSyncOperation(); } + + private: + RefPtr mRequest; + }; + + MOZ_ASSERT(aRequest->mLoader == this); + + if (!aRequest->mModuleScript) { + FinishDynamicImportAndReject(aRequest, NS_ERROR_FAILURE); + return; + } + + CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); + RefPtr runnable = + new DynamicImportMicroTask(aRequest); + context->DispatchToMicroTask(do_AddRef(runnable)); +} + +void ModuleLoaderBase::InstantiateAndEvaluateDynamicImport( + ModuleLoadRequest* aRequest) { + MOZ_ASSERT(aRequest->mModuleScript); + + if (!InstantiateModuleGraph(aRequest)) { + aRequest->mModuleScript = nullptr; } nsresult rv = NS_ERROR_FAILURE; diff --git a/js/loader/ModuleLoaderBase.h b/js/loader/ModuleLoaderBase.h index 29c1a41e98a9..4ceb757f1ca6 100644 --- a/js/loader/ModuleLoaderBase.h +++ b/js/loader/ModuleLoaderBase.h @@ -388,6 +388,8 @@ class ModuleLoaderBase : public nsISupports { void StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent, nsIURI* aURI); + void InstantiateAndEvaluateDynamicImport(ModuleLoadRequest* aRequest); + /** * Shorthand Wrapper for JSAPI FinishDynamicImport function for the reject * case where we do not have `aEvaluationPromise`. As there is no evaluation -- 2.11.4.GIT