summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/loader/mozJSModuleLoader.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/loader/mozJSModuleLoader.h')
-rw-r--r--js/xpconnect/loader/mozJSModuleLoader.h350
1 files changed, 350 insertions, 0 deletions
diff --git a/js/xpconnect/loader/mozJSModuleLoader.h b/js/xpconnect/loader/mozJSModuleLoader.h
new file mode 100644
index 0000000000..0b8e5f85d9
--- /dev/null
+++ b/js/xpconnect/loader/mozJSModuleLoader.h
@@ -0,0 +1,350 @@
+/* -*- 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 mozJSModuleLoader_h
+#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 "mozilla/ThreadLocal.h" // MOZ_THREAD_LOCAL
+#include "nsIMemoryReporter.h"
+#include "nsISupports.h"
+#include "nsIURI.h"
+#include "nsClassHashtable.h"
+#include "jsapi.h"
+#include "js/CompileOptions.h"
+#include "js/experimental/JSStencil.h"
+#include "SkipCheckForBrokenURLOrZeroSized.h"
+
+#include "xpcpublic.h"
+
+class nsIFile;
+class ModuleLoaderInfo;
+
+namespace mozilla {
+class ScriptPreloader;
+} // namespace mozilla
+
+namespace JS::loader {
+class ModuleLoadRequest;
+} // namespace JS::loader
+
+#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
+# define STARTUP_RECORDER_ENABLED
+#endif
+
+namespace mozilla::loader {
+
+class NonSharedGlobalSyncModuleLoaderScope;
+
+} // namespace mozilla::loader
+
+class mozJSModuleLoader final : public nsIMemoryReporter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+ // Returns the list of all JSMs.
+ void GetLoadedModules(nsTArray<nsCString>& aLoadedModules);
+
+ // Returns the list of all ESMs.
+ nsresult GetLoadedESModules(nsTArray<nsCString>& aLoadedModules);
+
+ // Returns the list of all JSMs and ESMs.
+ nsresult GetLoadedJSAndESModules(nsTArray<nsCString>& aLoadedModules);
+
+ nsresult GetModuleImportStack(const nsACString& aLocation,
+ nsACString& aRetval);
+
+ void FindTargetObject(JSContext* aCx, JS::MutableHandleObject aTargetObject);
+
+ static void InitStatics();
+ static void UnloadLoaders();
+ static void ShutdownLoaders();
+
+ static mozJSModuleLoader* Get() {
+ MOZ_ASSERT(sSelf, "Should have already created the module loader");
+ return sSelf;
+ }
+
+ 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();
+
+ nsresult ImportInto(const nsACString& aResourceURI,
+ JS::HandleValue aTargetObj, JSContext* aCx, uint8_t aArgc,
+ JS::MutableHandleValue aRetval);
+
+ // Load a JSM.
+ nsresult Import(JSContext* aCx, const nsACString& aResourceURI,
+ JS::MutableHandleObject aModuleGlobal,
+ JS::MutableHandleObject aModuleExports,
+ bool aIgnoreExports = false);
+
+ // Synchronously load an ES6 module and all its dependencies.
+ nsresult ImportESModule(
+ JSContext* aCx, const nsACString& aResourceURI,
+ JS::MutableHandleObject aModuleNamespace,
+ mozilla::loader::SkipCheckForBrokenURLOrZeroSized aSkipCheck =
+ mozilla::loader::SkipCheckForBrokenURLOrZeroSized::No);
+
+ // Fallback from Import to ImportESModule.
+ nsresult TryFallbackToImportESModule(JSContext* aCx,
+ const nsACString& aResourceURI,
+ JS::MutableHandleObject aModuleGlobal,
+ JS::MutableHandleObject aModuleExports,
+ bool aIgnoreExports);
+
+ // If the request was handled by fallback before, fills the output and
+ // sets *aFound to true and returns NS_OK.
+ // If the request wasn't yet handled by fallback, sets *Found to false
+ // and returns NS_OK.
+ nsresult TryCachedFallbackToImportESModule(
+ JSContext* aCx, const nsACString& aResourceURI,
+ JS::MutableHandleObject aModuleGlobal,
+ JS::MutableHandleObject aModuleExports, bool aIgnoreExports,
+ bool* aFound);
+
+#ifdef STARTUP_RECORDER_ENABLED
+ void RecordImportStack(JSContext* aCx, const nsACString& aLocation);
+ void RecordImportStack(JSContext* aCx,
+ JS::loader::ModuleLoadRequest* aRequest);
+#endif
+
+ nsresult Unload(const nsACString& aResourceURI);
+ nsresult IsModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
+ nsresult IsJSModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
+ nsresult IsESModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
+ 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(
+ mozilla::loader::SyncModuleLoader* aModuleLoader, JSContext* aCx,
+ JS::loader::ModuleLoadRequest* aRequest,
+ JS::MutableHandleScript aScriptOut);
+
+ private:
+ static nsresult ReadScriptOnMainThread(JSContext* aCx,
+ const nsCString& aLocation,
+ nsCString& aData);
+ static nsresult LoadSingleModuleScriptOnWorker(
+ mozilla::loader::SyncModuleLoader* aModuleLoader, JSContext* aCx,
+ JS::loader::ModuleLoadRequest* aRequest,
+ JS::MutableHandleScript aScriptOut);
+
+ public:
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ bool DefineJSServices(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+ protected:
+ mozJSModuleLoader();
+ ~mozJSModuleLoader();
+
+ friend class XPCJSRuntime;
+
+ private:
+ static mozilla::StaticRefPtr<mozJSModuleLoader> sSelf;
+ static mozilla::StaticRefPtr<mozJSModuleLoader> sDevToolsLoader;
+
+ void Unload();
+ void UnloadModules();
+
+ void CreateLoaderGlobal(JSContext* aCx, const nsACString& aLocation,
+ JS::MutableHandleObject aGlobal);
+ void CreateDevToolsLoaderGlobal(JSContext* aCx, const nsACString& aLocation,
+ JS::MutableHandleObject aGlobal);
+
+ bool CreateJSServices(JSContext* aCx);
+
+ static nsresult GetSourceFile(nsIURI* aResolvedURI, nsIFile** aSourceFileOut);
+
+ static bool LocationIsRealFile(nsIURI* aURI);
+
+ JSObject* PrepareObjectForLocation(JSContext* aCx, nsIFile* aModuleFile,
+ nsIURI* aURI, bool aRealFile);
+
+ nsresult ObjectForLocation(ModuleLoaderInfo& aInfo, nsIFile* aModuleFile,
+ JS::MutableHandleObject aObject,
+ JS::MutableHandleScript aTableScript,
+ char** aLocation, bool aCatchException,
+ JS::MutableHandleValue aException);
+
+ static void SetModuleOptions(JS::CompileOptions& aOptions);
+
+ // Get the script for a given location, either from a cached stencil or by
+ // compiling it from source.
+ static nsresult GetScriptForLocation(JSContext* aCx, ModuleLoaderInfo& aInfo,
+ nsIFile* aModuleFile, bool aUseMemMap,
+ JS::MutableHandleScript aScriptOut,
+ char** aLocationOut = nullptr);
+
+ static already_AddRefed<JS::Stencil> CompileStencil(
+ JSContext* aCx, const JS::CompileOptions& aOptions,
+ JS::SourceText<mozilla::Utf8Unit>& aSource, bool aIsModule);
+ static JSScript* InstantiateStencil(JSContext* aCx, JS::Stencil* aStencil,
+ bool aIsModule);
+
+ nsresult ImportInto(const nsACString& aLocation, JS::HandleObject targetObj,
+ JSContext* callercx, JS::MutableHandleObject vp);
+
+ class ModuleEntry {
+ public:
+ explicit ModuleEntry(JS::RootingContext* aRootingCx)
+ : obj(aRootingCx), exports(aRootingCx), thisObjectKey(aRootingCx) {
+ location = nullptr;
+ }
+
+ ~ModuleEntry() { Clear(); }
+
+ void Clear() {
+ if (obj) {
+ if (JS_HasExtensibleLexicalEnvironment(obj)) {
+ JS::RootedObject lexicalEnv(mozilla::dom::RootingCx(),
+ JS_ExtensibleLexicalEnvironment(obj));
+ JS_SetAllNonReservedSlotsToUndefined(lexicalEnv);
+ }
+ JS_SetAllNonReservedSlotsToUndefined(obj);
+ obj = nullptr;
+ thisObjectKey = nullptr;
+ }
+
+ if (location) {
+ free(location);
+ }
+
+ obj = nullptr;
+ thisObjectKey = nullptr;
+ location = nullptr;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ JS::PersistentRootedObject obj;
+ JS::PersistentRootedObject exports;
+ JS::PersistentRootedScript thisObjectKey;
+ char* location;
+ nsCString resolvedURL;
+ };
+
+ class FallbackModuleEntry {
+ public:
+ explicit FallbackModuleEntry(JS::RootingContext* aRootingCx)
+ : globalProxy(aRootingCx), moduleNamespace(aRootingCx) {}
+
+ ~FallbackModuleEntry() { Clear(); }
+
+ void Clear() {
+ globalProxy = nullptr;
+ moduleNamespace = nullptr;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this);
+ }
+
+ JS::PersistentRootedObject globalProxy;
+ JS::PersistentRootedObject moduleNamespace;
+ };
+
+ nsresult ExtractExports(JSContext* aCx, ModuleLoaderInfo& aInfo,
+ ModuleEntry* aMod, JS::MutableHandleObject aExports);
+
+ nsClassHashtable<nsCStringHashKey, ModuleEntry> mImports;
+ nsTHashMap<nsCStringHashKey, ModuleEntry*> mInProgressImports;
+ nsClassHashtable<nsCStringHashKey, FallbackModuleEntry> mFallbackImports;
+#ifdef STARTUP_RECORDER_ENABLED
+ nsTHashMap<nsCStringHashKey, nsCString> mImportStacks;
+#endif
+
+ // A map of on-disk file locations which are loaded as modules to the
+ // pre-resolved URIs they were loaded from. Used to prevent the same file
+ // from being loaded separately, from multiple URLs.
+ nsClassHashtable<nsCStringHashKey, nsCString> mLocations;
+
+ bool mInitialized;
+#ifdef DEBUG
+ bool mIsInitializingLoaderGlobal = false;
+#endif
+ JS::PersistentRooted<JSObject*> mLoaderGlobal;
+ JS::PersistentRooted<JSObject*> mServicesObj;
+
+ RefPtr<mozilla::loader::SyncModuleLoader> mModuleLoader;
+};
+
+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();
+
+ static void InitStatics();
+
+ private:
+ RefPtr<mozJSModuleLoader> mLoader;
+
+ // Reference to thread-local module loader on the stack.
+ // This is used by another sync module load during a sync module load is
+ // ongoing.
+ static MOZ_THREAD_LOCAL(mozJSModuleLoader*) sTlsActiveLoader;
+
+ // The module loader of the target global.
+ RefPtr<JS::loader::ModuleLoaderBase> mAsyncModuleLoader;
+
+ mozilla::Maybe<JS::loader::AutoOverrideModuleLoader> mMaybeOverride;
+};
+
+} // namespace mozilla::loader
+
+#endif // mozJSModuleLoader_h