From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/loader/ModuleLoaderBase.h | 435 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 js/loader/ModuleLoaderBase.h (limited to 'js/loader/ModuleLoaderBase.h') diff --git a/js/loader/ModuleLoaderBase.h b/js/loader/ModuleLoaderBase.h new file mode 100644 index 0000000000..d781add1d1 --- /dev/null +++ b/js/loader/ModuleLoaderBase.h @@ -0,0 +1,435 @@ +/* -*- 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/. */ + +#ifndef js_loader_ModuleLoaderBase_h +#define js_loader_ModuleLoaderBase_h + +#include "LoadedScript.h" +#include "ScriptLoadRequest.h" + +#include "ImportMap.h" +#include "js/TypeDecls.h" // JS::MutableHandle, JS::Handle, JS::Root +#include "js/Modules.h" +#include "nsRefPtrHashtable.h" +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "nsILoadInfo.h" // nsSecurityFlags +#include "nsINode.h" // nsIURI +#include "nsThreadUtils.h" // GetMainThreadSerialEventTarget +#include "nsURIHashKey.h" +#include "mozilla/CORSMode.h" +#include "mozilla/dom/JSExecutionContext.h" +#include "mozilla/MaybeOneOf.h" +#include "mozilla/MozPromise.h" +#include "mozilla/UniquePtr.h" +#include "ResolveResult.h" + +class nsIURI; + +namespace mozilla { + +class LazyLogModule; +union Utf8Unit; + +} // namespace mozilla + +namespace JS { + +class CompileOptions; + +template +class SourceText; + +namespace loader { + +class ModuleLoaderBase; +class ModuleLoadRequest; +class ModuleScript; + +/* + * [DOMDOC] Shared Classic/Module Script Methods + * + * The ScriptLoaderInterface defines the shared methods needed by both + * ScriptLoaders (loading classic scripts) and ModuleLoaders (loading module + * scripts). These include: + * + * * Error Logging + * * Generating the compile options + * * Optional: Bytecode Encoding + * + * ScriptLoaderInterface does not provide any implementations. + * It enables the ModuleLoaderBase to reference back to the behavior implemented + * by a given ScriptLoader. + * + * Not all methods will be used by all ModuleLoaders. For example, Bytecode + * Encoding does not apply to workers, as we only work with source text there. + * Fully virtual methods are implemented by all. + * + */ + +class ScriptLoaderInterface : public nsISupports { + public: + // alias common classes + using ScriptFetchOptions = JS::loader::ScriptFetchOptions; + using ScriptKind = JS::loader::ScriptKind; + using ScriptLoadRequest = JS::loader::ScriptLoadRequest; + using ScriptLoadRequestList = JS::loader::ScriptLoadRequestList; + using ModuleLoadRequest = JS::loader::ModuleLoadRequest; + + virtual ~ScriptLoaderInterface() = default; + + // In some environments, we will need to default to a base URI + virtual nsIURI* GetBaseURI() const = 0; + + virtual void ReportErrorToConsole(ScriptLoadRequest* aRequest, + nsresult aResult) const = 0; + + virtual void ReportWarningToConsole( + ScriptLoadRequest* aRequest, const char* aMessageName, + const nsTArray& aParams = nsTArray()) const = 0; + + // Fill in CompileOptions, as well as produce the introducer script for + // subsequent calls to UpdateDebuggerMetadata + virtual nsresult FillCompileOptionsForRequest( + JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions, + JS::MutableHandle aIntroductionScript) = 0; + + virtual void MaybePrepareModuleForBytecodeEncodingBeforeExecute( + JSContext* aCx, ModuleLoadRequest* aRequest) {} + + virtual nsresult MaybePrepareModuleForBytecodeEncodingAfterExecute( + ModuleLoadRequest* aRequest, nsresult aRv) { + return NS_OK; + } + + virtual void MaybeTriggerBytecodeEncoding() {} +}; + +/* + * [DOMDOC] Module Loading + * + * ModuleLoaderBase provides support for loading module graphs as defined in the + * EcmaScript specification. A derived module loader class must be created for a + * specific use case (for example loading HTML module scripts). The derived + * class provides operations such as fetching of source code and scheduling of + * module execution. + * + * Module loading works in terms of 'requests' which hold data about modules as + * they move through the loading process. There may be more than one load + * request active for a single module URI, but the module is only loaded + * once. This is achieved by tracking all fetching and fetched modules in the + * module map. + * + * The module map is made up of two parts. A module that has been requested but + * has not yet loaded is represented by a promise in the mFetchingModules map. A + * module which has been loaded is represented by a ModuleScript in the + * mFetchedModules map. + * + * Module loading typically works as follows: + * + * 1. The client ensures there is an instance of the derived module loader + * class for its global or creates one if necessary. + * + * 2. The client creates a ModuleLoadRequest object for the module to load and + * calls the loader's StartModuleLoad() method. This is a top-level request, + * i.e. not an import. + * + * 3. The module loader calls the virtual method CanStartLoad() to check + * whether the request should be loaded. + * + * 4. If the module is not already present in the module map, the loader calls + * the virtual method StartFetch() to set up an asynchronous operation to + * fetch the module source. + * + * 5. When the fetch operation is complete, the derived loader calls + * OnFetchComplete() passing an error code to indicate success or failure. + * + * 6. On success, the loader attempts to create a module script by calling the + * virtual CompileFetchedModule() method. + * + * 7. If compilation is successful, the loader creates load requests for any + * imported modules if present. If so, the process repeats from step 3. + * + * 8. When a load request is completed, the virtual OnModuleLoadComplete() + * method is called. This is called for the top-level request and import + * requests. + * + * 9. The client calls InstantiateModuleGraph() for the top-level request. This + * links the loaded module graph. + * + * 10. The client calls EvaluateModule() to execute the top-level module. + */ +class ModuleLoaderBase : public nsISupports { + private: + using GenericNonExclusivePromise = mozilla::GenericNonExclusivePromise; + using GenericPromise = mozilla::GenericPromise; + + // Module map + nsRefPtrHashtable + mFetchingModules; + nsRefPtrHashtable mFetchedModules; + + // List of dynamic imports that are currently being loaded. + ScriptLoadRequestList mDynamicImportRequests; + + nsCOMPtr mGlobalObject; + + // https://html.spec.whatwg.org/multipage/webappapis.html#import-maps-allowed + // + // Each Window has an import maps allowed boolean, initially true. + bool mImportMapsAllowed = true; + + protected: + // Event handler used to process MozPromise actions, used internally to wait + // for fetches to finish and for imports to become avilable. + nsCOMPtr mEventTarget; + RefPtr mLoader; + + mozilla::UniquePtr mImportMap; + + virtual ~ModuleLoaderBase(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(ModuleLoaderBase) + explicit ModuleLoaderBase(ScriptLoaderInterface* aLoader, + nsIGlobalObject* aGlobalObject, + nsISerialEventTarget* aEventTarget = + mozilla::GetMainThreadSerialEventTarget()); + + // Called to break cycles during shutdown to prevent memory leaks. + void Shutdown(); + + virtual nsIURI* GetBaseURI() const { return mLoader->GetBaseURI(); }; + + using LoadedScript = JS::loader::LoadedScript; + using ScriptFetchOptions = JS::loader::ScriptFetchOptions; + using ScriptLoadRequest = JS::loader::ScriptLoadRequest; + using ModuleLoadRequest = JS::loader::ModuleLoadRequest; + + using MaybeSourceText = + mozilla::MaybeOneOf, JS::SourceText>; + + // Methods that must be implemented by an extending class. These are called + // internally by ModuleLoaderBase. + + private: + // Create a module load request for a static module import. + virtual already_AddRefed CreateStaticImport( + nsIURI* aURI, ModuleLoadRequest* aParent) = 0; + + // Called by HostImportModuleDynamically hook. + virtual already_AddRefed CreateDynamicImport( + JSContext* aCx, nsIURI* aURI, LoadedScript* aMaybeActiveScript, + JS::Handle aReferencingPrivate, + JS::Handle aSpecifier, JS::Handle aPromise) = 0; + + // Check whether we can load a module. May return false with |aRvOut| set to + // NS_OK to abort load without returning an error. + virtual bool CanStartLoad(ModuleLoadRequest* aRequest, nsresult* aRvOut) = 0; + + // Start the process of fetching module source (or bytecode). This is only + // called if CanStartLoad returned true. + virtual nsresult StartFetch(ModuleLoadRequest* aRequest) = 0; + + // Create a JS module for a fetched module request. This might compile source + // text or decode cached bytecode. + virtual nsresult CompileFetchedModule( + JSContext* aCx, JS::Handle aGlobal, + JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest, + JS::MutableHandle aModuleOut) = 0; + + // Called when a module script has been loaded, including imports. + virtual void OnModuleLoadComplete(ModuleLoadRequest* aRequest) = 0; + + virtual bool IsModuleEvaluationAborted(ModuleLoadRequest* aRequest) { + return false; + } + + // Get the error message when resolving failed. The default is to call + // nsContentUtils::FormatLoalizedString. But currently + // nsContentUtils::FormatLoalizedString cannot be called on a worklet thread, + // see bug 1808301. So WorkletModuleLoader will override this function to + // get the error message. + virtual nsresult GetResolveFailureMessage(ResolveError aError, + const nsAString& aSpecifier, + nsAString& aResult); + + // Public API methods. + + public: + ScriptLoaderInterface* GetScriptLoaderInterface() const { return mLoader; } + + nsIGlobalObject* GetGlobalObject() const { return mGlobalObject; } + + bool HasPendingDynamicImports() const; + void CancelDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult); +#ifdef DEBUG + bool HasDynamicImport(const ModuleLoadRequest* aRequest) const; +#endif + + // Start a load for a module script URI. Returns immediately if the module is + // already being loaded. + nsresult StartModuleLoad(ModuleLoadRequest* aRequest); + nsresult RestartModuleLoad(ModuleLoadRequest* aRequest); + + // Notify the module loader when a fetch started by StartFetch() completes. + nsresult OnFetchComplete(ModuleLoadRequest* aRequest, nsresult aRv); + + // Link the module and all its imports. This must occur prior to evaluation. + bool InstantiateModuleGraph(ModuleLoadRequest* aRequest); + + // Executes the module. + // Implements https://html.spec.whatwg.org/#run-a-module-script + nsresult EvaluateModule(ModuleLoadRequest* aRequest); + + // Evaluate a module in the given context. Does not push an entry to the + // execution stack. + nsresult EvaluateModuleInContext(JSContext* aCx, ModuleLoadRequest* aRequest, + JS::ModuleErrorBehaviour errorBehaviour); + + void StartDynamicImport(ModuleLoadRequest* aRequest); + void ProcessDynamicImport(ModuleLoadRequest* aRequest); + void CancelAndClearDynamicImports(); + + // Process