Skip to content

Commit e9b68e6

Browse files
joyeecheungaduh95
authored andcommitted
src: move import.meta initializer to native land
When the source text module is compiled without custom callbacks, instead of calling into JS land from the per-isolate import.meta initializer and then back to C++ land to set up lazy data properties, just do the initialization all in C++ land. Only import.meta.resolve initialization will call back into JS land to generate a closure that call the cascaded loader for resolution. In addition, simplify the loader structure by merging allowImportMetaResolve into isForAsyncLoaderHookWorker - the two are essentially equivalent, as import.meta.resolve is only allowed in a non-loader-hook worker thread's loader. PR-URL: #60603 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent d64795b commit e9b68e6

File tree

9 files changed

+242
-219
lines changed

9 files changed

+242
-219
lines changed

lib/internal/modules/esm/hooks.js

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ const {
5858
let debug = require('internal/util/debuglog').debuglog('async_loader_worker', (fn) => {
5959
debug = fn;
6060
});
61-
let importMetaInitializer;
6261

6362
let importAssertionAlreadyWarned = false;
6463

@@ -111,7 +110,6 @@ function defineImportAssertionAlias(context) {
111110
* Interface for classes that implement asynchronous loader hooks that can be attached to the ModuleLoader
112111
* via `ModuleLoader.#setAsyncLoaderHooks()`.
113112
* @typedef {object} AsyncLoaderHooks
114-
* @property {boolean} allowImportMetaResolve Whether to allow the use of `import.meta.resolve`.
115113
* @property {boolean} isForAsyncLoaderHookWorker Whether the instance is running on the loader hook worker thread.
116114
* @property {(url: string, context: object, defaultLoad: Function) => Promise<LoadResult>} load
117115
* Calling the asynchronous `load` hook asynchronously.
@@ -163,8 +161,6 @@ class AsyncLoaderHooksOnLoaderHookWorker {
163161
// Cache URLs we've already validated to avoid repeated validation
164162
#validatedUrls = new SafeSet();
165163

166-
allowImportMetaResolve = false;
167-
168164
isForAsyncLoaderHookWorker = true;
169165

170166
/**
@@ -480,12 +476,6 @@ class AsyncLoaderHooksOnLoaderHookWorker {
480476
waitForLoaderHookInitialization() {
481477
// No-op
482478
}
483-
484-
importMetaInitialize(meta, context, loader) {
485-
importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
486-
meta = importMetaInitializer(meta, context, loader);
487-
return meta;
488-
}
489479
}
490480
ObjectSetPrototypeOf(AsyncLoaderHooksOnLoaderHookWorker.prototype, null);
491481

@@ -674,12 +664,6 @@ class AsyncLoaderHookWorker {
674664
return body;
675665
}
676666
}
677-
678-
#importMetaInitializer = require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
679-
680-
importMetaInitialize(meta, context, loader) {
681-
this.#importMetaInitializer(meta, context, loader);
682-
}
683667
}
684668
ObjectSetPrototypeOf(AsyncLoaderHookWorker.prototype, null);
685669

@@ -817,8 +801,6 @@ function getAsyncLoaderHookWorker() {
817801
*/
818802
class AsyncLoaderHooksProxiedToLoaderHookWorker {
819803

820-
allowImportMetaResolve = true;
821-
822804
isForAsyncLoaderHookWorker = false;
823805

824806
/**
@@ -876,10 +858,6 @@ class AsyncLoaderHooksProxiedToLoaderHookWorker {
876858
return asyncLoaderHookWorker.makeSyncRequest('load', undefined, url, context);
877859
}
878860

879-
importMetaInitialize(meta, context, loader) {
880-
asyncLoaderHookWorker.importMetaInitialize(meta, context, loader);
881-
}
882-
883861
waitForLoaderHookInitialization() {
884862
asyncLoaderHookWorker.waitForWorker();
885863
}

lib/internal/modules/esm/initialize_import_meta.js

Lines changed: 0 additions & 83 deletions
This file was deleted.

lib/internal/modules/esm/loader.js

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const {
5151
kErrored,
5252
kSourcePhase,
5353
throwIfPromiseRejected,
54+
setImportMetaResolveInitializer,
5455
} = internalBinding('module_wrap');
5556
const {
5657
urlToFilename,
@@ -62,7 +63,8 @@ const {
6263
loadWithHooks: loadWithSyncHooks,
6364
validateLoadSloppy,
6465
} = require('internal/modules/customization_hooks');
65-
let defaultResolve, defaultLoadSync, importMetaInitializer;
66+
67+
let defaultResolve, defaultLoadSync;
6668

6769
const { tracingChannel } = require('diagnostics_channel');
6870
const onImport = tracingChannel('module.import');
@@ -183,13 +185,6 @@ class ModuleLoader {
183185
*/
184186
translators = getTranslators();
185187

186-
/**
187-
* Truthy to allow the use of `import.meta.resolve`. This is needed
188-
* currently because the `Hooks` class does not have `resolveSync`
189-
* implemented and `import.meta.resolve` requires it.
190-
*/
191-
allowImportMetaResolve;
192-
193188
/**
194189
* @see {AsyncLoaderHooks.isForAsyncLoaderHookWorker}
195190
* Shortcut to this.#asyncLoaderHooks.isForAsyncLoaderHookWorker.
@@ -200,9 +195,10 @@ class ModuleLoader {
200195
* Asynchronous loader hooks to pass requests to.
201196
*
202197
* Note that this value _MUST_ be set with `#setAsyncLoaderHooks`
203-
* because it needs to copy `#asyncLoaderHooks.allowImportMetaResolve`
204-
* to this property and failure to do so will cause undefined
205-
* behavior when invoking `import.meta.resolve`.
198+
* because it needs to copy `#asyncLoaderHooks.isForAsyncLoaderHookWorker`
199+
* to this property.
200+
* TODO(joyeecheung): this was a legacy of the previous setup of import.meta.resolve
201+
* configuration; put this information in the environment directly instead.
206202
*
207203
* When the ModuleLoader is created on a loader hook thread, this is
208204
* {@link AsyncLoaderHooksOnLoaderHookWorker}, and its methods directly call out
@@ -234,10 +230,8 @@ class ModuleLoader {
234230
#setAsyncLoaderHooks(asyncLoaderHooks) {
235231
this.#asyncLoaderHooks = asyncLoaderHooks;
236232
if (asyncLoaderHooks) {
237-
this.allowImportMetaResolve = asyncLoaderHooks.allowImportMetaResolve;
238233
this.isForAsyncLoaderHookWorker = asyncLoaderHooks.isForAsyncLoaderHookWorker;
239234
} else {
240-
this.allowImportMetaResolve = true;
241235
this.isForAsyncLoaderHookWorker = false;
242236
}
243237
}
@@ -821,15 +815,6 @@ class ModuleLoader {
821815
}
822816
}
823817

824-
importMetaInitialize(meta, context) {
825-
if (this.#asyncLoaderHooks) {
826-
return this.#asyncLoaderHooks.importMetaInitialize(meta, context, this);
827-
}
828-
importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
829-
meta = importMetaInitializer(meta, context, this);
830-
return meta;
831-
}
832-
833818
/**
834819
* Block until the async loader hooks have been initialized.
835820
*
@@ -883,8 +868,47 @@ function createModuleLoader(asyncLoaderHooks) {
883868
return new ModuleLoader(asyncLoaderHooks);
884869
}
885870

886-
let cascadedLoader;
871+
let allowImportMetaResolveParentURL;
872+
/**
873+
* This is only called from the native ImportMetaObjectInitialize function to set up import.meta.resolve
874+
* when import.meta.resolve is accessed for the first time in a module.
875+
* @param {ModuleLoader} loader The cascaded loader to use. Bound when this function gets passed to native land.
876+
* @param {string} moduleURL URL of the module accessing import.meta
877+
* @returns {function(string, URL['href']=): string} The import.meta.resolve function
878+
*/
879+
function createImportMetaResolve(loader, moduleURL) {
880+
/**
881+
* @param {string} specifier The module specifier to resolve.
882+
* @param {URL['href']} [parentURL] Optional parent URL to resolve against. Ignored unless
883+
* `--experimental-import-meta-resolve` is enabled.
884+
* @returns {string}
885+
*/
886+
return function resolve(specifier, parentURL) {
887+
// The second argument is ignored unless --experimental-import-meta-resolve is enabled.
888+
// Even then, if it's not provided, parentURL defaults to the url of the module accessing
889+
// import.meta.resolve.
890+
allowImportMetaResolveParentURL ??= getOptionValue('--experimental-import-meta-resolve');
891+
parentURL = allowImportMetaResolveParentURL ? (parentURL ?? moduleURL) : moduleURL;
892+
893+
let url;
894+
try {
895+
({ url } = loader.resolveSync(parentURL, { specifier, __proto__: null }));
896+
return url;
897+
} catch (error) {
898+
switch (error?.code) {
899+
case 'ERR_UNSUPPORTED_DIR_IMPORT':
900+
case 'ERR_MODULE_NOT_FOUND':
901+
({ url } = error);
902+
if (url) {
903+
return url;
904+
}
905+
}
906+
throw error;
907+
}
908+
};
909+
}
887910

911+
let cascadedLoader;
888912
/**
889913
* This is a singleton ESM loader that integrates the loader hooks, if any.
890914
* It it used by other internal built-ins when they need to load user-land ESM code
@@ -898,7 +922,16 @@ let cascadedLoader;
898922
* @returns {ModuleLoader}
899923
*/
900924
function getOrInitializeCascadedLoader(asyncLoaderHooks) {
901-
cascadedLoader ??= createModuleLoader(asyncLoaderHooks);
925+
if (!cascadedLoader) {
926+
cascadedLoader = createModuleLoader(asyncLoaderHooks);
927+
// import.meta.resolve is not allowed in the async loader hook worker thread.
928+
// So only set up the import.meta.resolve initializer when we are initializing
929+
// the non-loader-hook-thread cascaded loader. When the native land doesn't see it,
930+
// it knows the loader is running on the loader hook thread.
931+
if (!(asyncLoaderHooks?.isForAsyncLoaderHookWorker)) {
932+
setImportMetaResolveInitializer(createImportMetaResolve.bind(null, cascadedLoader));
933+
}
934+
}
902935
return cascadedLoader;
903936
}
904937

lib/internal/modules/esm/utils.js

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -183,27 +183,14 @@ function registerModule(referrer, registry) {
183183
}
184184

185185
/**
186-
* Proxy the import meta handling to the default loader for source text modules.
187-
* @param {Record<string, string | Function>} meta - The import.meta object to initialize.
188-
* @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced.
189-
* @returns {object}
190-
*/
191-
function defaultInitializeImportMetaForModule(meta, wrap) {
192-
const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
193-
return cascadedLoader.importMetaInitialize(meta, { url: wrap.url, isMain: wrap.isMain });
194-
}
195-
196-
/**
197-
* Defines the `import.meta` object for a given module.
186+
* Initializes the `import.meta` object for a given module. This is only called when the module
187+
* is compiled with custom callbacks. Ordinary user-land source text modules are
188+
* initialized by the native DefaultImportMetaObjectInitializer directly.
198189
* @param {symbol} symbol - Reference to the module.
199190
* @param {Record<string, string | Function>} meta - The import.meta object to initialize.
200191
* @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced.
201192
*/
202193
function initializeImportMetaObject(symbol, meta, wrap) {
203-
if (symbol === source_text_module_default_hdo) {
204-
defaultInitializeImportMetaForModule(meta, wrap);
205-
return;
206-
}
207194
const data = moduleRegistries.get(symbol);
208195
assert(data, `import.meta registry not found for ${wrap.url}`);
209196
const { initializeImportMeta, callbackReferrer } = data;

lib/internal/modules/package_json_reader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ function findPackageJSON(specifier, base = 'data:') {
350350

351351
try {
352352
// TODO(@JakobJingleheimer): Detect whether findPackageJSON is being used within a loader
353-
// (possibly piggyback on `allowImportMetaResolve`)
353+
// (possibly piggyback on `isForAsyncLoaderHookWorker` from the loader?) and if so:
354354
// - When inside, use the default resolve
355355
// - (I think it's impossible to use the chain because of re-entry & a deadlock from atomics).
356356
// - When outside, use cascadedLoader.resolveSync (not implemented yet, but the pieces exist).

src/env_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@
470470
V(enhance_fatal_stack_before_inspector, v8::Function) \
471471
V(get_source_map_error_source, v8::Function) \
472472
V(host_import_module_dynamically_callback, v8::Function) \
473+
V(host_import_meta_resolve_initializer, v8::Function) \
473474
V(host_initialize_import_meta_object_callback, v8::Function) \
474475
V(http2session_on_altsvc_function, v8::Function) \
475476
V(http2session_on_error_function, v8::Function) \

0 commit comments

Comments
 (0)