mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 07:50:41 +00:00
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: https://github.com/nodejs/node/pull/60603 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
This commit is contained in:
parent
b15070b5ac
commit
1d6a62662c
@ -58,7 +58,6 @@ const {
|
||||
let debug = require('internal/util/debuglog').debuglog('async_loader_worker', (fn) => {
|
||||
debug = fn;
|
||||
});
|
||||
let importMetaInitializer;
|
||||
|
||||
let importAssertionAlreadyWarned = false;
|
||||
|
||||
@ -111,7 +110,6 @@ function defineImportAssertionAlias(context) {
|
||||
* Interface for classes that implement asynchronous loader hooks that can be attached to the ModuleLoader
|
||||
* via `ModuleLoader.#setAsyncLoaderHooks()`.
|
||||
* @typedef {object} AsyncLoaderHooks
|
||||
* @property {boolean} allowImportMetaResolve Whether to allow the use of `import.meta.resolve`.
|
||||
* @property {boolean} isForAsyncLoaderHookWorker Whether the instance is running on the loader hook worker thread.
|
||||
* @property {(url: string, context: object, defaultLoad: Function) => Promise<LoadResult>} load
|
||||
* Calling the asynchronous `load` hook asynchronously.
|
||||
@ -163,8 +161,6 @@ class AsyncLoaderHooksOnLoaderHookWorker {
|
||||
// Cache URLs we've already validated to avoid repeated validation
|
||||
#validatedUrls = new SafeSet();
|
||||
|
||||
allowImportMetaResolve = false;
|
||||
|
||||
isForAsyncLoaderHookWorker = true;
|
||||
|
||||
/**
|
||||
@ -480,12 +476,6 @@ class AsyncLoaderHooksOnLoaderHookWorker {
|
||||
waitForLoaderHookInitialization() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
importMetaInitialize(meta, context, loader) {
|
||||
importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
|
||||
meta = importMetaInitializer(meta, context, loader);
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
ObjectSetPrototypeOf(AsyncLoaderHooksOnLoaderHookWorker.prototype, null);
|
||||
|
||||
@ -674,12 +664,6 @@ class AsyncLoaderHookWorker {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
#importMetaInitializer = require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
|
||||
|
||||
importMetaInitialize(meta, context, loader) {
|
||||
this.#importMetaInitializer(meta, context, loader);
|
||||
}
|
||||
}
|
||||
ObjectSetPrototypeOf(AsyncLoaderHookWorker.prototype, null);
|
||||
|
||||
@ -817,8 +801,6 @@ function getAsyncLoaderHookWorker() {
|
||||
*/
|
||||
class AsyncLoaderHooksProxiedToLoaderHookWorker {
|
||||
|
||||
allowImportMetaResolve = true;
|
||||
|
||||
isForAsyncLoaderHookWorker = false;
|
||||
|
||||
/**
|
||||
@ -876,10 +858,6 @@ class AsyncLoaderHooksProxiedToLoaderHookWorker {
|
||||
return asyncLoaderHookWorker.makeSyncRequest('load', undefined, url, context);
|
||||
}
|
||||
|
||||
importMetaInitialize(meta, context, loader) {
|
||||
asyncLoaderHookWorker.importMetaInitialize(meta, context, loader);
|
||||
}
|
||||
|
||||
waitForLoaderHookInitialization() {
|
||||
asyncLoaderHookWorker.waitForWorker();
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
StringPrototypeStartsWith,
|
||||
} = primordials;
|
||||
|
||||
const { getOptionValue } = require('internal/options');
|
||||
const {
|
||||
setLazyPathHelpers,
|
||||
} = internalBinding('modules');
|
||||
|
||||
const experimentalImportMetaResolve = getOptionValue('--experimental-import-meta-resolve');
|
||||
|
||||
/**
|
||||
* Generate a function to be used as import.meta.resolve for a particular module.
|
||||
* @param {string} defaultParentURL The default base to use for resolution
|
||||
* @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader
|
||||
* @param {bool} allowParentURL Whether to permit parentURL second argument for contextual resolution
|
||||
* @returns {(specifier: string) => string} Function to assign to import.meta.resolve
|
||||
*/
|
||||
function createImportMetaResolve(defaultParentURL, loader, allowParentURL) {
|
||||
/**
|
||||
* @param {string} specifier
|
||||
* @param {URL['href']} [parentURL] When `--experimental-import-meta-resolve` is specified, a
|
||||
* second argument can be provided.
|
||||
* @returns {string}
|
||||
*/
|
||||
return function resolve(specifier, parentURL = defaultParentURL) {
|
||||
let url;
|
||||
|
||||
if (!allowParentURL) {
|
||||
parentURL = defaultParentURL;
|
||||
}
|
||||
|
||||
try {
|
||||
const request = { specifier, __proto__: null };
|
||||
({ url } = loader.resolveSync(parentURL, request));
|
||||
return url;
|
||||
} catch (error) {
|
||||
switch (error?.code) {
|
||||
case 'ERR_UNSUPPORTED_DIR_IMPORT':
|
||||
case 'ERR_MODULE_NOT_FOUND':
|
||||
({ url } = error);
|
||||
if (url) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `import.meta` object for a module.
|
||||
* @param {object} meta
|
||||
* @param {{url: string, isMain?: boolean}} context
|
||||
* @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader
|
||||
* @returns {{dirname?: string, filename?: string, url: string, resolve?: Function}}
|
||||
*/
|
||||
function initializeImportMeta(meta, context, loader) {
|
||||
const { url, isMain } = context;
|
||||
|
||||
// Alphabetical
|
||||
if (StringPrototypeStartsWith(url, 'file:') === true) {
|
||||
// dirname
|
||||
// filename
|
||||
setLazyPathHelpers(meta, url);
|
||||
}
|
||||
|
||||
meta.main = !!isMain;
|
||||
|
||||
if (!loader || loader.allowImportMetaResolve) {
|
||||
meta.resolve = createImportMetaResolve(url, loader, experimentalImportMetaResolve);
|
||||
}
|
||||
|
||||
meta.url = url;
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initializeImportMeta,
|
||||
};
|
||||
@ -51,6 +51,7 @@ const {
|
||||
kErrored,
|
||||
kSourcePhase,
|
||||
throwIfPromiseRejected,
|
||||
setImportMetaResolveInitializer,
|
||||
} = internalBinding('module_wrap');
|
||||
const {
|
||||
urlToFilename,
|
||||
@ -62,7 +63,8 @@ const {
|
||||
loadWithHooks: loadWithSyncHooks,
|
||||
validateLoadSloppy,
|
||||
} = require('internal/modules/customization_hooks');
|
||||
let defaultResolve, defaultLoadSync, importMetaInitializer;
|
||||
|
||||
let defaultResolve, defaultLoadSync;
|
||||
|
||||
const { tracingChannel } = require('diagnostics_channel');
|
||||
const onImport = tracingChannel('module.import');
|
||||
@ -183,13 +185,6 @@ class ModuleLoader {
|
||||
*/
|
||||
translators = getTranslators();
|
||||
|
||||
/**
|
||||
* Truthy to allow the use of `import.meta.resolve`. This is needed
|
||||
* currently because the `Hooks` class does not have `resolveSync`
|
||||
* implemented and `import.meta.resolve` requires it.
|
||||
*/
|
||||
allowImportMetaResolve;
|
||||
|
||||
/**
|
||||
* @see {AsyncLoaderHooks.isForAsyncLoaderHookWorker}
|
||||
* Shortcut to this.#asyncLoaderHooks.isForAsyncLoaderHookWorker.
|
||||
@ -200,9 +195,10 @@ class ModuleLoader {
|
||||
* Asynchronous loader hooks to pass requests to.
|
||||
*
|
||||
* Note that this value _MUST_ be set with `#setAsyncLoaderHooks`
|
||||
* because it needs to copy `#asyncLoaderHooks.allowImportMetaResolve`
|
||||
* to this property and failure to do so will cause undefined
|
||||
* behavior when invoking `import.meta.resolve`.
|
||||
* because it needs to copy `#asyncLoaderHooks.isForAsyncLoaderHookWorker`
|
||||
* to this property.
|
||||
* TODO(joyeecheung): this was a legacy of the previous setup of import.meta.resolve
|
||||
* configuration; put this information in the environment directly instead.
|
||||
*
|
||||
* When the ModuleLoader is created on a loader hook thread, this is
|
||||
* {@link AsyncLoaderHooksOnLoaderHookWorker}, and its methods directly call out
|
||||
@ -234,10 +230,8 @@ class ModuleLoader {
|
||||
#setAsyncLoaderHooks(asyncLoaderHooks) {
|
||||
this.#asyncLoaderHooks = asyncLoaderHooks;
|
||||
if (asyncLoaderHooks) {
|
||||
this.allowImportMetaResolve = asyncLoaderHooks.allowImportMetaResolve;
|
||||
this.isForAsyncLoaderHookWorker = asyncLoaderHooks.isForAsyncLoaderHookWorker;
|
||||
} else {
|
||||
this.allowImportMetaResolve = true;
|
||||
this.isForAsyncLoaderHookWorker = false;
|
||||
}
|
||||
}
|
||||
@ -821,15 +815,6 @@ class ModuleLoader {
|
||||
}
|
||||
}
|
||||
|
||||
importMetaInitialize(meta, context) {
|
||||
if (this.#asyncLoaderHooks) {
|
||||
return this.#asyncLoaderHooks.importMetaInitialize(meta, context, this);
|
||||
}
|
||||
importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
|
||||
meta = importMetaInitializer(meta, context, this);
|
||||
return meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block until the async loader hooks have been initialized.
|
||||
*
|
||||
@ -883,8 +868,47 @@ function createModuleLoader(asyncLoaderHooks) {
|
||||
return new ModuleLoader(asyncLoaderHooks);
|
||||
}
|
||||
|
||||
let cascadedLoader;
|
||||
let allowImportMetaResolveParentURL;
|
||||
/**
|
||||
* This is only called from the native ImportMetaObjectInitialize function to set up import.meta.resolve
|
||||
* when import.meta.resolve is accessed for the first time in a module.
|
||||
* @param {ModuleLoader} loader The cascaded loader to use. Bound when this function gets passed to native land.
|
||||
* @param {string} moduleURL URL of the module accessing import.meta
|
||||
* @returns {function(string, URL['href']=): string} The import.meta.resolve function
|
||||
*/
|
||||
function createImportMetaResolve(loader, moduleURL) {
|
||||
/**
|
||||
* @param {string} specifier The module specifier to resolve.
|
||||
* @param {URL['href']} [parentURL] Optional parent URL to resolve against. Ignored unless
|
||||
* `--experimental-import-meta-resolve` is enabled.
|
||||
* @returns {string}
|
||||
*/
|
||||
return function resolve(specifier, parentURL) {
|
||||
// The second argument is ignored unless --experimental-import-meta-resolve is enabled.
|
||||
// Even then, if it's not provided, parentURL defaults to the url of the module accessing
|
||||
// import.meta.resolve.
|
||||
allowImportMetaResolveParentURL ??= getOptionValue('--experimental-import-meta-resolve');
|
||||
parentURL = allowImportMetaResolveParentURL ? (parentURL ?? moduleURL) : moduleURL;
|
||||
|
||||
let url;
|
||||
try {
|
||||
({ url } = loader.resolveSync(parentURL, { specifier, __proto__: null }));
|
||||
return url;
|
||||
} catch (error) {
|
||||
switch (error?.code) {
|
||||
case 'ERR_UNSUPPORTED_DIR_IMPORT':
|
||||
case 'ERR_MODULE_NOT_FOUND':
|
||||
({ url } = error);
|
||||
if (url) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let cascadedLoader;
|
||||
/**
|
||||
* This is a singleton ESM loader that integrates the loader hooks, if any.
|
||||
* It it used by other internal built-ins when they need to load user-land ESM code
|
||||
@ -898,7 +922,16 @@ let cascadedLoader;
|
||||
* @returns {ModuleLoader}
|
||||
*/
|
||||
function getOrInitializeCascadedLoader(asyncLoaderHooks) {
|
||||
cascadedLoader ??= createModuleLoader(asyncLoaderHooks);
|
||||
if (!cascadedLoader) {
|
||||
cascadedLoader = createModuleLoader(asyncLoaderHooks);
|
||||
// import.meta.resolve is not allowed in the async loader hook worker thread.
|
||||
// So only set up the import.meta.resolve initializer when we are initializing
|
||||
// the non-loader-hook-thread cascaded loader. When the native land doesn't see it,
|
||||
// it knows the loader is running on the loader hook thread.
|
||||
if (!(asyncLoaderHooks?.isForAsyncLoaderHookWorker)) {
|
||||
setImportMetaResolveInitializer(createImportMetaResolve.bind(null, cascadedLoader));
|
||||
}
|
||||
}
|
||||
return cascadedLoader;
|
||||
}
|
||||
|
||||
|
||||
@ -183,27 +183,14 @@ function registerModule(referrer, registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy the import meta handling to the default loader for source text modules.
|
||||
* @param {Record<string, string | Function>} meta - The import.meta object to initialize.
|
||||
* @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced.
|
||||
* @returns {object}
|
||||
*/
|
||||
function defaultInitializeImportMetaForModule(meta, wrap) {
|
||||
const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
|
||||
return cascadedLoader.importMetaInitialize(meta, { url: wrap.url, isMain: wrap.isMain });
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the `import.meta` object for a given module.
|
||||
* Initializes the `import.meta` object for a given module. This is only called when the module
|
||||
* is compiled with custom callbacks. Ordinary user-land source text modules are
|
||||
* initialized by the native DefaultImportMetaObjectInitializer directly.
|
||||
* @param {symbol} symbol - Reference to the module.
|
||||
* @param {Record<string, string | Function>} meta - The import.meta object to initialize.
|
||||
* @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced.
|
||||
*/
|
||||
function initializeImportMetaObject(symbol, meta, wrap) {
|
||||
if (symbol === source_text_module_default_hdo) {
|
||||
defaultInitializeImportMetaForModule(meta, wrap);
|
||||
return;
|
||||
}
|
||||
const data = moduleRegistries.get(symbol);
|
||||
assert(data, `import.meta registry not found for ${wrap.url}`);
|
||||
const { initializeImportMeta, callbackReferrer } = data;
|
||||
|
||||
@ -350,7 +350,7 @@ function findPackageJSON(specifier, base = 'data:') {
|
||||
|
||||
try {
|
||||
// TODO(@JakobJingleheimer): Detect whether findPackageJSON is being used within a loader
|
||||
// (possibly piggyback on `allowImportMetaResolve`)
|
||||
// (possibly piggyback on `isForAsyncLoaderHookWorker` from the loader?) and if so:
|
||||
// - When inside, use the default resolve
|
||||
// - (I think it's impossible to use the chain because of re-entry & a deadlock from atomics).
|
||||
// - When outside, use cascadedLoader.resolveSync (not implemented yet, but the pieces exist).
|
||||
|
||||
@ -470,6 +470,7 @@
|
||||
V(enhance_fatal_stack_before_inspector, v8::Function) \
|
||||
V(get_source_map_error_source, v8::Function) \
|
||||
V(host_import_module_dynamically_callback, v8::Function) \
|
||||
V(host_import_meta_resolve_initializer, v8::Function) \
|
||||
V(host_initialize_import_meta_object_callback, v8::Function) \
|
||||
V(http2session_on_altsvc_function, v8::Function) \
|
||||
V(http2session_on_error_function, v8::Function) \
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "node_external_reference.h"
|
||||
#include "node_internals.h"
|
||||
#include "node_process-inl.h"
|
||||
#include "node_url.h"
|
||||
#include "node_watchdog.h"
|
||||
#include "util-inl.h"
|
||||
|
||||
@ -1207,6 +1208,167 @@ void ModuleWrap::SetImportModuleDynamicallyCallback(
|
||||
ImportModuleDynamicallyWithPhase);
|
||||
}
|
||||
|
||||
void ModuleWrap::SetImportMetaResolveInitializer(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
Local<Function> initializer = args[0].As<Function>();
|
||||
realm->set_host_import_meta_resolve_initializer(initializer);
|
||||
}
|
||||
|
||||
static void ImportMetaResolveLazyGetter(
|
||||
Local<v8::Name> name, const PropertyCallbackInfo<Value>& info) {
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
Local<Value> receiver_val = info.This();
|
||||
if (!receiver_val->IsObject()) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Local<Object> receiver = receiver_val.As<Object>();
|
||||
Local<Context> context;
|
||||
if (!receiver->GetCreationContext().ToLocal(&context)) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Realm* realm = Realm::GetCurrent(context);
|
||||
if (realm == nullptr) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
}
|
||||
Local<Function> initializer = realm->host_import_meta_resolve_initializer();
|
||||
if (initializer.IsEmpty()) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
|
||||
// This should be createImportMetaResolve(). The loader argument is already
|
||||
// bound at initialization time.
|
||||
Local<Value> args[] = {info.Data()};
|
||||
Local<Value> ret;
|
||||
if (!initializer
|
||||
->Call(context, Undefined(realm->isolate()), arraysize(args), args)
|
||||
.ToLocal(&ret)) {
|
||||
return;
|
||||
}
|
||||
info.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
static void PathHelpersLazyGetter(Local<v8::Name> name,
|
||||
const PropertyCallbackInfo<Value>& info) {
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
// This getter has no JavaScript function representation and is not
|
||||
// invoked in the creation context.
|
||||
// When this getter is invoked in a vm context, the `Realm::GetCurrent(info)`
|
||||
// returns a nullptr and retrieve the creation context via `this` object and
|
||||
// get the creation Realm.
|
||||
Local<Value> receiver_val = info.This();
|
||||
if (!receiver_val->IsObject()) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Local<Object> receiver = receiver_val.As<Object>();
|
||||
Local<Context> context;
|
||||
if (!receiver->GetCreationContext().ToLocal(&context)) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
|
||||
node::Utf8Value url(isolate, info.Data());
|
||||
auto file_url = ada::parse(url.ToStringView());
|
||||
CHECK(file_url);
|
||||
auto file_path = url::FileURLToPath(env, *file_url);
|
||||
CHECK(file_path.has_value());
|
||||
std::string_view ret_view = file_path.value();
|
||||
|
||||
node::Utf8Value utf8name(isolate, name);
|
||||
auto plain_name = utf8name.ToStringView();
|
||||
if (plain_name == "dirname") {
|
||||
#ifdef _WIN32
|
||||
#define PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define PATH_SEPARATOR '/'
|
||||
#endif
|
||||
auto index = ret_view.rfind(PATH_SEPARATOR);
|
||||
CHECK(index != std::string_view::npos);
|
||||
ret_view.remove_suffix(ret_view.size() - index);
|
||||
#undef PATH_SEPARATOR
|
||||
}
|
||||
Local<Value> ret;
|
||||
if (!ToV8Value(context, ret_view, isolate).ToLocal(&ret)) {
|
||||
return;
|
||||
}
|
||||
info.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
static Maybe<void> DefaultImportMetaObjectInitializer(Realm* realm,
|
||||
Local<Object> wrap,
|
||||
Local<Object> meta) {
|
||||
Local<Context> context = realm->context();
|
||||
Isolate* isolate = realm->isolate();
|
||||
Environment* env = realm->env();
|
||||
|
||||
Local<Value> url;
|
||||
if (!wrap->Get(context, env->url_string()).ToLocal(&url)) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
// N.B.: Order is important to keep keys in alphabetical order.
|
||||
|
||||
Utf8Value url_utf8(isolate, url);
|
||||
if (url_utf8.ToStringView().starts_with("file:")) {
|
||||
// Set a lazy getter of import.meta.dirname
|
||||
if (meta->SetLazyDataProperty(
|
||||
context, env->dirname_string(), PathHelpersLazyGetter, url)
|
||||
.IsNothing()) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
// Set a lazy getter of import.meta.filename
|
||||
if (meta->SetLazyDataProperty(
|
||||
context, env->filename_string(), PathHelpersLazyGetter, url)
|
||||
.IsNothing()) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
}
|
||||
|
||||
// Set import.meta.main = moduleWrap.isMain
|
||||
Local<Value> is_main;
|
||||
if (!wrap->Get(context, FIXED_ONE_BYTE_STRING(isolate, "isMain"))
|
||||
.ToLocal(&is_main)) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
if (meta->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "main"),
|
||||
Boolean::New(isolate, is_main->IsTrue()))
|
||||
.IsEmpty()) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
// Set a lazy getter of import.meta.resolve - only if the initializer is set,
|
||||
// which is only the case when run on a non-loader-hook thread.
|
||||
Local<Function> import_meta_resolve_initializer =
|
||||
realm->host_import_meta_resolve_initializer();
|
||||
if (!import_meta_resolve_initializer.IsEmpty() &&
|
||||
meta->SetLazyDataProperty(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "resolve"),
|
||||
ImportMetaResolveLazyGetter,
|
||||
url)
|
||||
.IsNothing()) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
// Set import.meta.url = moduleWrap.url
|
||||
if (meta->Set(context, env->url_string(), url).IsEmpty()) {
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
return JustVoid();
|
||||
}
|
||||
|
||||
void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
||||
Local<Context> context, Local<Module> module, Local<Object> meta) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
@ -1232,8 +1394,19 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
||||
return;
|
||||
}
|
||||
DCHECK(id->IsSymbol());
|
||||
|
||||
// Use the default initializer for source text modules without custom
|
||||
// callbacks.
|
||||
if (id == env->source_text_module_default_hdo()) {
|
||||
USE(DefaultImportMetaObjectInitializer(realm, wrap, meta));
|
||||
return;
|
||||
}
|
||||
|
||||
// For modules that have custom callbacks, call into JS land
|
||||
// to look up the callback from the registry.
|
||||
Local<Value> args[] = {id, meta, wrap};
|
||||
TryCatchScope try_catch(env);
|
||||
|
||||
USE(callback->Call(
|
||||
context, Undefined(realm->isolate()), arraysize(args), args));
|
||||
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
||||
@ -1452,6 +1625,10 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
target,
|
||||
"setInitializeImportMetaObjectCallback",
|
||||
SetInitializeImportMetaObjectCallback);
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
"setImportMetaResolveInitializer",
|
||||
SetImportMetaResolveInitializer);
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
"createRequiredModuleFacade",
|
||||
@ -1505,6 +1682,7 @@ void ModuleWrap::RegisterExternalReferences(
|
||||
|
||||
registry->Register(SetImportModuleDynamicallyCallback);
|
||||
registry->Register(SetInitializeImportMetaObjectCallback);
|
||||
registry->Register(SetImportMetaResolveInitializer);
|
||||
registry->Register(ThrowIfPromiseRejected);
|
||||
}
|
||||
} // namespace loader
|
||||
|
||||
@ -182,6 +182,8 @@ class ModuleWrap : public BaseObject {
|
||||
|
||||
static void SetImportModuleDynamicallyCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetImportMetaResolveInitializer(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetInitializeImportMetaObjectCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static v8::MaybeLocal<v8::Value> SyntheticModuleEvaluationStepsCallback(
|
||||
|
||||
@ -35,7 +35,6 @@ using v8::Null;
|
||||
using v8::Object;
|
||||
using v8::ObjectTemplate;
|
||||
using v8::Primitive;
|
||||
using v8::PropertyCallbackInfo;
|
||||
using v8::String;
|
||||
using v8::Undefined;
|
||||
using v8::Value;
|
||||
@ -549,76 +548,6 @@ void GetCompileCacheEntry(const FunctionCallbackInfo<Value>& args) {
|
||||
isolate, v8::Null(isolate), names.data(), values.data(), names.size()));
|
||||
}
|
||||
|
||||
static void PathHelpersLazyGetter(Local<v8::Name> name,
|
||||
const PropertyCallbackInfo<Value>& info) {
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
// This getter has no JavaScript function representation and is not
|
||||
// invoked in the creation context.
|
||||
// When this getter is invoked in a vm context, the `Realm::GetCurrent(info)`
|
||||
// returns a nullptr and retrieve the creation context via `this` object and
|
||||
// get the creation Realm.
|
||||
Local<Value> receiver_val = info.This();
|
||||
if (!receiver_val->IsObject()) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Local<Object> receiver = receiver_val.As<Object>();
|
||||
Local<Context> context;
|
||||
if (!receiver->GetCreationContext().ToLocal(&context)) {
|
||||
THROW_ERR_INVALID_INVOCATION(isolate);
|
||||
return;
|
||||
}
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
|
||||
node::Utf8Value url(isolate, info.Data());
|
||||
auto file_url = ada::parse(url.ToStringView());
|
||||
CHECK(file_url);
|
||||
auto file_path = url::FileURLToPath(env, *file_url);
|
||||
CHECK(file_path.has_value());
|
||||
std::string_view ret_view = file_path.value();
|
||||
|
||||
node::Utf8Value utf8name(isolate, name);
|
||||
auto plain_name = utf8name.ToStringView();
|
||||
if (plain_name == "dirname") {
|
||||
#ifdef _WIN32
|
||||
#define PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define PATH_SEPARATOR '/'
|
||||
#endif
|
||||
auto index = ret_view.rfind(PATH_SEPARATOR);
|
||||
CHECK(index != std::string_view::npos);
|
||||
ret_view.remove_suffix(ret_view.size() - index);
|
||||
#undef PATH_SEPARATOR
|
||||
}
|
||||
Local<Value> ret;
|
||||
if (!ToV8Value(context, ret_view, isolate).ToLocal(&ret)) {
|
||||
return;
|
||||
}
|
||||
info.GetReturnValue().Set(ret);
|
||||
}
|
||||
void InitImportMetaPathHelpers(const FunctionCallbackInfo<Value>& args) {
|
||||
// target, url, shouldSetDirnameAndFilename, resolve
|
||||
CHECK_GE(args.Length(), 2);
|
||||
CHECK(args[0]->IsObject());
|
||||
CHECK(args[1]->IsString());
|
||||
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
|
||||
auto target = args[0].As<Object>();
|
||||
|
||||
// N.B.: Order is important to keep keys in alphabetical order.
|
||||
if (target
|
||||
->SetLazyDataProperty(
|
||||
context, env->dirname_string(), PathHelpersLazyGetter, args[1])
|
||||
.IsNothing() ||
|
||||
target
|
||||
->SetLazyDataProperty(
|
||||
context, env->filename_string(), PathHelpersLazyGetter, args[1])
|
||||
.IsNothing())
|
||||
return;
|
||||
}
|
||||
void SaveCompileCacheEntry(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
@ -648,7 +577,6 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
SetMethod(isolate, target, "flushCompileCache", FlushCompileCache);
|
||||
SetMethod(isolate, target, "getCompileCacheEntry", GetCompileCacheEntry);
|
||||
SetMethod(isolate, target, "saveCompileCacheEntry", SaveCompileCacheEntry);
|
||||
SetMethod(isolate, target, "setLazyPathHelpers", InitImportMetaPathHelpers);
|
||||
}
|
||||
|
||||
void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
@ -703,7 +631,6 @@ void BindingData::RegisterExternalReferences(
|
||||
registry->Register(FlushCompileCache);
|
||||
registry->Register(GetCompileCacheEntry);
|
||||
registry->Register(SaveCompileCacheEntry);
|
||||
registry->Register(InitImportMetaPathHelpers);
|
||||
}
|
||||
|
||||
} // namespace modules
|
||||
|
||||
Loading…
Reference in New Issue
Block a user