modules: move callbacks and conditions into modules/esm/utils.js

This moves the following utils into modules/esm/utils.js:

- Code related to default conditions
- The callbackMap (which is now created in the module instead of
  hanging off the module_wrap binding, since the C++ land
  does not need it).
- Per-isolate module callbacks

These are self-contained code that can be included into the
built-in snapshot.

PR-URL: https://github.com/nodejs/node/pull/45849
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Joyee Cheung
2022-12-13 22:58:06 +01:00
parent 0c4f5c40ad
commit 36608c14b7
11 changed files with 130 additions and 91 deletions

View File

@@ -35,7 +35,7 @@ ${ArrayPrototypeJoin(ArrayPrototypeMap(imports, createImport), '\n')}
${ArrayPrototypeJoin(ArrayPrototypeMap(exports, createExport), '\n')}
import.meta.done();
`;
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
const { ModuleWrap } = internalBinding('module_wrap');
const m = new ModuleWrap(`${url}`, undefined, source, 0, 0);
const readyfns = new SafeSet();
@@ -46,8 +46,8 @@ import.meta.done();
if (imports.length)
reflect.imports = ObjectCreate(null);
callbackMap.set(m, {
const { setCallbackForWrap } = require('internal/modules/esm/utils');
setCallbackForWrap(m, {
initializeImportMeta: (meta, wrap) => {
meta.exports = reflect.exports;
if (reflect.imports)

View File

@@ -47,9 +47,12 @@ function newModuleMap() {
const {
defaultResolve,
DEFAULT_CONDITIONS,
} = require('internal/modules/esm/resolve');
const {
getDefaultConditions,
} = require('internal/modules/esm/utils');
function getTranslators() {
const { translators } = require('internal/modules/esm/translators');
return translators;
@@ -363,9 +366,10 @@ class ESMLoader {
url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href
) {
const evalInstance = (url) => {
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
const { ModuleWrap } = internalBinding('module_wrap');
const { setCallbackForWrap } = require('internal/modules/esm/utils');
const module = new ModuleWrap(url, undefined, source, 0, 0);
callbackMap.set(module, {
setCallbackForWrap(module, {
importModuleDynamically: (specifier, { url }, importAssertions) => {
return this.import(specifier, url, importAssertions);
}
@@ -798,7 +802,7 @@ class ESMLoader {
}
const chain = this.#hooks.resolve;
const context = {
conditions: DEFAULT_CONDITIONS,
conditions: getDefaultConditions(),
importAssertions,
parentURL,
};

View File

@@ -6,7 +6,6 @@ const {
ArrayPrototypeJoin,
ArrayPrototypeShift,
JSONStringify,
ObjectFreeze,
ObjectGetOwnPropertyNames,
ObjectPrototypeHasOwnProperty,
RegExp,
@@ -45,7 +44,6 @@ const typeFlag = getOptionValue('--input-type');
const { URL, pathToFileURL, fileURLToPath } = require('internal/url');
const {
ERR_INPUT_TYPE_NOT_ALLOWED,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_MODULE_SPECIFIER,
ERR_INVALID_PACKAGE_CONFIG,
ERR_INVALID_PACKAGE_TARGET,
@@ -60,25 +58,13 @@ const {
const { Module: CJSModule } = require('internal/modules/cjs/loader');
const { getPackageConfig, getPackageScopeConfig } = require('internal/modules/esm/package_config');
const { getConditionsSet } = require('internal/modules/esm/utils');
/**
* @typedef {import('internal/modules/esm/package_config.js').PackageConfig} PackageConfig
*/
const userConditions = getOptionValue('--conditions');
const noAddons = getOptionValue('--no-addons');
const addonConditions = noAddons ? [] : ['node-addons'];
const DEFAULT_CONDITIONS = ObjectFreeze([
'node',
'import',
...addonConditions,
...userConditions,
]);
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);
const emittedPackageWarnings = new SafeSet();
function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) {
@@ -147,21 +133,6 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
);
}
/**
* @param {string[]} [conditions]
* @returns {Set<string>}
*/
function getConditionsSet(conditions) {
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
if (!ArrayIsArray(conditions)) {
throw new ERR_INVALID_ARG_VALUE('conditions', conditions,
'expected an array');
}
return new SafeSet(conditions);
}
return DEFAULT_CONDITIONS_SET;
}
const realpathCache = new SafeMap();
/**
@@ -1125,7 +1096,6 @@ async function defaultResolve(specifier, context = {}) {
}
module.exports = {
DEFAULT_CONDITIONS,
defaultResolve,
encodedSepRegEx,
getPackageScopeConfig,

View File

@@ -115,7 +115,8 @@ translators.set('module', async function moduleStrategy(url, source, isMain) {
maybeCacheSourceMap(url, source);
debug(`Translating StandardModule ${url}`);
const module = new ModuleWrap(url, undefined, source, 0, 0);
moduleWrap.callbackMap.set(module, {
const { setCallbackForWrap } = require('internal/modules/esm/utils');
setCallbackForWrap(module, {
initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }),
importModuleDynamically,
});

View File

@@ -0,0 +1,105 @@
'use strict';
const {
ArrayIsArray,
SafeSet,
SafeWeakMap,
ObjectFreeze,
} = primordials;
const {
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
ERR_INVALID_ARG_VALUE,
} = require('internal/errors').codes;
const { getOptionValue } = require('internal/options');
const {
setImportModuleDynamicallyCallback,
setInitializeImportMetaObjectCallback
} = internalBinding('module_wrap');
const {
getModuleFromWrap,
} = require('internal/vm/module');
const assert = require('internal/assert');
const callbackMap = new SafeWeakMap();
function setCallbackForWrap(wrap, data) {
callbackMap.set(wrap, data);
}
let defaultConditions;
function getDefaultConditions() {
assert(defaultConditions !== undefined);
return defaultConditions;
}
let defaultConditionsSet;
function getDefaultConditionsSet() {
assert(defaultConditionsSet !== undefined);
return defaultConditionsSet;
}
// This function is called during pre-execution, before any user code is run.
function initializeDefaultConditions() {
const userConditions = getOptionValue('--conditions');
const noAddons = getOptionValue('--no-addons');
const addonConditions = noAddons ? [] : ['node-addons'];
defaultConditions = ObjectFreeze([
'node',
'import',
...addonConditions,
...userConditions,
]);
defaultConditionsSet = new SafeSet(defaultConditions);
}
/**
* @param {string[]} [conditions]
* @returns {Set<string>}
*/
function getConditionsSet(conditions) {
if (conditions !== undefined && conditions !== getDefaultConditions()) {
if (!ArrayIsArray(conditions)) {
throw new ERR_INVALID_ARG_VALUE('conditions', conditions,
'expected an array');
}
return new SafeSet(conditions);
}
return getDefaultConditionsSet();
}
function initializeImportMetaObject(wrap, meta) {
if (callbackMap.has(wrap)) {
const { initializeImportMeta } = callbackMap.get(wrap);
if (initializeImportMeta !== undefined) {
initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap);
}
}
}
async function importModuleDynamicallyCallback(wrap, specifier, assertions) {
if (callbackMap.has(wrap)) {
const { importModuleDynamically } = callbackMap.get(wrap);
if (importModuleDynamically !== undefined) {
return importModuleDynamically(
specifier, getModuleFromWrap(wrap) || wrap, assertions);
}
}
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
}
function initializeESM() {
initializeDefaultConditions();
// Setup per-isolate callbacks that locate data or callbacks that we keep
// track of for different ESM modules.
setInitializeImportMetaObjectCallback(initializeImportMetaObject);
setImportModuleDynamicallyCallback(importModuleDynamicallyCallback);
}
module.exports = {
setCallbackForWrap,
initializeESM,
getDefaultConditions,
getConditionsSet,
};

View File

@@ -5,40 +5,11 @@ const {
ObjectCreate,
} = primordials;
const {
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
} = require('internal/errors').codes;
const { ESMLoader } = require('internal/modules/esm/loader');
const {
hasUncaughtExceptionCaptureCallback,
} = require('internal/process/execution');
const { pathToFileURL } = require('internal/url');
const {
getModuleFromWrap,
} = require('internal/vm/module');
exports.initializeImportMetaObject = function(wrap, meta) {
const { callbackMap } = internalBinding('module_wrap');
if (callbackMap.has(wrap)) {
const { initializeImportMeta } = callbackMap.get(wrap);
if (initializeImportMeta !== undefined) {
initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap);
}
}
};
exports.importModuleDynamicallyCallback =
async function importModuleDynamicallyCallback(wrap, specifier, assertions) {
const { callbackMap } = internalBinding('module_wrap');
if (callbackMap.has(wrap)) {
const { importModuleDynamically } = callbackMap.get(wrap);
if (importModuleDynamically !== undefined) {
return importModuleDynamically(
specifier, getModuleFromWrap(wrap) || wrap, assertions);
}
}
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
};
const esmLoader = new ESMLoader();
exports.esmLoader = esmLoader;

View File

@@ -6,7 +6,6 @@ const {
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor,
SafeMap,
SafeWeakMap,
StringPrototypeStartsWith,
globalThis,
} = primordials;
@@ -571,20 +570,10 @@ function initializeCJSLoader() {
}
function initializeESMLoader() {
// Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
internalBinding('module_wrap').callbackMap = new SafeWeakMap();
if (getEmbedderOptions().shouldNotRegisterESMLoader) return;
const {
setImportModuleDynamicallyCallback,
setInitializeImportMetaObjectCallback
} = internalBinding('module_wrap');
const esm = require('internal/process/esm_loader');
// Setup per-isolate callbacks that locate data or callbacks that we keep
// track of for different ESM modules.
setInitializeImportMetaObjectCallback(esm.initializeImportMetaObject);
setImportModuleDynamicallyCallback(esm.importModuleDynamicallyCallback);
const { initializeESM } = require('internal/modules/esm/utils');
initializeESM();
// Patch the vm module when --experimental-vm-modules is on.
// Please update the comments in vm.js when this block changes.

View File

@@ -94,12 +94,11 @@ function internalCompileFunction(code, params, options) {
if (importModuleDynamically !== undefined) {
validateFunction(importModuleDynamically,
'options.importModuleDynamically');
const { importModuleDynamicallyWrap } =
require('internal/vm/module');
const { callbackMap } = internalBinding('module_wrap');
const { importModuleDynamicallyWrap } = require('internal/vm/module');
const wrapped = importModuleDynamicallyWrap(importModuleDynamically);
const func = result.function;
callbackMap.set(result.cacheKey, {
const { setCallbackForWrap } = require('internal/modules/esm/utils');
setCallbackForWrap(result.cacheKey, {
importModuleDynamically: (s, _k, i) => wrapped(s, func, i),
});
}

View File

@@ -125,8 +125,8 @@ class Module {
this[kWrap] = new ModuleWrap(identifier, context, sourceText,
options.lineOffset, options.columnOffset,
options.cachedData);
binding.callbackMap.set(this[kWrap], {
const { setCallbackForWrap } = require('internal/modules/esm/utils');
setCallbackForWrap(this[kWrap], {
initializeImportMeta: options.initializeImportMeta,
importModuleDynamically: options.importModuleDynamically ?
importModuleDynamicallyWrap(options.importModuleDynamically) :

View File

@@ -111,10 +111,9 @@ class Script extends ContextifyScript {
if (importModuleDynamically !== undefined) {
validateFunction(importModuleDynamically,
'options.importModuleDynamically');
const { importModuleDynamicallyWrap } =
require('internal/vm/module');
const { callbackMap } = internalBinding('module_wrap');
callbackMap.set(this, {
const { importModuleDynamicallyWrap } = require('internal/vm/module');
const { setCallbackForWrap } = require('internal/modules/esm/utils');
setCallbackForWrap(this, {
importModuleDynamically:
importModuleDynamicallyWrap(importModuleDynamically),
});

View File

@@ -65,6 +65,7 @@ const expectedModules = new Set([
'NativeModule internal/modules/esm/package_config',
'NativeModule internal/modules/esm/resolve',
'NativeModule internal/modules/esm/translators',
'NativeModule internal/modules/esm/utils',
'NativeModule internal/modules/package_json_reader',
'NativeModule internal/modules/run_main',
'NativeModule internal/net',