bootstrap: lazy load non-essential modules

It turns out that even with startup snapshots, there is a non-trivial
overhead for loading internal modules. This patch makes the loading
of the non-essential modules lazy again.

Caveat: we have to make some of the globals lazily-loaded too,
so the WPT runner is updated to test what the state of the global
scope is after the globals are accessed (and replaced with the
loaded value).

PR-URL: https://github.com/nodejs/node/pull/45659
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Reviewed-By: Tierney Cyren <hello@bnb.im>
This commit is contained in:
Joyee Cheung 2022-12-09 23:37:35 +01:00 committed by GitHub
parent 7d80ca3683
commit 265ea1e74e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 320 additions and 366 deletions

View File

@ -78,7 +78,8 @@ const {
isInsideNodeModules,
lazyDOMException,
normalizeEncoding,
kIsEncodingSymbol
kIsEncodingSymbol,
defineLazyProperties,
} = require('internal/util');
const {
isAnyArrayBuffer,
@ -121,15 +122,6 @@ const {
createUnsafeBuffer
} = require('internal/buffer');
const {
Blob,
resolveObjectURL,
} = require('internal/blob');
const {
File,
} = require('internal/file');
FastBuffer.prototype.constructor = Buffer;
Buffer.prototype = FastBuffer.prototype;
addBufferPrototypeMethods(Buffer.prototype);
@ -1323,9 +1315,6 @@ function atob(input) {
}
module.exports = {
Blob,
File,
resolveObjectURL,
Buffer,
SlowBuffer,
transcode,
@ -1352,3 +1341,14 @@ ObjectDefineProperties(module.exports, {
set(val) { INSPECT_MAX_BYTES = val; }
}
});
defineLazyProperties(
module.exports,
'internal/blob',
['Blob', 'resolveObjectURL']
);
defineLazyProperties(
module.exports,
'internal/file',
['File']
);

View File

@ -57,7 +57,6 @@ const {
const pathModule = require('path');
const { isArrayBufferView } = require('internal/util/types');
const nonNativeWatcher = require('internal/fs/recursive_watch');
// We need to get the statValues from the binding at the callsite since
// it's re-initialized after deserialization.
@ -84,6 +83,7 @@ const {
custom: kCustomPromisifiedSymbol,
},
SideEffectFreeRegExpPrototypeExec,
defineLazyProperties,
} = require('internal/util');
const {
constants: {
@ -119,11 +119,6 @@ const {
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate
} = require('internal/fs/utils');
const {
Dir,
opendir,
opendirSync
} = require('internal/fs/dir');
const {
CHAR_FORWARD_SLASH,
CHAR_BACKWARD_SLASH,
@ -140,9 +135,6 @@ const {
validateString,
} = require('internal/validators');
const watchers = require('internal/fs/watchers');
const ReadFileContext = require('internal/fs/read_file_context');
let truncateWarn = true;
let fs;
@ -379,6 +371,7 @@ function checkAborted(signal, callback) {
function readFile(path, options, callback) {
callback = maybeCallback(callback || options);
options = getOptions(options, { flag: 'r' });
const ReadFileContext = require('internal/fs/read_file_context');
const context = new ReadFileContext(callback, options.encoding);
context.isUserFd = isFd(path); // File descriptor ownership
@ -2298,11 +2291,12 @@ function watch(filename, options, listener) {
if (options.recursive === undefined) options.recursive = false;
let watcher;
const watchers = require('internal/fs/watchers');
// TODO(anonrig): Remove non-native watcher when/if libuv supports recursive.
// As of November 2022, libuv does not support recursive file watch on all platforms,
// e.g. Linux due to the limitations of inotify.
if (options.recursive && !isOSX && !isWindows) {
const nonNativeWatcher = require('internal/fs/recursive_watch');
watcher = new nonNativeWatcher.FSWatcher(options);
watcher[watchers.kFSWatchStart](filename);
} else {
@ -2370,7 +2364,7 @@ function watchFile(filename, options, listener) {
validateFunction(listener, 'listener');
stat = statWatchers.get(filename);
const watchers = require('internal/fs/watchers');
if (stat === undefined) {
stat = new watchers.StatWatcher(options.bigint);
stat[watchers.kFSStatWatcherStart](filename,
@ -2396,7 +2390,7 @@ function unwatchFile(filename, listener) {
const stat = statWatchers.get(filename);
if (stat === undefined) return;
const watchers = require('internal/fs/watchers');
if (typeof listener === 'function') {
const beforeListenerCount = stat.listenerCount('change');
stat.removeListener('change', listener);
@ -3000,8 +2994,6 @@ module.exports = fs = {
mkdtempSync,
open,
openSync,
opendir,
opendirSync,
readdir,
readdirSync,
read,
@ -3039,7 +3031,6 @@ module.exports = fs = {
writeSync,
writev,
writevSync,
Dir,
Dirent,
Stats,
@ -3085,6 +3076,12 @@ module.exports = fs = {
_toUnixTimestamp: toUnixTimestamp
};
defineLazyProperties(
fs,
'internal/fs/dir',
['Dir', 'opendir', 'opendirSync']
);
ObjectDefineProperties(fs, {
F_OK: { __proto__: null, enumerable: true, value: F_OK || 0 },
R_OK: { __proto__: null, enumerable: true, value: R_OK || 0 },

View File

@ -10,8 +10,6 @@ const {
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const promiseHooks = require('internal/promise_hooks');
const async_wrap = internalBinding('async_wrap');
const { setCallbackTrampoline } = async_wrap;
/* async_hook_fields is a Uint32Array wrapping the uint32_t array of
@ -384,6 +382,7 @@ function updatePromiseHookMode() {
initHook = destroyTracking;
}
if (stopPromiseHook) stopPromiseHook();
const promiseHooks = require('internal/promise_hooks');
stopPromiseHook = promiseHooks.createHook({
init: initHook,
before: promiseBeforeHook,

View File

@ -9,6 +9,9 @@ const {
defineOperation,
exposeInterface,
lazyDOMExceptionClass,
defineLazyProperties,
defineReplaceableLazyAttribute,
exposeLazyInterfaces,
} = require('internal/util');
const config = internalBinding('config');
@ -28,36 +31,6 @@ exposeGetterAndSetter(globalThis,
exposeInterface(globalThis, 'DOMException', value);
});
const {
TextEncoder, TextDecoder
} = require('internal/encoding');
// https://encoding.spec.whatwg.org/#textencoder
exposeInterface(globalThis, 'TextEncoder', TextEncoder);
// https://encoding.spec.whatwg.org/#textdecoder
exposeInterface(globalThis, 'TextDecoder', TextDecoder);
const {
AbortController,
AbortSignal,
} = require('internal/abort_controller');
exposeInterface(globalThis, 'AbortController', AbortController);
exposeInterface(globalThis, 'AbortSignal', AbortSignal);
const {
EventTarget,
Event,
} = require('internal/event_target');
exposeInterface(globalThis, 'EventTarget', EventTarget);
exposeInterface(globalThis, 'Event', Event);
const {
MessageChannel,
MessagePort,
MessageEvent,
} = require('internal/worker/io');
exposeInterface(globalThis, 'MessageChannel', MessageChannel);
exposeInterface(globalThis, 'MessagePort', MessagePort);
exposeInterface(globalThis, 'MessageEvent', MessageEvent);
// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope
const timers = require('timers');
defineOperation(globalThis, 'clearInterval', timers.clearInterval);
@ -65,24 +38,32 @@ defineOperation(globalThis, 'clearTimeout', timers.clearTimeout);
defineOperation(globalThis, 'setInterval', timers.setInterval);
defineOperation(globalThis, 'setTimeout', timers.setTimeout);
const buffer = require('buffer');
defineOperation(globalThis, 'atob', buffer.atob);
defineOperation(globalThis, 'btoa', buffer.btoa);
// Lazy ones.
exposeLazyInterfaces(globalThis, 'internal/abort_controller', [
'AbortController', 'AbortSignal',
]);
exposeLazyInterfaces(globalThis, 'internal/event_target', [
'EventTarget', 'Event',
]);
exposeLazyInterfaces(globalThis, 'internal/worker/io', [
'MessageChannel', 'MessagePort', 'MessageEvent',
]);
defineLazyProperties(globalThis, 'buffer', ['atob', 'btoa']);
// https://www.w3.org/TR/FileAPI/#dfn-Blob
exposeInterface(globalThis, 'Blob', buffer.Blob);
exposeLazyInterfaces(globalThis, 'internal/blob', ['Blob']);
// https://www.w3.org/TR/hr-time-2/#the-performance-attribute
const perf_hooks = require('perf_hooks');
exposeInterface(globalThis, 'Performance', perf_hooks.Performance);
exposeInterface(globalThis, 'PerformanceEntry', perf_hooks.PerformanceEntry);
exposeInterface(globalThis, 'PerformanceMark', perf_hooks.PerformanceMark);
exposeInterface(globalThis, 'PerformanceMeasure', perf_hooks.PerformanceMeasure);
exposeInterface(globalThis, 'PerformanceObserver', perf_hooks.PerformanceObserver);
exposeInterface(globalThis, 'PerformanceObserverEntryList', perf_hooks.PerformanceObserverEntryList);
exposeInterface(globalThis, 'PerformanceResourceTiming', perf_hooks.PerformanceResourceTiming);
defineReplaceableAttribute(globalThis, 'performance',
perf_hooks.performance);
exposeLazyInterfaces(globalThis, 'perf_hooks', [
'Performance', 'PerformanceEntry', 'PerformanceMark', 'PerformanceMeasure',
'PerformanceObserver', 'PerformanceObserverEntryList', 'PerformanceResourceTiming',
]);
defineReplaceableLazyAttribute(globalThis, 'perf_hooks', ['performance']);
// https://encoding.spec.whatwg.org/#textencoder
// https://encoding.spec.whatwg.org/#textdecoder
exposeLazyInterfaces(globalThis,
'internal/encoding',
['TextEncoder', 'TextDecoder']);
function createGlobalConsole() {
const consoleFromNode =
@ -120,86 +101,43 @@ function exposeGetterAndSetter(target, name, getter, setter = undefined) {
});
}
// https://webidl.spec.whatwg.org/#Replaceable
function defineReplaceableAttribute(target, name, value) {
let slot = value;
// https://webidl.spec.whatwg.org/#dfn-attribute-getter
function get() {
return slot;
}
ObjectDefineProperty(get, 'name', {
__proto__: null,
value: `get ${name}`,
});
function set(value) {
slot = value;
}
ObjectDefineProperty(set, 'name', {
__proto__: null,
value: `set ${name}`,
});
ObjectDefineProperty(target, name, {
__proto__: null,
enumerable: true,
configurable: true,
get,
set,
});
}
// Web Streams API
const {
TransformStream,
TransformStreamDefaultController,
} = require('internal/webstreams/transformstream');
exposeLazyInterfaces(
globalThis,
'internal/webstreams/transformstream',
['TransformStream', 'TransformStreamDefaultController']);
const {
WritableStream,
WritableStreamDefaultController,
WritableStreamDefaultWriter,
} = require('internal/webstreams/writablestream');
exposeLazyInterfaces(
globalThis,
'internal/webstreams/writablestream',
['WritableStream', 'WritableStreamDefaultController', 'WritableStreamDefaultWriter']);
const {
ReadableStream,
ReadableStreamDefaultReader,
ReadableStreamBYOBReader,
ReadableStreamBYOBRequest,
ReadableByteStreamController,
ReadableStreamDefaultController,
} = require('internal/webstreams/readablestream');
exposeLazyInterfaces(
globalThis,
'internal/webstreams/readablestream',
[
'ReadableStream', 'ReadableStreamDefaultReader',
'ReadableStreamBYOBReader', 'ReadableStreamBYOBRequest',
'ReadableByteStreamController', 'ReadableStreamDefaultController',
]);
const {
ByteLengthQueuingStrategy,
CountQueuingStrategy,
} = require('internal/webstreams/queuingstrategies');
exposeLazyInterfaces(
globalThis,
'internal/webstreams/queuingstrategies',
[
'ByteLengthQueuingStrategy', 'CountQueuingStrategy',
]);
const {
TextEncoderStream,
TextDecoderStream,
} = require('internal/webstreams/encoding');
exposeLazyInterfaces(
globalThis,
'internal/webstreams/encoding',
[
'TextEncoderStream', 'TextDecoderStream',
]);
const {
CompressionStream,
DecompressionStream,
} = require('internal/webstreams/compression');
exposeInterface(globalThis, 'ReadableStream', ReadableStream);
exposeInterface(globalThis, 'ReadableStreamDefaultReader', ReadableStreamDefaultReader);
exposeInterface(globalThis, 'ReadableStreamBYOBReader', ReadableStreamBYOBReader);
exposeInterface(globalThis, 'ReadableStreamBYOBRequest', ReadableStreamBYOBRequest);
exposeInterface(globalThis, 'ReadableByteStreamController', ReadableByteStreamController);
exposeInterface(globalThis, 'ReadableStreamDefaultController', ReadableStreamDefaultController);
exposeInterface(globalThis, 'TransformStream', TransformStream);
exposeInterface(globalThis, 'TransformStreamDefaultController', TransformStreamDefaultController);
exposeInterface(globalThis, 'WritableStream', WritableStream);
exposeInterface(globalThis, 'WritableStreamDefaultWriter', WritableStreamDefaultWriter);
exposeInterface(globalThis, 'WritableStreamDefaultController', WritableStreamDefaultController);
exposeInterface(globalThis, 'ByteLengthQueuingStrategy', ByteLengthQueuingStrategy);
exposeInterface(globalThis, 'CountQueuingStrategy', CountQueuingStrategy);
exposeInterface(globalThis, 'TextEncoderStream', TextEncoderStream);
exposeInterface(globalThis, 'TextDecoderStream', TextDecoderStream);
exposeInterface(globalThis, 'CompressionStream', CompressionStream);
exposeInterface(globalThis, 'DecompressionStream', DecompressionStream);
exposeLazyInterfaces(
globalThis,
'internal/webstreams/compression',
[
'CompressionStream', 'DecompressionStream',
]);

View File

@ -74,7 +74,8 @@ const internalTimers = require('internal/timers');
const {
defineOperation,
deprecate,
exposeInterface,
defineLazyProperties,
exposeLazyInterfaces,
} = require('internal/util');
const {
validateInteger,
@ -223,20 +224,23 @@ const {
} = require('internal/process/task_queues');
// Non-standard extensions:
const { BroadcastChannel } = require('internal/worker/io');
exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel);
defineOperation(globalThis, 'queueMicrotask', queueMicrotask);
const timers = require('timers');
defineOperation(globalThis, 'clearImmediate', timers.clearImmediate);
defineOperation(globalThis, 'setImmediate', timers.setImmediate);
const {
structuredClone,
} = require('internal/structured_clone');
defineOperation(globalThis, 'structuredClone', structuredClone);
defineLazyProperties(
globalThis,
'internal/structured_clone',
['structuredClone']
);
exposeLazyInterfaces(
globalThis,
'internal/worker/io',
['BroadcastChannel']
);
// Set the per-Environment callback that will be called
// when the TrackingTraceStateObserver updates trace state.
// Note that when NODE_USE_V8_PLATFORM is true, the observer is
@ -348,16 +352,6 @@ process.emitWarning = emitWarning;
// Note: only after this point are the timers effective
}
// Preload modules so that they are included in the builtin snapshot.
require('fs');
require('v8');
require('vm');
require('url');
require('internal/options');
if (config.hasOpenSSL) {
require('crypto');
}
function setupPrepareStackTrace() {
const {
setEnhanceStackForFatalException,

View File

@ -3,9 +3,11 @@
const { ObjectDefineProperty } = primordials;
const rawMethods = internalBinding('process_methods');
const {
addSerializeCallback,
isBuildingSnapshot
} = require('v8').startupSnapshot;
namespace: {
addSerializeCallback,
isBuildingSnapshot
},
} = require('internal/v8/startup_snapshot');
// TODO(joyeecheung): deprecate and remove these underscore methods
process._debugProcess = rawMethods._debugProcess;
process._debugEnd = rawMethods._debugEnd;

View File

@ -690,9 +690,11 @@ function initializeGlobalConsole(globalConsole) {
globalConsole[kBindProperties](true, 'auto');
const {
addSerializeCallback,
isBuildingSnapshot,
} = require('v8').startupSnapshot;
namespace: {
addSerializeCallback,
isBuildingSnapshot,
}
} = require('internal/v8/startup_snapshot');
if (!internalBinding('config').hasInspector || !isBuildingSnapshot()) {
return;

View File

@ -37,10 +37,12 @@ const {
} = errors.codes;
const {
addSerializeCallback,
addDeserializeCallback,
isBuildingSnapshot,
} = require('v8').startupSnapshot;
namespace: {
addSerializeCallback,
addDeserializeCallback,
isBuildingSnapshot,
},
} = require('internal/v8/startup_snapshot');
function validateTimeout(options) {
const { timeout = -1 } = { ...options };

View File

@ -200,7 +200,7 @@ function lazyBuffer() {
function isErrorStackTraceLimitWritable() {
// Do no touch Error.stackTraceLimit as V8 would attempt to install
// it again during deserialization.
if (require('v8').startupSnapshot.isBuildingSnapshot()) {
if (require('internal/v8/startup_snapshot').namespace.isBuildingSnapshot()) {
return false;
}

View File

@ -109,14 +109,6 @@ const {
JSTransferable, kDeserialize, kTransfer, kTransferList
} = require('internal/worker/js_transferable');
const {
newReadableStreamFromStreamBase,
} = require('internal/webstreams/adapters');
const {
readableStreamCancel,
} = require('internal/webstreams/readablestream');
const getDirectoryEntriesPromise = promisify(getDirents);
const validateRmOptionsPromise = promisify(validateRmOptions);
@ -264,12 +256,17 @@ class FileHandle extends EventEmitterMixin(JSTransferable) {
if (this[kLocked])
throw new ERR_INVALID_STATE('The FileHandle is locked');
this[kLocked] = true;
const {
newReadableStreamFromStreamBase,
} = require('internal/webstreams/adapters');
const readable = newReadableStreamFromStreamBase(
this[kHandle],
undefined,
{ ondone: () => this[kUnref]() });
const {
readableStreamCancel,
} = require('internal/webstreams/readablestream');
this[kRef]();
this.once('close', () => {
readableStreamCancel(readable);

View File

@ -95,7 +95,6 @@ const internalFS = require('internal/fs/utils');
const path = require('path');
const { sep } = path;
const { internalModuleStat } = internalBinding('fs');
const packageJsonReader = require('internal/modules/package_json_reader');
const { safeGetenv } = internalBinding('credentials');
const {
cjsConditions,
@ -338,6 +337,7 @@ function readPackage(requestPath) {
const existing = packageJsonCache.get(jsonPath);
if (existing !== undefined) return existing;
const packageJsonReader = require('internal/modules/package_json_reader');
const result = packageJsonReader.read(jsonPath);
const json = result.containsKeys === false ? '{}' : result.string;
if (json === undefined) {

View File

@ -8,7 +8,6 @@ const {
} = primordials;
const { basename, extname, relative } = require('path');
const { getOptionValue } = require('internal/options');
const { fetchModule } = require('internal/modules/esm/fetch_module');
const {
extensionFormatMap,
mimeToFormat,
@ -82,6 +81,7 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) {
*/
function getHttpProtocolModuleFormat(url, context) {
if (experimentalNetworkImports) {
const { fetchModule } = require('internal/modules/esm/fetch_module');
return PromisePrototypeThen(
PromiseResolve(fetchModule(url, context)),
(entry) => {

View File

@ -9,7 +9,6 @@ const {
const { defaultGetFormat } = require('internal/modules/esm/get_format');
const { validateAssertions } = require('internal/modules/esm/assert');
const { getOptionValue } = require('internal/options');
const { fetchModule } = require('internal/modules/esm/fetch_module');
// Do not eagerly grab .manifest, it may be in TDZ
const policy = getOptionValue('--experimental-policy') ?
@ -20,7 +19,6 @@ const experimentalNetworkImports =
const { Buffer: { from: BufferFrom } } = require('buffer');
const { readFile: readFileAsync } = require('internal/fs/promises').exports;
const { URL } = require('internal/url');
const {
ERR_INVALID_URL,
@ -34,6 +32,7 @@ async function getSource(url, context) {
let responseURL = url;
let source;
if (parsed.protocol === 'file:') {
const { readFile: readFileAsync } = require('internal/fs/promises').exports;
source = await readFileAsync(parsed);
} else if (parsed.protocol === 'data:') {
const match = RegExpPrototypeExec(DATA_URL_PATTERN, parsed.pathname);
@ -46,6 +45,7 @@ async function getSource(url, context) {
parsed.protocol === 'https:' ||
parsed.protocol === 'http:'
)) {
const { fetchModule } = require('internal/modules/esm/fetch_module');
const res = await fetchModule(parsed, context);
source = await res.body;
responseURL = res.resolvedHREF;

View File

@ -20,7 +20,6 @@ const {
StringPrototypeToUpperCase,
globalThis,
} = primordials;
const { MessageChannel } = require('internal/worker/io');
const {
ERR_LOADER_CHAIN_INCOMPLETE,
@ -41,19 +40,20 @@ const {
validateObject,
validateString,
} = require('internal/validators');
const ModuleMap = require('internal/modules/esm/module_map');
const ModuleJob = require('internal/modules/esm/module_job');
function newModuleMap() {
const ModuleMap = require('internal/modules/esm/module_map');
return new ModuleMap();
}
const {
defaultResolve,
DEFAULT_CONDITIONS,
} = require('internal/modules/esm/resolve');
const {
initializeImportMeta
} = require('internal/modules/esm/initialize_import_meta');
const { defaultLoad } = require('internal/modules/esm/load');
const { translators } = require(
'internal/modules/esm/translators');
function getTranslators() {
const { translators } = require('internal/modules/esm/translators');
return translators;
}
const { getOptionValue } = require('internal/options');
/**
@ -176,6 +176,7 @@ function nextHookFactory(chain, meta, { validateArgs, validateOutput }) {
* Currently, this is a singleton -- there is only one used for loading
* the main module and everything in its dependency graph.
*/
class ESMLoader {
#hooks = {
/**
@ -192,7 +193,7 @@ class ESMLoader {
*/
load: [
{
fn: defaultLoad,
fn: require('internal/modules/esm/load').defaultLoad,
url: 'node:internal/modules/esm/load',
},
],
@ -210,7 +211,7 @@ class ESMLoader {
],
};
#importMetaInitializer = initializeImportMeta;
#importMetaInitializer = require('internal/modules/esm/initialize_import_meta').initializeImportMeta;
/**
* Map of already-loaded CJS modules to use
@ -225,12 +226,12 @@ class ESMLoader {
/**
* Registry of loaded modules, akin to `require.cache`
*/
moduleMap = new ModuleMap();
moduleMap = newModuleMap();
/**
* Methods which translate input code or other information into ES modules
*/
translators = translators;
translators = getTranslators();
constructor() {
if (getOptionValue('--experimental-loader').length > 0) {
@ -372,6 +373,7 @@ class ESMLoader {
return module;
};
const ModuleJob = require('internal/modules/esm/module_job');
const job = new ModuleJob(
this, url, undefined, evalInstance, false, false);
this.moduleMap.set(url, undefined, job);
@ -448,7 +450,7 @@ class ESMLoader {
importAssertions,
});
const translator = translators.get(finalFormat);
const translator = getTranslators().get(finalFormat);
if (!translator) {
throw new ERR_UNKNOWN_MODULE_FORMAT(finalFormat, responseURL);
@ -465,7 +467,7 @@ class ESMLoader {
if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) {
process.send({ 'watch:import': [url] });
}
const ModuleJob = require('internal/modules/esm/module_job');
const job = new ModuleJob(
this,
url,
@ -674,6 +676,7 @@ class ESMLoader {
preload() {
for (let i = this.#hooks.globalPreload.length - 1; i >= 0; i--) {
const { MessageChannel } = require('internal/worker/io');
const channel = new MessageChannel();
const {
port1: insidePreload,

View File

@ -1,6 +1,5 @@
'use strict';
const ModuleJob = require('internal/modules/esm/module_job');
const { kImplicitAssertType } = require('internal/modules/esm/assert');
const {
ObjectCreate,
@ -23,6 +22,8 @@ class ModuleMap extends SafeMap {
set(url, type = kImplicitAssertType, job) {
validateString(url, 'url');
validateString(type, 'type');
const ModuleJob = require('internal/modules/esm/module_job');
if (job instanceof ModuleJob !== true &&
typeof job !== 'function') {
throw new ERR_INVALID_ARG_TYPE('job', 'ModuleJob', job);

View File

@ -11,7 +11,6 @@ const {
ERR_INVALID_PACKAGE_CONFIG,
} = require('internal/errors').codes;
const packageJsonReader = require('internal/modules/package_json_reader');
const { filterOwnProperties } = require('internal/util');
@ -42,6 +41,7 @@ function getPackageConfig(path, specifier, base) {
if (existing !== undefined) {
return existing;
}
const packageJsonReader = require('internal/modules/package_json_reader');
const source = packageJsonReader.read(path).string;
if (source === undefined) {
const packageConfig = {

View File

@ -36,8 +36,6 @@ const {
cjsParseCache
} = require('internal/modules/cjs/loader');
const internalURLModule = require('internal/url');
const createDynamicModule = require(
'internal/modules/esm/create_dynamic_module');
const { fileURLToPath, URL } = require('url');
let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
debug = fn;
@ -52,7 +50,6 @@ const moduleWrap = internalBinding('module_wrap');
const { ModuleWrap } = moduleWrap;
const asyncESM = require('internal/process/esm_loader');
const { emitWarningSync } = require('internal/process/warning');
const { TextDecoder } = require('internal/encoding');
let cjsParse;
async function initCJSParse() {
@ -94,6 +91,7 @@ function assertBufferSource(body, allowString, hookName) {
function stringify(body) {
if (typeof body === 'string') return body;
assertBufferSource(body, false, 'transformSource');
const { TextDecoder } = require('internal/encoding');
DECODER = DECODER === null ? new TextDecoder() : DECODER;
return DECODER.decode(body);
}
@ -341,6 +339,8 @@ translators.set('wasm', async function(url, source) {
ArrayPrototypeMap(WebAssembly.Module.exports(compiled),
({ name }) => name);
const createDynamicModule = require(
'internal/modules/esm/create_dynamic_module');
return createDynamicModule(imports, exports, url, (reflect) => {
const { exports } = new WebAssembly.Instance(compiled, reflect.imports);
for (const expt of ObjectKeys(exports))

View File

@ -8,9 +8,6 @@ const CJSLoader = require('internal/modules/cjs/loader');
const { Module, toRealPath, readPackageScope } = CJSLoader;
const { getOptionValue } = require('internal/options');
const path = require('path');
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
function resolveMainPath(main) {
// Note extension resolution for the main entry point can be deprecated in a
@ -61,6 +58,9 @@ function runMainESM(mainPath) {
}
async function handleMainPromise(promise) {
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
process.on('exit', handleProcessExit);
try {
return await promise;

View File

@ -49,10 +49,6 @@ const {
} = require('internal/validators');
const constants = internalBinding('constants').os.signals;
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
const kInternal = Symbol('internal properties');
function assert(x, msg) {
@ -181,6 +177,9 @@ function wrapProcessMethods(binding) {
memoryUsage.rss = rss;
function exit(code) {
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
process.off('exit', handleProcessExit);
if (arguments.length !== 0) {

View File

@ -20,6 +20,8 @@ const { reconnectZeroFillToggle } = require('internal/buffer');
const {
defineOperation,
exposeInterface,
exposeLazyInterfaces,
defineReplaceableLazyAttribute,
} = require('internal/util');
const {
@ -27,11 +29,12 @@ const {
ERR_NO_CRYPTO,
} = require('internal/errors').codes;
const assert = require('internal/assert');
const {
addSerializeCallback,
isBuildingSnapshot,
} = require('v8').startupSnapshot;
namespace: {
addSerializeCallback,
isBuildingSnapshot,
},
} = require('internal/v8/startup_snapshot');
function prepareMainThreadExecution(expandArgv1 = false, initializeModules = true) {
prepareExecution({
@ -274,16 +277,13 @@ function setupWebCrypto() {
}
if (internalBinding('config').hasOpenSSL) {
const webcrypto = require('internal/crypto/webcrypto');
ObjectDefineProperty(globalThis, 'crypto',
{ __proto__: null, ...ObjectGetOwnPropertyDescriptor({
get crypto() {
return webcrypto.crypto;
}
}, 'crypto') });
exposeInterface(globalThis, 'Crypto', webcrypto.Crypto);
exposeInterface(globalThis, 'CryptoKey', webcrypto.CryptoKey);
exposeInterface(globalThis, 'SubtleCrypto', webcrypto.SubtleCrypto);
defineReplaceableLazyAttribute(
globalThis, 'internal/crypto/webcrypto', ['crypto'], false
);
exposeLazyInterfaces(
globalThis, 'internal/crypto/webcrypto',
['Crypto', 'CryptoKey', 'SubtleCrypto']
);
} else {
ObjectDefineProperty(globalThis, 'crypto',
{ __proto__: null, ...ObjectGetOwnPropertyDescriptor({

View File

@ -23,7 +23,6 @@ const { Buffer } = require('buffer');
let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
debug = fn;
});
const fs = require('fs');
const { getOptionValue } = require('internal/options');
const { IterableWeakMap } = require('internal/util/iterable_weak_map');
const {
@ -222,6 +221,7 @@ function lineLengths(content) {
function sourceMapFromFile(mapURL) {
try {
const fs = require('fs');
const content = fs.readFileSync(fileURLToPath(mapURL), 'utf8');
const data = JSONParse(content);
return sourcesToAbsolute(mapURL, data);

View File

@ -501,6 +501,90 @@ function exposeInterface(target, name, interfaceObject) {
});
}
function defineLazyProperties(target, id, keys, enumerable = true) {
const descriptors = { __proto__: null };
let mod;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
let lazyLoadedValue;
function set(value) {
ObjectDefineProperty(target, key, {
__proto__: null,
writable: true,
value,
});
}
ObjectDefineProperty(set, 'name', {
__proto__: null,
value: `set ${key}`,
});
function get() {
mod ??= require(id);
if (lazyLoadedValue === undefined) {
lazyLoadedValue = mod[key];
set(lazyLoadedValue);
}
return lazyLoadedValue;
}
ObjectDefineProperty(get, 'name', {
__proto__: null,
value: `get ${key}`,
});
descriptors[key] = {
__proto__: null,
configurable: true,
enumerable,
get,
set,
};
}
ObjectDefineProperties(target, descriptors);
}
function defineReplaceableLazyAttribute(target, id, keys, writable = true) {
let mod;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
let value;
let setterCalled = false;
function get() {
if (setterCalled) {
return value;
}
mod ??= require(id);
value ??= mod[key];
return value;
}
ObjectDefineProperty(get, 'name', {
__proto__: null,
value: `get ${key}`,
});
function set(val) {
setterCalled = true;
value = val;
}
ObjectDefineProperty(set, 'name', {
__proto__: null,
value: `set ${key}`,
});
ObjectDefineProperty(target, key, {
__proto__: null,
enumerable: true,
configurable: true,
get,
set: writable ? set : undefined,
});
}
}
function exposeLazyInterfaces(target, id, keys) {
defineLazyProperties(target, id, keys, false);
}
let _DOMException;
const lazyDOMExceptionClass = () => {
_DOMException ??= internalBinding('messaging').DOMException;
@ -578,9 +662,12 @@ module.exports = {
createDeferredPromise,
decorateErrorStack,
defineOperation,
defineLazyProperties,
defineReplaceableLazyAttribute,
deprecate,
emitExperimentalWarning,
exposeInterface,
exposeLazyInterfaces,
filterDuplicateStrings,
filterOwnProperties,
getConstructorOf,

View File

@ -62,13 +62,10 @@ const {
stripVTControlCharacters,
} = require('internal/util/inspect');
const { debuglog } = require('internal/util/debuglog');
const { parseArgs } = require('internal/util/parse_args/parse_args');
const {
validateFunction,
validateNumber,
} = require('internal/validators');
const { TextDecoder, TextEncoder } = require('internal/encoding');
const { MIMEType, MIMEParams } = require('internal/mime');
const { isBuffer } = require('buffer').Buffer;
const types = require('internal/util/types');
@ -78,6 +75,7 @@ const {
getSystemErrorName: internalErrorName,
promisify,
toUSVString,
defineLazyProperties,
} = require('internal/util');
let abortController;
@ -386,14 +384,9 @@ module.exports = {
isFunction,
isPrimitive,
log,
MIMEType,
MIMEParams,
parseArgs,
promisify,
stripVTControlCharacters,
toUSVString,
TextDecoder,
TextEncoder,
get transferableAbortSignal() {
return lazyAbortController().transferableAbortSignal;
},
@ -402,3 +395,21 @@ module.exports = {
},
types
};
defineLazyProperties(
module.exports,
'internal/util/parse_args/parse_args',
['parseArgs']
);
defineLazyProperties(
module.exports,
'internal/encoding',
['TextDecoder', 'TextEncoder']
);
defineLazyProperties(
module.exports,
'internal/mime',
['MIMEType', 'MIMEParams']
);

View File

@ -364,6 +364,7 @@ class WPTRunner {
this.globalThisInitScripts.push(
`global.Window = Object.getPrototypeOf(globalThis).constructor;
self.GLOBAL.isWorker = () => false;`);
this.loadLazyGlobals();
break;
}
@ -377,6 +378,31 @@ class WPTRunner {
}
}
loadLazyGlobals() {
const lazyProperties = [
'Performance', 'PerformanceEntry', 'PerformanceMark', 'PerformanceMeasure',
'PerformanceObserver', 'PerformanceObserverEntryList', 'PerformanceResourceTiming',
'Blob', 'atob', 'btoa',
'MessageChannel', 'MessagePort', 'MessageEvent',
'EventTarget', 'Event',
'AbortController', 'AbortSignal',
'performance',
'TransformStream', 'TransformStreamDefaultController',
'WritableStream', 'WritableStreamDefaultController', 'WritableStreamDefaultWriter',
'ReadableStream', 'ReadableStreamDefaultReader',
'ReadableStreamBYOBReader', 'ReadableStreamBYOBRequest',
'ReadableByteStreamController', 'ReadableStreamDefaultController',
'ByteLengthQueuingStrategy', 'CountQueuingStrategy',
'TextEncoderStream', 'TextDecoderStream',
'CompressionStream', 'DecompressionStream',
];
if (Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO) {
lazyProperties.push('crypto');
}
const script = lazyProperties.map((name) => `globalThis.${name};`).join('\n');
this.globalThisInitScripts.push(script);
}
brandCheckGlobalScopeAttribute(name) {
// TODO(legendecas): idlharness GlobalScope attribute receiver validation.
const script = `

View File

@ -10,7 +10,6 @@ const assert = require('assert');
const expectedModules = new Set([
'Internal Binding async_wrap',
'Internal Binding block_list',
'Internal Binding buffer',
'Internal Binding builtins',
'Internal Binding config',
@ -18,74 +17,48 @@ const expectedModules = new Set([
'Internal Binding contextify',
'Internal Binding credentials',
'Internal Binding errors',
'Internal Binding fs_dir',
'Internal Binding fs_event_wrap',
'Internal Binding fs',
'Internal Binding heap_utils',
'Internal Binding mksnapshot',
'Internal Binding messaging',
'Internal Binding module_wrap',
'Internal Binding options',
'Internal Binding performance',
'Internal Binding pipe_wrap',
'Internal Binding process_methods',
'Internal Binding report',
'Internal Binding serdes',
'Internal Binding stream_wrap',
'Internal Binding string_decoder',
'Internal Binding symbols',
'Internal Binding task_queue',
'Internal Binding tcp_wrap',
'Internal Binding timers',
'Internal Binding trace_events',
'Internal Binding types',
'Internal Binding url',
'Internal Binding util',
'Internal Binding uv',
'Internal Binding v8',
'Internal Binding wasm_web_api',
'Internal Binding worker',
'NativeModule buffer',
'NativeModule diagnostics_channel',
'NativeModule events',
'NativeModule fs',
'NativeModule internal/abort_controller',
'NativeModule internal/assert',
'NativeModule internal/async_hooks',
'NativeModule internal/blocklist',
'NativeModule internal/buffer',
'NativeModule internal/console/constructor',
'NativeModule internal/console/global',
'NativeModule internal/constants',
'NativeModule internal/dns/utils',
'NativeModule internal/encoding',
'NativeModule internal/errors',
'NativeModule internal/event_target',
'NativeModule internal/fixed_queue',
'NativeModule internal/fs/dir',
'NativeModule internal/fs/promises',
'NativeModule internal/fs/read_file_context',
'NativeModule internal/fs/recursive_watch',
'NativeModule internal/fs/rimraf',
'NativeModule internal/fs/utils',
'NativeModule internal/fs/watchers',
'NativeModule internal/heap_utils',
'NativeModule internal/histogram',
'NativeModule internal/idna',
'NativeModule internal/linkedlist',
'NativeModule internal/mime',
'NativeModule internal/modules/cjs/helpers',
'NativeModule internal/modules/cjs/loader',
'NativeModule internal/modules/esm/assert',
'NativeModule internal/modules/esm/create_dynamic_module',
'NativeModule internal/modules/esm/fetch_module',
'NativeModule internal/modules/esm/formats',
'NativeModule internal/modules/esm/get_format',
'NativeModule internal/modules/esm/handle_process_exit',
'NativeModule internal/modules/esm/initialize_import_meta',
'NativeModule internal/modules/esm/load',
'NativeModule internal/modules/esm/loader',
'NativeModule internal/modules/esm/module_job',
'NativeModule internal/modules/esm/module_map',
'NativeModule internal/modules/esm/package_config',
'NativeModule internal/modules/esm/resolve',
@ -94,15 +67,6 @@ const expectedModules = new Set([
'NativeModule internal/modules/run_main',
'NativeModule internal/net',
'NativeModule internal/options',
'NativeModule internal/perf/event_loop_delay',
'NativeModule internal/perf/event_loop_utilization',
'NativeModule internal/perf/nodetiming',
'NativeModule internal/perf/observe',
'NativeModule internal/perf/performance_entry',
'NativeModule internal/perf/performance',
'NativeModule internal/perf/timerify',
'NativeModule internal/perf/usertiming',
'NativeModule internal/perf/resource_timing',
'NativeModule internal/perf/utils',
'NativeModule internal/priority_queue',
'NativeModule internal/process/esm_loader',
@ -114,70 +78,27 @@ const expectedModules = new Set([
'NativeModule internal/process/signal',
'NativeModule internal/process/task_queues',
'NativeModule internal/process/warning',
'NativeModule internal/promise_hooks',
'NativeModule internal/querystring',
'NativeModule internal/readline/callbacks',
'NativeModule internal/readline/interface',
'NativeModule internal/readline/utils',
'NativeModule internal/socketaddress',
'NativeModule internal/source_map/source_map_cache',
'NativeModule internal/stream_base_commons',
'NativeModule internal/streams/add-abort-signal',
'NativeModule internal/streams/buffer_list',
'NativeModule internal/streams/compose',
'NativeModule internal/streams/destroy',
'NativeModule internal/streams/duplex',
'NativeModule internal/streams/end-of-stream',
'NativeModule internal/streams/from',
'NativeModule internal/streams/legacy',
'NativeModule internal/streams/operators',
'NativeModule internal/streams/passthrough',
'NativeModule internal/streams/pipeline',
'NativeModule internal/streams/readable',
'NativeModule internal/streams/state',
'NativeModule internal/streams/transform',
'NativeModule internal/streams/utils',
'NativeModule internal/streams/writable',
'NativeModule internal/structured_clone',
'NativeModule internal/timers',
'NativeModule internal/url',
'NativeModule internal/util',
'NativeModule internal/util/debuglog',
'NativeModule internal/util/inspect',
'NativeModule internal/util/iterable_weak_map',
'NativeModule internal/util/parse_args/utils',
'NativeModule internal/util/parse_args/parse_args',
'NativeModule internal/util/types',
'NativeModule internal/validators',
'NativeModule internal/vm',
'NativeModule internal/vm/module',
'NativeModule internal/wasm_web_api',
'NativeModule internal/webidl',
'NativeModule internal/webstreams/adapters',
'NativeModule internal/webstreams/compression',
'NativeModule internal/webstreams/encoding',
'NativeModule internal/webstreams/queuingstrategies',
'NativeModule internal/webstreams/readablestream',
'NativeModule internal/webstreams/transformstream',
'NativeModule internal/webstreams/util',
'NativeModule internal/webstreams/writablestream',
'NativeModule internal/worker/io',
'NativeModule internal/worker/js_transferable',
'Internal Binding blob',
'NativeModule internal/blob',
'NativeModule internal/file',
'NativeModule async_hooks',
'NativeModule net',
'NativeModule path',
'NativeModule perf_hooks',
'NativeModule querystring',
'NativeModule stream',
'NativeModule stream/promises',
'NativeModule string_decoder',
'NativeModule timers',
'NativeModule url',
'NativeModule util',
'NativeModule v8',
'NativeModule internal/v8/startup_snapshot',
'NativeModule vm',
]);
@ -188,23 +109,33 @@ if (!common.isMainThread) {
'Internal Binding performance',
'Internal Binding symbols',
'Internal Binding worker',
'NativeModule internal/streams/duplex',
'NativeModule internal/streams/passthrough',
'NativeModule internal/streams/readable',
'NativeModule internal/streams/transform',
'NativeModule internal/streams/writable',
'NativeModule diagnostics_channel',
'NativeModule internal/abort_controller',
'NativeModule internal/error_serdes',
'NativeModule internal/perf/event_loop_utilization',
'NativeModule internal/process/worker_thread_only',
'NativeModule internal/streams/add-abort-signal',
'NativeModule internal/streams/buffer_list',
'NativeModule internal/streams/compose',
'NativeModule internal/streams/destroy',
'NativeModule internal/streams/duplex',
'NativeModule internal/streams/end-of-stream',
'NativeModule internal/streams/from',
'NativeModule internal/streams/legacy',
'NativeModule internal/streams/operators',
'NativeModule internal/streams/passthrough',
'NativeModule internal/streams/pipeline',
'NativeModule internal/streams/readable',
'NativeModule internal/streams/state',
'NativeModule internal/streams/transform',
'NativeModule internal/streams/utils',
'NativeModule internal/streams/writable',
'NativeModule internal/worker',
'NativeModule internal/worker/io',
'NativeModule stream',
'NativeModule worker_threads',
'NativeModule stream',
'NativeModule stream/promises',
'NativeModule string_decoder',
].forEach(expectedModules.add.bind(expectedModules));
}
@ -214,46 +145,10 @@ if (common.hasIntl) {
expectedModules.add('NativeModule url');
}
if (common.hasCrypto) {
expectedModules.add('Internal Binding crypto')
.add('NativeModule internal/crypto/hash')
.add('NativeModule internal/crypto/hashnames')
.add('NativeModule internal/crypto/keys')
.add('NativeModule internal/crypto/random')
.add('NativeModule internal/crypto/util')
.add('NativeModule internal/crypto/webcrypto')
.add('NativeModule internal/streams/lazy_transform');
}
if (process.features.inspector) {
expectedModules.add('Internal Binding inspector');
expectedModules.add('NativeModule internal/inspector_async_hook');
expectedModules.add('NativeModule internal/util/inspector');
expectedModules.add('Internal Binding profiler');
}
if (process.env.NODE_V8_COVERAGE) {
expectedModules.add('Internal Binding profiler');
}
if (common.hasCrypto) {
expectedModules.add('Internal Binding crypto');
expectedModules.add('NativeModule crypto');
expectedModules.add('NativeModule internal/crypto/certificate');
expectedModules.add('NativeModule internal/crypto/cipher');
expectedModules.add('NativeModule internal/crypto/diffiehellman');
expectedModules.add('NativeModule internal/crypto/hash');
expectedModules.add('NativeModule internal/crypto/hashnames');
expectedModules.add('NativeModule internal/crypto/hkdf');
expectedModules.add('NativeModule internal/crypto/keygen');
expectedModules.add('NativeModule internal/crypto/keys');
expectedModules.add('NativeModule internal/crypto/pbkdf2');
expectedModules.add('NativeModule internal/crypto/random');
expectedModules.add('NativeModule internal/crypto/scrypt');
expectedModules.add('NativeModule internal/crypto/sig');
expectedModules.add('NativeModule internal/crypto/util');
expectedModules.add('NativeModule internal/crypto/x509');
expectedModules.add('NativeModule internal/streams/lazy_transform');
}
const difference = (setA, setB) => {

View File

@ -6,6 +6,7 @@ const { WPTRunner } = require('../common/wpt');
const runner = new WPTRunner('streams');
// Set a script that will be executed in the worker before running the tests.
runner.pretendGlobalThisAs('Window');
runner.setInitScript(`
// Simulate global postMessage for enqueue-with-detached-buffer.window.js
function postMessage(value, origin, transferList) {