From 10f5974fac05c98979f2cb38843296b865035754 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 30 Jan 2023 14:23:22 +0100 Subject: [PATCH 01/13] bootstrap: lazy-load Performance.prototype.timeOrigin --- lib/internal/perf/performance.js | 29 +++++++++++++-------------- lib/internal/process/pre_execution.js | 1 - 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/internal/perf/performance.js b/lib/internal/perf/performance.js index 130613c5635fa4..6b619c3ecc13f7 100644 --- a/lib/internal/perf/performance.js +++ b/lib/internal/perf/performance.js @@ -206,15 +206,24 @@ ObjectDefineProperties(Performance.prototype, { enumerable: false, value: timerify, }, - // This would be updated during pre-execution in case - // the process is launched from a snapshot. - // TODO(joyeecheung): we may want to warn about access to - // this during snapshot building. timeOrigin: { __proto__: null, configurable: true, enumerable: true, - value: getTimeOriginTimestamp(), + get() { + const value = getTimeOriginTimestamp(); + ObjectDefineProperty(Performance.prototype, 'timeOrigin', { + __proto__: null, + value, + }); + return value; + }, + set(value) { + ObjectDefineProperty(Performance.prototype, 'timeOrigin', { + __proto__: null, + value, + }); + }, }, toJSON: { __proto__: null, @@ -224,15 +233,6 @@ ObjectDefineProperties(Performance.prototype, { }, }); -function refreshTimeOrigin() { - ObjectDefineProperty(Performance.prototype, 'timeOrigin', { - __proto__: null, - configurable: true, - enumerable: true, - value: getTimeOriginTimestamp(), - }); -} - const performance = new InternalPerformance(); function dispatchBufferFull(type) { @@ -246,5 +246,4 @@ setDispatchBufferFull(dispatchBufferFull); module.exports = { Performance, performance, - refreshTimeOrigin, }; diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index b4a24bbffb6c43..776be4414900da 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -383,7 +383,6 @@ function setupTraceCategoryState() { } function setupPerfHooks() { - require('internal/perf/performance').refreshTimeOrigin(); require('internal/perf/utils').refreshTimeOrigin(); } From 3076119c190b4cd6a9aca8a4a53f81374a49c840 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 30 Jan 2023 14:43:04 +0100 Subject: [PATCH 02/13] test: use an array for WPT gloablThis initialization scripts So that we can add multiple initialization scripts in the test runner. Refs: https://github.com/nodejs/node/pull/44483/ --- test/common/wpt.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/common/wpt.js b/test/common/wpt.js index 976668b69bfde3..048850a5be32b5 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -408,7 +408,7 @@ class WPTRunner { this.resource = new ResourceLoader(path); this.flags = []; - this.dummyGlobalThisScript = null; + this.globalThisInitScripts = []; this.initScript = null; this.status = new StatusLoader(path); @@ -463,17 +463,17 @@ class WPTRunner { initScript = `${initScript}\n\n//===\nglobalThis.location.search = "${locationSearchString}";`; } - if (initScript === null && this.dummyGlobalThisScript === null) { - return null; + if (this.globalThisInitScripts.length === null) { + return initScript; } + const globalThisInitScript = this.globalThisInitScripts.join('\n\n//===\n'); + if (initScript === null) { - return this.dummyGlobalThisScript; - } else if (this.dummyGlobalThisScript === null) { - return initScript; + return globalThisInitScript; } - return `${this.dummyGlobalThisScript}\n\n//===\n${initScript}`; + return `${globalThisInitScript}\n\n//===\n${initScript}`; } /** @@ -484,8 +484,9 @@ class WPTRunner { pretendGlobalThisAs(name) { switch (name) { case 'Window': { - this.dummyGlobalThisScript = - 'global.Window = Object.getPrototypeOf(globalThis).constructor;'; + this.globalThisInitScripts.push( + `global.Window = Object.getPrototypeOf(globalThis).constructor; + self.GLOBAL.isWorker = () => false;`); break; } From 9960b3df1c0a63bc0e54d48c3a537c7edebf0787 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 9 Dec 2022 23:37:35 +0100 Subject: [PATCH 03/13] 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 Reviewed-By: Yagiz Nizipli Reviewed-By: Daeyeon Jeong Reviewed-By: Jacob Smith Reviewed-By: James M Snell Reviewed-By: Rafael Gonzaga Reviewed-By: Antoine du Hamel Reviewed-By: Minwoo Jung Reviewed-By: Tierney Cyren --- lib/buffer.js | 24 +-- lib/fs.js | 26 ++- lib/internal/async_hooks.js | 3 +- lib/internal/bootstrap/browser.js | 162 +++++++----------- lib/internal/bootstrap/node.js | 30 ++-- .../bootstrap/switches/is_main_thread.js | 8 +- lib/internal/console/constructor.js | 8 +- lib/internal/dns/utils.js | 10 +- lib/internal/errors.js | 2 +- lib/internal/modules/cjs/loader.js | 2 +- lib/internal/modules/esm/get_format.js | 2 +- lib/internal/modules/esm/load.js | 4 +- lib/internal/modules/esm/loader.js | 33 ++-- lib/internal/modules/esm/module_map.js | 3 +- lib/internal/modules/esm/package_config.js | 2 +- lib/internal/modules/esm/translators.js | 6 +- lib/internal/modules/run_main.js | 6 +- lib/internal/process/per_thread.js | 7 +- lib/internal/process/pre_execution.js | 31 ++-- lib/internal/source_map/source_map_cache.js | 2 +- lib/internal/util.js | 87 ++++++++++ lib/util.js | 27 ++- test/common/wpt.js | 26 +++ test/parallel/test-bootstrap-modules.js | 132 ++------------ test/wpt/test-streams.js | 1 + 25 files changed, 314 insertions(+), 330 deletions(-) diff --git a/lib/buffer.js b/lib/buffer.js index 704b0369a53ffd..0c8c185cfbac7c 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -85,6 +85,7 @@ const { lazyDOMException, normalizeEncoding, kIsEncodingSymbol, + defineLazyProperties, } = require('internal/util'); const { isAnyArrayBuffer, @@ -128,15 +129,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); @@ -1382,9 +1374,6 @@ function isAscii(input) { } module.exports = { - Blob, - File, - resolveObjectURL, Buffer, SlowBuffer, transcode, @@ -1413,3 +1402,14 @@ ObjectDefineProperties(module.exports, { set(val) { INSPECT_MAX_BYTES = val; }, }, }); + +defineLazyProperties( + module.exports, + 'internal/blob', + ['Blob', 'resolveObjectURL'], +); +defineLazyProperties( + module.exports, + 'internal/file', + ['File'], +); diff --git a/lib/fs.js b/lib/fs.js index db3c202bfa6d75..8b6af16e5e56b8 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -87,6 +87,7 @@ const { custom: kCustomPromisifiedSymbol, }, SideEffectFreeRegExpPrototypeExec, + defineLazyProperties, } = require('internal/util'); const { constants: { @@ -125,11 +126,6 @@ const { validatePrimitiveStringAfterArrayBufferView, warnOnNonPortableTemplate, } = require('internal/fs/utils'); -const { - Dir, - opendir, - opendirSync, -} = require('internal/fs/dir'); const { CHAR_FORWARD_SLASH, CHAR_BACKWARD_SLASH, @@ -146,9 +142,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; @@ -392,6 +385,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 @@ -2422,12 +2416,13 @@ function watch(filename, options, listener) { if (options.recursive === undefined) options.recursive = false; if (options.recursive && !(isOSX || isWindows)) throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively'); + + const watchers = require('internal/fs/watchers'); const watcher = new watchers.FSWatcher(); watcher[watchers.kFSWatchStart](filename, options.persistent, options.recursive, options.encoding); - if (listener) { watcher.addListener('change', listener); } @@ -2486,7 +2481,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, @@ -2512,7 +2507,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); @@ -3116,8 +3111,6 @@ module.exports = fs = { mkdtempSync, open, openSync, - opendir, - opendirSync, readdir, readdirSync, read, @@ -3157,7 +3150,6 @@ module.exports = fs = { writeSync, writev, writevSync, - Dir, Dirent, Stats, @@ -3203,6 +3195,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 }, diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 9294773fe4126a..ee9094b184f039 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -8,8 +8,6 @@ const { Symbol, } = primordials; -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 @@ -382,6 +380,7 @@ function updatePromiseHookMode() { initHook = destroyTracking; } if (stopPromiseHook) stopPromiseHook(); + const promiseHooks = require('internal/promise_hooks'); stopPromiseHook = promiseHooks.createHook({ init: initHook, before: promiseBeforeHook, diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js index 5be4dd6176482c..93ae26f77a1a75 100644 --- a/lib/internal/bootstrap/browser.js +++ b/lib/internal/bootstrap/browser.js @@ -9,6 +9,9 @@ const { defineOperation, exposeInterface, lazyDOMExceptionClass, + defineLazyProperties, + defineReplaceableLazyAttribute, + exposeLazyInterfaces, } = require('internal/util'); const config = internalBinding('config'); @@ -28,37 +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); @@ -66,18 +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); -defineReplacableAttribute(globalThis, 'performance', - perf_hooks.performance); + +exposeLazyInterfaces(globalThis, 'perf_hooks', [ + 'Performance', +]); + +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 = @@ -115,67 +101,43 @@ function exposeGetterAndSetter(target, name, getter, setter = undefined) { }); } -// https://heycam.github.io/webidl/#Replaceable -function defineReplacableAttribute(target, name, value) { - ObjectDefineProperty(target, name, { - __proto__: null, - writable: true, - enumerable: true, - configurable: true, - value, - }); -} - // Web Streams API -const { - TransformStream, - TransformStreamDefaultController, -} = require('internal/webstreams/transformstream'); - -const { - WritableStream, - WritableStreamDefaultController, - WritableStreamDefaultWriter, -} = require('internal/webstreams/writablestream'); +exposeLazyInterfaces( + globalThis, + 'internal/webstreams/transformstream', + ['TransformStream', 'TransformStreamDefaultController']); -const { - ReadableStream, - ReadableStreamDefaultReader, - ReadableStreamBYOBReader, - ReadableStreamBYOBRequest, - ReadableByteStreamController, - ReadableStreamDefaultController, -} = require('internal/webstreams/readablestream'); +exposeLazyInterfaces( + globalThis, + 'internal/webstreams/writablestream', + ['WritableStream', 'WritableStreamDefaultController', 'WritableStreamDefaultWriter']); -const { - ByteLengthQueuingStrategy, - CountQueuingStrategy, -} = require('internal/webstreams/queuingstrategies'); +exposeLazyInterfaces( + globalThis, + 'internal/webstreams/readablestream', + [ + 'ReadableStream', 'ReadableStreamDefaultReader', + 'ReadableStreamBYOBReader', 'ReadableStreamBYOBRequest', + 'ReadableByteStreamController', 'ReadableStreamDefaultController', + ]); + +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', + ]); diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 07c6d5e9351a96..93009b5beccbba 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -71,7 +71,8 @@ const internalTimers = require('internal/timers'); const { defineOperation, deprecate, - exposeInterface, + defineLazyProperties, + exposeLazyInterfaces, } = require('internal/util'); const { privateSymbols: { @@ -235,20 +236,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 @@ -360,16 +364,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, diff --git a/lib/internal/bootstrap/switches/is_main_thread.js b/lib/internal/bootstrap/switches/is_main_thread.js index b2f40d9c9b264a..ace7dc1c904018 100644 --- a/lib/internal/bootstrap/switches/is_main_thread.js +++ b/lib/internal/bootstrap/switches/is_main_thread.js @@ -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; diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 21e74f336c41ac..cbc9cd8d3a531c 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -693,9 +693,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; diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js index 56b2b3930b2f6e..10395ecb89607b 100644 --- a/lib/internal/dns/utils.js +++ b/lib/internal/dns/utils.js @@ -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 }; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index fbe04a1e096c73..7bc7998f918ac6 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -205,7 +205,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; } diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 88bb870a8fd2e5..cc0e17cb488a38 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -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 { privateSymbols: { @@ -367,6 +366,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) { diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 219ef03a21214d..e9da560396da57 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -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, getLegacyExtensionFormat, @@ -89,6 +88,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) => { diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js index 71a9f8da0b49f3..d71a81a4edc811 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js @@ -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, @@ -39,6 +37,7 @@ async function getSource(url, context) { let responseURL = href; let source; if (protocol === 'file:') { + const { readFile: readFileAsync } = require('internal/fs/promises').exports; source = await readFileAsync(url); } else if (protocol === 'data:') { const match = RegExpPrototypeExec(DATA_URL_PATTERN, url.pathname); @@ -51,6 +50,7 @@ async function getSource(url, context) { protocol === 'https:' || protocol === 'http:' )) { + const { fetchModule } = require('internal/modules/esm/fetch_module'); const res = await fetchModule(url, context); source = await res.body; responseURL = res.resolvedHREF; diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 32b34ee8478e37..64d07fb1468ce5 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -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'); /** @@ -178,6 +178,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 = { /** @@ -194,7 +195,7 @@ class ESMLoader { */ load: [ { - fn: defaultLoad, + fn: require('internal/modules/esm/load').defaultLoad, url: 'node:internal/modules/esm/load', }, ], @@ -212,7 +213,7 @@ class ESMLoader { ], }; - #importMetaInitializer = initializeImportMeta; + #importMetaInitializer = require('internal/modules/esm/initialize_import_meta').initializeImportMeta; /** * Map of already-loaded CJS modules to use @@ -227,12 +228,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) { @@ -384,6 +385,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); @@ -459,7 +461,7 @@ class ESMLoader { importAssertions, }); - const translator = translators.get(finalFormat); + const translator = getTranslators().get(finalFormat); if (!translator) { throw new ERR_UNKNOWN_MODULE_FORMAT(finalFormat, responseURL); @@ -476,7 +478,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, @@ -683,6 +685,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, diff --git a/lib/internal/modules/esm/module_map.js b/lib/internal/modules/esm/module_map.js index df02ebd708517f..7280f052fef59a 100644 --- a/lib/internal/modules/esm/module_map.js +++ b/lib/internal/modules/esm/module_map.js @@ -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); diff --git a/lib/internal/modules/esm/package_config.js b/lib/internal/modules/esm/package_config.js index 1327bf1470d0eb..dc3c37f6042333 100644 --- a/lib/internal/modules/esm/package_config.js +++ b/lib/internal/modules/esm/package_config.js @@ -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 = { diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index a425749e82acd7..f42674f21f2ba0 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -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)) diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index daaa153516c424..e4b971d0c4e0a0 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -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 @@ -65,6 +62,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; diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index d3f9ff479cdeb9..22362e420b28b2 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -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) { @@ -182,6 +178,9 @@ function wrapProcessMethods(binding) { memoryUsage.rss = rss; function exit(code) { + const { + handleProcessExit, + } = require('internal/modules/esm/handle_process_exit'); process.off('exit', handleProcessExit); if (code || code === 0) diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 776be4414900da..37eff55f98175f 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -4,7 +4,6 @@ const { NumberParseInt, ObjectDefineProperties, ObjectDefineProperty, - ObjectGetOwnPropertyDescriptor, SafeMap, SafeWeakMap, StringPrototypeStartsWith, @@ -23,6 +22,8 @@ const { reconnectZeroFillToggle } = require('internal/buffer'); const { defineOperation, exposeInterface, + exposeLazyInterfaces, + defineReplaceableLazyAttribute, setupCoverageHooks, } = require('internal/util'); @@ -30,11 +31,12 @@ const { ERR_MANIFEST_ASSERT_INTEGRITY, } = 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({ @@ -277,19 +279,14 @@ function setupWebCrypto() { return; } - let webcrypto; - ObjectDefineProperty(globalThis, 'crypto', - { __proto__: null, ...ObjectGetOwnPropertyDescriptor({ - get crypto() { - webcrypto ??= require('internal/crypto/webcrypto'); - return webcrypto.crypto; - }, - }, 'crypto') }); if (internalBinding('config').hasOpenSSL) { - webcrypto ??= require('internal/crypto/webcrypto'); - 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'], + ); } } diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index f51f97d4420363..f8367608b13307 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -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); diff --git a/lib/internal/util.js b/lib/internal/util.js index 2a366f3454049b..4d72783617dd03 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -509,6 +509,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; @@ -636,9 +720,12 @@ module.exports = { createDeferredPromise, decorateErrorStack, defineOperation, + defineLazyProperties, + defineReplaceableLazyAttribute, deprecate, emitExperimentalWarning, exposeInterface, + exposeLazyInterfaces, filterDuplicateStrings, filterOwnProperties, getConstructorOf, diff --git a/lib/util.js b/lib/util.js index f71089c6b4e6dc..7fb7994e6536ba 100644 --- a/lib/util.js +++ b/lib/util.js @@ -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; @@ -385,14 +383,9 @@ module.exports = { isFunction, isPrimitive, log, - MIMEType, - MIMEParams, - parseArgs, promisify, stripVTControlCharacters, toUSVString, - TextDecoder, - TextEncoder, get transferableAbortSignal() { return lazyAbortController().transferableAbortSignal; }, @@ -404,3 +397,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'], +); diff --git a/test/common/wpt.js b/test/common/wpt.js index 048850a5be32b5..f33911ea784517 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -487,6 +487,7 @@ class WPTRunner { this.globalThisInitScripts.push( `global.Window = Object.getPrototypeOf(globalThis).constructor; self.GLOBAL.isWorker = () => false;`); + this.loadLazyGlobals(); break; } @@ -500,6 +501,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); + } + // TODO(joyeecheung): work with the upstream to port more tests in .html // to .js. async runJsTests() { diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index b68a3d3da256a8..b407760bdc9e6a 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -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,73 +17,47 @@ 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 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/dtrace', '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/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', @@ -93,15 +66,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', @@ -113,70 +77,26 @@ 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', ]); @@ -187,23 +107,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 internal/abort_controller', 'NativeModule internal/error_serdes', + 'NativeModule internal/event_target', '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', + 'NativeModule util', ].forEach(expectedModules.add.bind(expectedModules)); } @@ -217,36 +147,6 @@ 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 { internalBinding } = require('internal/test/binding'); -if (internalBinding('config').hasDtrace) { - expectedModules.add('Internal Binding dtrace'); } const difference = (setA, setB) => { diff --git a/test/wpt/test-streams.js b/test/wpt/test-streams.js index 6aa435303ab6b3..faf906efb2ef74 100644 --- a/test/wpt/test-streams.js +++ b/test/wpt/test-streams.js @@ -5,6 +5,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) { From a73d72983ba63f197458d630bdd9c83193571cd0 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 21:59:14 +0100 Subject: [PATCH 04/13] bootstrap: support module_wrap binding in snapshot PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- src/module_wrap.cc | 20 ++++++++++++++++++++ src/module_wrap.h | 2 ++ src/node_external_reference.h | 1 + 3 files changed, 23 insertions(+) diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 0645b3ddf506df..9b2b0b8334d102 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -4,6 +4,7 @@ #include "memory_tracker-inl.h" #include "node_contextify.h" #include "node_errors.h" +#include "node_external_reference.h" #include "node_internals.h" #include "node_process-inl.h" #include "node_watchdog.h" @@ -808,8 +809,27 @@ void ModuleWrap::Initialize(Local target, #undef V } +void ModuleWrap::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(New); + + registry->Register(Link); + registry->Register(Instantiate); + registry->Register(Evaluate); + registry->Register(SetSyntheticExport); + registry->Register(CreateCachedData); + registry->Register(GetNamespace); + registry->Register(GetStatus); + registry->Register(GetError); + registry->Register(GetStaticDependencySpecifiers); + + registry->Register(SetImportModuleDynamicallyCallback); + registry->Register(SetInitializeImportMetaObjectCallback); +} } // namespace loader } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL(module_wrap, node::loader::ModuleWrap::Initialize) +NODE_BINDING_EXTERNAL_REFERENCE( + module_wrap, node::loader::ModuleWrap::RegisterExternalReferences) diff --git a/src/module_wrap.h b/src/module_wrap.h index 58b233d036515c..c609ba5509dcd0 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -11,6 +11,7 @@ namespace node { class Environment; +class ExternalReferenceRegistry; namespace contextify { class ContextifyContext; @@ -44,6 +45,7 @@ class ModuleWrap : public BaseObject { v8::Local unused, v8::Local context, void* priv); + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); static void HostInitializeImportMetaObjectCallback( v8::Local context, v8::Local module, diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 954bb233e941f8..0b594967732faf 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -74,6 +74,7 @@ class ExternalReferenceRegistry { V(heap_utils) \ V(messaging) \ V(mksnapshot) \ + V(module_wrap) \ V(options) \ V(os) \ V(performance) \ From 1892b8d9970c626db7914f22eaa7f1afd7e87377 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:03:24 +0100 Subject: [PATCH 05/13] bootstrap: include event_target into the built-in snapshot Since the module has to be loaded during bootstrap anyway. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/bootstrap/browser.js | 8 +++++--- test/parallel/test-bootstrap-modules.js | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js index 93ae26f77a1a75..9e63e0679aca4c 100644 --- a/lib/internal/bootstrap/browser.js +++ b/lib/internal/bootstrap/browser.js @@ -42,9 +42,11 @@ defineOperation(globalThis, 'setTimeout', timers.setTimeout); exposeLazyInterfaces(globalThis, 'internal/abort_controller', [ 'AbortController', 'AbortSignal', ]); -exposeLazyInterfaces(globalThis, 'internal/event_target', [ - 'EventTarget', 'Event', -]); +const { + EventTarget, Event, +} = require('internal/event_target'); +exposeInterface(globalThis, 'Event', Event); +exposeInterface(globalThis, 'EventTarget', EventTarget); exposeLazyInterfaces(globalThis, 'internal/worker/io', [ 'MessageChannel', 'MessagePort', 'MessageEvent', ]); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index b407760bdc9e6a..64fb780ae67749 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -46,6 +46,7 @@ const expectedModules = new Set([ 'NativeModule internal/constants', 'NativeModule internal/dns/utils', 'NativeModule internal/errors', + 'NativeModule internal/event_target', 'NativeModule internal/fixed_queue', 'NativeModule internal/fs/utils', 'NativeModule internal/idna', @@ -90,6 +91,7 @@ const expectedModules = new Set([ 'NativeModule internal/vm', 'NativeModule internal/vm/module', 'NativeModule internal/wasm_web_api', + 'NativeModule internal/webidl', 'NativeModule internal/worker/js_transferable', 'Internal Binding blob', 'NativeModule async_hooks', @@ -99,6 +101,7 @@ const expectedModules = new Set([ 'NativeModule url', 'NativeModule internal/v8/startup_snapshot', 'NativeModule vm', + 'NativeModule util', ]); if (!common.isMainThread) { From 4a6a4736d4a25c80e203f319094538748e13ecbb Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:24:28 +0100 Subject: [PATCH 06/13] modules: move modules/cjs/helpers.js to modules/helpers.js The helpers are actually shared by the two loaders, so move them under modules/ directly. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/main/eval_string.js | 2 +- lib/internal/modules/cjs/loader.js | 2 +- lib/internal/modules/esm/translators.js | 2 +- lib/internal/modules/{cjs => }/helpers.js | 0 lib/internal/source_map/source_map_cache.js | 2 +- lib/internal/util/inspector.js | 2 +- lib/repl.js | 2 +- test/parallel/test-bootstrap-modules.js | 2 +- test/parallel/test-util-inspect.js | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename lib/internal/modules/{cjs => }/helpers.js (100%) diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js index cb5b758ad4152e..2b9c99e1944fdc 100644 --- a/lib/internal/main/eval_string.js +++ b/lib/internal/main/eval_string.js @@ -12,7 +12,7 @@ const { markBootstrapComplete, } = require('internal/process/pre_execution'); const { evalModule, evalScript } = require('internal/process/execution'); -const { addBuiltinLibsToObject } = require('internal/modules/cjs/helpers'); +const { addBuiltinLibsToObject } = require('internal/modules/helpers'); const { getOptionValue } = require('internal/options'); diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index cc0e17cb488a38..1f1b8ee3e46bb9 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -108,7 +108,7 @@ const { makeRequireFunction, normalizeReferrerURL, stripBOM, -} = require('internal/modules/cjs/helpers'); +} = require('internal/modules/helpers'); const { getOptionValue } = require('internal/options'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index f42674f21f2ba0..eb03858c586c31 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -30,7 +30,7 @@ const { hasEsmSyntax, loadBuiltinModule, stripBOM, -} = require('internal/modules/cjs/helpers'); +} = require('internal/modules/helpers'); const { Module: CJSModule, cjsParseCache, diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/helpers.js similarity index 100% rename from lib/internal/modules/cjs/helpers.js rename to lib/internal/modules/helpers.js diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index f8367608b13307..32dbe249c8f05e 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -27,7 +27,7 @@ const { getOptionValue } = require('internal/options'); const { IterableWeakMap } = require('internal/util/iterable_weak_map'); const { normalizeReferrerURL, -} = require('internal/modules/cjs/helpers'); +} = require('internal/modules/helpers'); const { validateBoolean } = require('internal/validators'); const { setMaybeCacheGeneratedSourceMap } = internalBinding('errors'); diff --git a/lib/internal/util/inspector.js b/lib/internal/util/inspector.js index f5cba165fc0f32..0d9580c83224e4 100644 --- a/lib/internal/util/inspector.js +++ b/lib/internal/util/inspector.js @@ -66,7 +66,7 @@ function installConsoleExtensions(commandLineApi) { if (commandLineApi.require) { return; } const { tryGetCwd } = require('internal/process/execution'); const CJSModule = require('internal/modules/cjs/loader').Module; - const { makeRequireFunction } = require('internal/modules/cjs/helpers'); + const { makeRequireFunction } = require('internal/modules/helpers'); const consoleAPIModule = new CJSModule(''); const cwd = tryGetCwd(); consoleAPIModule.paths = []; diff --git a/lib/repl.js b/lib/repl.js index 8109595b119a8b..94ce92c2d045ba 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -101,7 +101,7 @@ const { BuiltinModule } = require('internal/bootstrap/loaders'); const { makeRequireFunction, addBuiltinLibsToObject, -} = require('internal/modules/cjs/helpers'); +} = require('internal/modules/helpers'); const { isIdentifierStart, isIdentifierChar, diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 64fb780ae67749..8a0e49e072fea8 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -51,7 +51,7 @@ const expectedModules = new Set([ 'NativeModule internal/fs/utils', 'NativeModule internal/idna', 'NativeModule internal/linkedlist', - 'NativeModule internal/modules/cjs/helpers', + 'NativeModule internal/modules/helpers', 'NativeModule internal/modules/cjs/loader', 'NativeModule internal/modules/esm/assert', 'NativeModule internal/modules/esm/formats', diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 9b57fd4a99b10e..b64d6531abd02c 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -2829,7 +2829,7 @@ assert.strictEqual( ' at Function.Module._load (node:internal/modules/cjs/loader:621:3)', // This file is not an actual Node.js core file. ' at Module.require [as weird/name] (node:internal/aaaaa/loader:735:19)', - ' at require (node:internal/modules/cjs/helpers:14:16)', + ' at require (node:internal/modules/helpers:14:16)', ' at Array.forEach ()', ` at ${process.cwd()}/test/parallel/test-util-inspect.js:2760:12`, ` at Object. (${process.cwd()}/node_modules/hyper_module/folder/file.js:2753:10)`, From 92c4600d07ee71228accb4cc2e3768df46293547 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 14 Dec 2022 00:02:51 +0100 Subject: [PATCH 07/13] lib: add getLazy() method to internal/util This patch adds a getLazy() method to facilitate initialize-once lazy loading in the internals. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/util.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/internal/util.js b/lib/internal/util.js index 4d72783617dd03..be4e60051d8319 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -691,6 +691,24 @@ function isArrayBufferDetached(value) { return false; } +/** + * Helper function to lazy-load an initialize-once value. + * @template T Return value of initializer + * @param {()=>T} initializer Initializer of the lazily loaded value. + * @returns {()=>T} + */ +function getLazy(initializer) { + let value; + let initialized = false; + return function() { + if (initialized === false) { + value = initializer(); + initialized = true; + } + return value; + }; +} + // Setup user-facing NODE_V8_COVERAGE environment variable that writes // ScriptCoverage objects to a specified directory. function setupCoverageHooks(dir) { @@ -713,6 +731,7 @@ function setupCoverageHooks(dir) { } module.exports = { + getLazy, assertCrypto, cachedResult, convertToValidSignal, From 9a9a5b543e7c1f9bd6d4f5aef06723a3a86b252a Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:32:02 +0100 Subject: [PATCH 08/13] lib: lazy-load deps in source_map_cache.js So that the file can be snapshotted. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/source_map/source_map_cache.js | 20 ++++++++++++-------- test/parallel/test-bootstrap-modules.js | 1 - 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 32dbe249c8f05e..d86f43290abd61 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -24,17 +24,19 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { debug = fn; }); const { getOptionValue } = require('internal/options'); -const { IterableWeakMap } = require('internal/util/iterable_weak_map'); -const { - normalizeReferrerURL, -} = require('internal/modules/helpers'); + const { validateBoolean } = require('internal/validators'); const { setMaybeCacheGeneratedSourceMap } = internalBinding('errors'); +const { getLazy } = require('internal/util'); // Since the CJS module cache is mutable, which leads to memory leaks when // modules are deleted, we use a WeakMap so that the source map cache will // be purged automatically: -const cjsSourceMapCache = new IterableWeakMap(); +const getCjsSourceMapCache = getLazy(() => { + const { IterableWeakMap } = require('internal/util/iterable_weak_map'); + return new IterableWeakMap(); +}); + // The esm cache is not mutable, so we can use a Map without memory concerns: const esmSourceMapCache = new SafeMap(); // The generated sources is not mutable, so we can use a Map without memory concerns: @@ -44,6 +46,7 @@ const kSourceMappingURLMagicComment = /\/[*/]#\s+sourceMappingURL=(?[^\s]+)/g; const { fileURLToPath, pathToFileURL, URL } = require('internal/url'); + let SourceMap; let sourceMapsEnabled; @@ -114,6 +117,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSo const sourceMapsEnabled = getSourceMapsEnabled(); if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; try { + const { normalizeReferrerURL } = require('internal/modules/helpers'); filename = normalizeReferrerURL(filename); } catch (err) { // This is most likely an invalid filename in sourceURL of [eval]-wrapper. @@ -137,7 +141,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSo const data = dataFromUrl(filename, sourceMapURL); const url = data ? null : sourceMapURL; if (cjsModuleInstance) { - cjsSourceMapCache.set(cjsModuleInstance, { + getCjsSourceMapCache().set(cjsModuleInstance, { filename, lineLengths: lineLengths(content), data, @@ -291,7 +295,7 @@ function sourceMapCacheToObject() { } function appendCJSCache(obj) { - for (const value of cjsSourceMapCache) { + for (const value of getCjsSourceMapCache()) { obj[ObjectGetValueSafe(value, 'filename')] = { lineLengths: ObjectGetValueSafe(value, 'lineLengths'), data: ObjectGetValueSafe(value, 'data'), @@ -309,7 +313,7 @@ function findSourceMap(sourceURL) { } let sourceMap = esmSourceMapCache.get(sourceURL) ?? generatedSourceMapCache.get(sourceURL); if (sourceMap === undefined) { - for (const value of cjsSourceMapCache) { + for (const value of getCjsSourceMapCache()) { const filename = ObjectGetValueSafe(value, 'filename'); const cachedSourceURL = ObjectGetValueSafe(value, 'sourceURL'); if (sourceURL === filename || sourceURL === cachedSourceURL) { diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 8a0e49e072fea8..04a273b643ef1a 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -85,7 +85,6 @@ const expectedModules = new Set([ 'NativeModule internal/util', 'NativeModule internal/util/debuglog', 'NativeModule internal/util/inspect', - 'NativeModule internal/util/iterable_weak_map', 'NativeModule internal/util/types', 'NativeModule internal/validators', 'NativeModule internal/vm', From ce5f49ebaee1316801d14b544405245665485618 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:35:42 +0100 Subject: [PATCH 09/13] lib: lazy-load deps in modules/run_main.js So that the file can be snapshotted PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/modules/run_main.js | 6 ++++-- test/fixtures/errors/force_colors.snapshot | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index e4b971d0c4e0a0..42a0b6af0626ec 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -4,8 +4,7 @@ const { ObjectCreate, StringPrototypeEndsWith, } = primordials; -const CJSLoader = require('internal/modules/cjs/loader'); -const { Module, toRealPath, readPackageScope } = CJSLoader; + const { getOptionValue } = require('internal/options'); const path = require('path'); @@ -13,6 +12,7 @@ function resolveMainPath(main) { // Note extension resolution for the main entry point can be deprecated in a // future major. // Module._findPath is monkey-patchable here. + const { Module, toRealPath } = require('internal/modules/cjs/loader'); let mainPath = Module._findPath(path.resolve(main), null, true); if (!mainPath) return; @@ -41,6 +41,7 @@ function shouldUseESMLoader(mainPath) { getOptionValue('--experimental-specifier-resolution'); if (esModuleSpecifierResolution === 'node') return true; + const { readPackageScope } = require('internal/modules/cjs/loader'); // Determine the module format of the main if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) return true; @@ -83,6 +84,7 @@ function executeUserEntryPoint(main = process.argv[1]) { runMainESM(resolvedMain || main); } else { // Module._load is the monkey-patchable CJS module loader. + const { Module } = require('internal/modules/cjs/loader'); Module._load(main, null, true); } } diff --git a/test/fixtures/errors/force_colors.snapshot b/test/fixtures/errors/force_colors.snapshot index 4c33acbc2d5c12..acd77450f02c2f 100644 --- a/test/fixtures/errors/force_colors.snapshot +++ b/test/fixtures/errors/force_colors.snapshot @@ -8,7 +8,7 @@ Error: Should include grayed stack trace  at Module._extensions..js (node:internal*modules*cjs*loader:1310:10)  at Module.load (node:internal*modules*cjs*loader:1119:32)  at Module._load (node:internal*modules*cjs*loader:960:12) - at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:86:12) + at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:88:12)  at node:internal*main*run_main_module:23:47 Node.js * From 9ea0ae423fcd4c3a95f9957a5af78689f47f2fd0 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:58:06 +0100 Subject: [PATCH 10/13] 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 Reviewed-By: Chengzhong Wu --- .../modules/esm/create_dynamic_module.js | 6 ++-- lib/internal/modules/esm/loader.js | 12 ++++--- lib/internal/modules/esm/resolve.js | 32 +------------------ lib/internal/modules/esm/translators.js | 3 +- lib/internal/process/esm_loader.js | 29 ----------------- lib/internal/process/pre_execution.js | 15 ++------- lib/internal/vm.js | 7 ++-- lib/internal/vm/module.js | 4 +-- lib/vm.js | 7 ++-- test/parallel/test-bootstrap-modules.js | 1 + 10 files changed, 25 insertions(+), 91 deletions(-) diff --git a/lib/internal/modules/esm/create_dynamic_module.js b/lib/internal/modules/esm/create_dynamic_module.js index c99da19d5c8271..32f1c82a7a20c2 100644 --- a/lib/internal/modules/esm/create_dynamic_module.js +++ b/lib/internal/modules/esm/create_dynamic_module.js @@ -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) diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 64d07fb1468ce5..a367fa415a38f5 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -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; @@ -375,9 +378,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); }, @@ -802,7 +806,7 @@ class ESMLoader { } const chain = this.#hooks.resolve; const context = { - conditions: DEFAULT_CONDITIONS, + conditions: getDefaultConditions(), importAssertions, parentURL, }; diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 7006887a6fe2ba..0dc8711643c6f0 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -6,7 +6,6 @@ const { ArrayPrototypeShift, JSONParse, JSONStringify, - ObjectFreeze, ObjectGetOwnPropertyNames, ObjectPrototypeHasOwnProperty, RegExp, @@ -46,7 +45,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, @@ -61,25 +59,13 @@ const { const { Module: CJSModule } = require('internal/modules/cjs/loader'); const packageJsonReader = require('internal/modules/package_json_reader'); 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) { @@ -150,21 +136,6 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) { } } -/** - * @param {string[]} [conditions] - * @returns {Set} - */ -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(); /** @@ -1167,7 +1138,6 @@ async function defaultResolve(specifier, context = {}) { } module.exports = { - DEFAULT_CONDITIONS, defaultResolve, encodedSepRegEx, getPackageScopeConfig, diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index eb03858c586c31..e185e29ad046f3 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -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, }); diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index bb621d2aa255c3..70f48c2f9ac965 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -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; diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 37eff55f98175f..3f42b8f4bef9ac 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -5,7 +5,6 @@ const { ObjectDefineProperties, ObjectDefineProperty, SafeMap, - SafeWeakMap, StringPrototypeStartsWith, Symbol, SymbolDispose, @@ -551,20 +550,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. diff --git a/lib/internal/vm.js b/lib/internal/vm.js index 4f87ce87cc29a2..b14ba13e7e4cfb 100644 --- a/lib/internal/vm.js +++ b/lib/internal/vm.js @@ -97,12 +97,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), }); } diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 109b2d7e5b650c..ec9618139b5dc2 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -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) : diff --git a/lib/vm.js b/lib/vm.js index 21acc55e2eff0a..b48e79c282541b 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -105,10 +105,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), }); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 04a273b643ef1a..d99372aa482433 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -63,6 +63,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', From 5060fd2cc4420873057700a297a7aad1c20623e3 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 23:51:05 +0100 Subject: [PATCH 11/13] bootstrap: make CJS loader snapshotable This patch makes the top-level access to runtime states in the CJS loader lazy, and move the side-effects into a initializeCJS() function that gets called during pre-execution. As a result the CJS loader can be included into the built-in snapshot. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- lib/internal/modules/cjs/loader.js | 191 ++++++++++-------- lib/internal/modules/helpers.js | 34 ++-- lib/internal/process/pre_execution.js | 9 +- test/fixtures/errors/force_colors.snapshot | 8 +- ...ction.js => inspector-global-function.mjs} | 0 test/parallel/test-bootstrap-modules.js | 19 +- .../test-inspector-break-when-eval.js | 7 +- 7 files changed, 144 insertions(+), 124 deletions(-) rename test/fixtures/{inspector-global-function.js => inspector-global-function.mjs} (100%) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 1f1b8ee3e46bb9..c037d169585198 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -73,6 +73,7 @@ const cjsParseCache = new SafeWeakMap(); module.exports = { wrapSafe, Module, toRealPath, readPackageScope, cjsParseCache, get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; }, + initializeCJS, }; const { BuiltinModule } = require('internal/bootstrap/loaders'); @@ -86,8 +87,8 @@ const { kEmptyObject, filterOwnProperties, setOwnProperty, + getLazy, } = require('internal/util'); -const { Script } = require('vm'); const { internalCompileFunction } = require('internal/vm'); const assert = require('internal/assert'); const fs = require('fs'); @@ -102,21 +103,24 @@ const { }, } = internalBinding('util'); const { - cjsConditions, + getCjsConditions, + initializeCjsConditions, hasEsmSyntax, loadBuiltinModule, makeRequireFunction, normalizeReferrerURL, stripBOM, } = require('internal/modules/helpers'); -const { getOptionValue } = require('internal/options'); -const preserveSymlinks = getOptionValue('--preserve-symlinks'); -const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); -const shouldReportRequiredModules = process.env.WATCH_REPORT_DEPENDENCIES; -// Do not eagerly grab .manifest, it may be in TDZ -const policy = getOptionValue('--experimental-policy') ? - require('internal/process/policy') : - null; +const packageJsonReader = require('internal/modules/package_json_reader'); +const { getOptionValue, getEmbedderOptions } = require('internal/options'); +const policy = getLazy( + () => (getOptionValue('--experimental-policy') ? require('internal/process/policy') : null), +); +const shouldReportRequiredModules = getLazy(() => process.env.WATCH_REPORT_DEPENDENCIES); + +const getCascadedLoader = getLazy( + () => require('internal/process/esm_loader').esmLoader, +); // Whether any user-provided CJS modules had been loaded (executed). // Used for internal assertions. @@ -132,7 +136,6 @@ const { setArrowMessage, } = require('internal/errors'); const { validateString } = require('internal/validators'); -const pendingDeprecation = getOptionValue('--pending-deprecation'); const { CHAR_BACKWARD_SLASH, @@ -145,15 +148,7 @@ const { isProxy, } = require('internal/util/types'); -const asyncESM = require('internal/process/esm_loader'); -const { enrichCJSError } = require('internal/modules/esm/translators'); const { kEvaluated } = internalBinding('module_wrap'); -const { - encodedSepRegEx, - packageExportsResolve, - packageImportsResolve, -} = require('internal/modules/esm/resolve'); - const isWindows = process.platform === 'win32'; const relativeResolveCache = ObjectCreate(null); @@ -209,13 +204,13 @@ function updateChildren(parent, child, scan) { } function reportModuleToWatchMode(filename) { - if (shouldReportRequiredModules && process.send) { + if (shouldReportRequiredModules() && process.send) { process.send({ 'watch:require': [filename] }); } } function reportModuleNotFoundToWatchMode(basePath, extensions) { - if (shouldReportRequiredModules && process.send) { + if (shouldReportRequiredModules() && process.send) { process.send({ 'watch:require': ArrayPrototypeMap(extensions, (ext) => path.resolve(`${basePath}${ext}`)) }); } } @@ -231,9 +226,10 @@ function Module(id = '', parent) { this.loaded = false; this.children = []; let redirects; - if (policy?.manifest) { + const manifest = policy()?.manifest; + if (manifest) { const moduleURL = pathToFileURL(id); - redirects = policy.manifest.getDependencyMapper(moduleURL); + redirects = manifest.getDependencyMapper(moduleURL); // TODO(rafaelgss): remove the necessity of this branch setOwnProperty(this, 'require', makeRequireFunction(this, redirects)); // eslint-disable-next-line no-proto @@ -242,22 +238,6 @@ function Module(id = '', parent) { this[require_private_symbol] = internalRequire; } -const builtinModules = []; -for (const { 0: id, 1: mod } of BuiltinModule.map) { - if (mod.canBeRequiredByUsers && - BuiltinModule.canBeRequiredWithoutScheme(id)) { - ArrayPrototypePush(builtinModules, id); - } -} - -const allBuiltins = new SafeSet( - ArrayPrototypeFlatMap(builtinModules, (bm) => [bm, `node:${bm}`]), -); -BuiltinModule.getSchemeOnlyModuleNames().forEach((builtin) => allBuiltins.add(`node:${builtin}`)); - -ObjectFreeze(builtinModules); -Module.builtinModules = builtinModules; - Module._cache = ObjectCreate(null); Module._pathCache = ObjectCreate(null); Module._extensions = ObjectCreate(null); @@ -326,26 +306,59 @@ function setModuleParent(value) { moduleParentCache.set(this, value); } -ObjectDefineProperty(Module.prototype, 'parent', { - __proto__: null, - get: pendingDeprecation ? deprecate( - getModuleParent, - 'module.parent is deprecated due to accuracy issues. Please use ' + - 'require.main to find program entry point instead.', - 'DEP0144', - ) : getModuleParent, - set: pendingDeprecation ? deprecate( - setModuleParent, - 'module.parent is deprecated due to accuracy issues. Please use ' + - 'require.main to find program entry point instead.', - 'DEP0144', - ) : setModuleParent, -}); - let debug = require('internal/util/debuglog').debuglog('module', (fn) => { debug = fn; }); -Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); + +const builtinModules = []; +// This function is called during pre-execution, before any user code is run. +function initializeCJS() { + const pendingDeprecation = getOptionValue('--pending-deprecation'); + ObjectDefineProperty(Module.prototype, 'parent', { + __proto__: null, + get: pendingDeprecation ? deprecate( + getModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144', + ) : getModuleParent, + set: pendingDeprecation ? deprecate( + setModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144', + ) : setModuleParent, + }); + Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); + + for (const { 0: id, 1: mod } of BuiltinModule.map) { + if (mod.canBeRequiredByUsers && + BuiltinModule.canBeRequiredWithoutScheme(id)) { + ArrayPrototypePush(builtinModules, id); + } + } + + const allBuiltins = new SafeSet( + ArrayPrototypeFlatMap(builtinModules, (bm) => [bm, `node:${bm}`]), + ); + BuiltinModule.getSchemeOnlyModuleNames().forEach((builtin) => allBuiltins.add(`node:${builtin}`)); + ObjectFreeze(builtinModules); + Module.builtinModules = builtinModules; + + Module.isBuiltin = function isBuiltin(moduleName) { + return allBuiltins.has(moduleName); + }; + + initializeCjsConditions(); + + if (!getEmbedderOptions().noGlobalSearchPaths) { + Module._initPaths(); + } + + // TODO(joyeecheung): deprecate this in favor of a proper hook? + Module.runMain = + require('internal/modules/run_main').executeUserEntryPoint; +} // Given a module name, and a list of paths to test, returns the first // matching file in the following precedence. @@ -366,7 +379,6 @@ 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) { @@ -469,7 +481,7 @@ const realpathCache = new SafeMap(); function tryFile(requestPath, isMain) { const rc = _stat(requestPath); if (rc !== 0) return; - if (preserveSymlinks && !isMain) { + if (getOptionValue('--preserve-symlinks') && !isMain) { return path.resolve(requestPath); } return toRealPath(requestPath); @@ -540,9 +552,10 @@ function trySelf(parentPath, request) { } try { + const { packageExportsResolve } = require('internal/modules/esm/resolve'); return finalizeEsmResolution(packageExportsResolve( pathToFileURL(pkgPath + '/package.json'), expansion, pkg, - pathToFileURL(parentPath), cjsConditions), parentPath, pkgPath); + pathToFileURL(parentPath), getCjsConditions()), parentPath, pkgPath); } catch (e) { if (e.code === 'ERR_MODULE_NOT_FOUND') throw createEsmNotFoundErr(request, pkgPath + '/package.json'); @@ -564,9 +577,10 @@ function resolveExports(nmPath, request) { const pkg = _readPackage(pkgPath); if (pkg?.exports != null) { try { + const { packageExportsResolve } = require('internal/modules/esm/resolve'); return finalizeEsmResolution(packageExportsResolve( pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, - cjsConditions), null, pkgPath); + getCjsConditions()), null, pkgPath); } catch (e) { if (e.code === 'ERR_MODULE_NOT_FOUND') throw createEsmNotFoundErr(request, pkgPath + '/package.json'); @@ -645,19 +659,19 @@ Module._findPath = function(request, paths, isMain) { if (!trailingSlash) { if (rc === 0) { // File. if (!isMain) { - if (preserveSymlinks) { + if (getOptionValue('--preserve-symlinks')) { filename = path.resolve(basePath); } else { filename = toRealPath(basePath); } - } else if (preserveSymlinksMain) { - // For the main module, we use the preserveSymlinksMain flag instead + } else if (getOptionValue('--preserve-symlinks-main')) { + // For the main module, we use the --preserve-symlinks-main flag instead // mainly for backward compatibility, as the preserveSymlinks flag // historically has not applied to the main module. Most likely this // was intended to keep .bin/ binaries working, as following those // symlinks is usually required for the imports in the corresponding // files to resolve; that said, in some use cases following symlinks - // causes bigger problems which is why the preserveSymlinksMain option + // causes bigger problems which is why the --preserve-symlinks-main option // is needed. filename = path.resolve(basePath); } else { @@ -1037,9 +1051,10 @@ Module._resolveFilename = function(request, parent, isMain, options) { const pkg = readPackageScope(parentPath) || {}; if (pkg.data?.imports != null) { try { + const { packageImportsResolve } = require('internal/modules/esm/resolve'); return finalizeEsmResolution( packageImportsResolve(request, pathToFileURL(parentPath), - cjsConditions), parentPath, + getCjsConditions()), parentPath, pkg.path); } catch (e) { if (e.code === 'ERR_MODULE_NOT_FOUND') @@ -1081,6 +1096,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { }; function finalizeEsmResolution(resolved, parentPath, pkgPath) { + const { encodedSepRegEx } = require('internal/modules/esm/resolve'); if (RegExpPrototypeExec(encodedSepRegEx, resolved) !== null) throw new ERR_INVALID_MODULE_SPECIFIER( resolved, 'must not include encoded "/" or "\\" characters', parentPath); @@ -1119,14 +1135,14 @@ Module.prototype.load = function(filename) { Module._extensions[extension](this, filename); this.loaded = true; - const esmLoader = asyncESM.esmLoader; + const cascadedLoader = getCascadedLoader(); // Create module entry at load time to snapshot exports correctly const exports = this.exports; // Preemptively cache if ((module?.module === undefined || module.module.getStatus() < kEvaluated) && - !esmLoader.cjsCache.has(this)) - esmLoader.cjsCache.set(this, exports); + !cascadedLoader.cjsCache.has(this)) + cascadedLoader.cjsCache.set(this, exports); }; // Loads a module at the given file path. Returns that module's @@ -1150,17 +1166,20 @@ Module.prototype.require = function(id) { // (needed for setting breakpoint when called with --inspect-brk) let resolvedArgv; let hasPausedEntry = false; - +let Script; function wrapSafe(filename, content, cjsModuleInstance) { if (patched) { const wrapper = Module.wrap(content); + if (Script === undefined) { + ({ Script } = require('vm')); + } const script = new Script(wrapper, { filename, lineOffset: 0, importModuleDynamically: async (specifier, _, importAssertions) => { - const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename), - importAssertions); + const cascadedLoader = getCascadedLoader(); + return cascadedLoader.import(specifier, normalizeReferrerURL(filename), + importAssertions); }, }); @@ -1184,9 +1203,9 @@ function wrapSafe(filename, content, cjsModuleInstance) { ], { filename, importModuleDynamically(specifier, _, importAssertions) { - const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename), - importAssertions); + const cascadedLoader = getCascadedLoader(); + return cascadedLoader.import(specifier, normalizeReferrerURL(filename), + importAssertions); }, }); @@ -1197,8 +1216,10 @@ function wrapSafe(filename, content, cjsModuleInstance) { return result.function; } catch (err) { - if (process.mainModule === cjsModuleInstance) + if (process.mainModule === cjsModuleInstance) { + const { enrichCJSError } = require('internal/modules/esm/translators'); enrichCJSError(err, content); + } throw err; } } @@ -1210,7 +1231,7 @@ function wrapSafe(filename, content, cjsModuleInstance) { Module.prototype._compile = function(content, filename) { let moduleURL; let redirects; - const manifest = policy?.manifest; + const manifest = policy()?.manifest; if (manifest) { moduleURL = pathToFileURL(filename); redirects = manifest.getDependencyMapper(moduleURL); @@ -1315,9 +1336,10 @@ Module._extensions['.js'] = function(module, filename) { Module._extensions['.json'] = function(module, filename) { const content = fs.readFileSync(filename, 'utf8'); - if (policy?.manifest) { + const manifest = policy()?.manifest; + if (manifest) { const moduleURL = pathToFileURL(filename); - policy.manifest.assertIntegrity(moduleURL, content); + manifest.assertIntegrity(moduleURL, content); } try { @@ -1331,10 +1353,11 @@ Module._extensions['.json'] = function(module, filename) { // Native extension for .node Module._extensions['.node'] = function(module, filename) { - if (policy?.manifest) { + const manifest = policy()?.manifest; + if (manifest) { const content = fs.readFileSync(filename); const moduleURL = pathToFileURL(filename); - policy.manifest.assertIntegrity(moduleURL, content); + manifest.assertIntegrity(moduleURL, content); } // Be aware this doesn't use `content` return process.dlopen(module, path.toNamespacedPath(filename)); @@ -1443,14 +1466,10 @@ Module.syncBuiltinESMExports = function syncBuiltinESMExports() { } }; -Module.isBuiltin = function isBuiltin(moduleName) { - return allBuiltins.has(moduleName); -}; - ObjectDefineProperty(Module.prototype, 'constructor', { __proto__: null, get: function() { - return policy ? undefined : Module; + return policy() ? undefined : Module; }, configurable: false, enumerable: false, diff --git a/lib/internal/modules/helpers.js b/lib/internal/modules/helpers.js index 00d2b628c44172..05619d3ad54f24 100644 --- a/lib/internal/modules/helpers.js +++ b/lib/internal/modules/helpers.js @@ -26,7 +26,6 @@ const { pathToFileURL, fileURLToPath, URL } = require('internal/url'); const { getOptionValue } = require('internal/options'); const { setOwnProperty } = require('internal/util'); -const userConditions = getOptionValue('--conditions'); const { privateSymbols: { @@ -38,16 +37,26 @@ let debug = require('internal/util/debuglog').debuglog('module', (fn) => { debug = fn; }); -const noAddons = getOptionValue('--no-addons'); -const addonConditions = noAddons ? [] : ['node-addons']; +let cjsConditions; +function initializeCjsConditions() { + const userConditions = getOptionValue('--conditions'); + const noAddons = getOptionValue('--no-addons'); + const addonConditions = noAddons ? [] : ['node-addons']; + // TODO: Use this set when resolving pkg#exports conditions in loader.js. + cjsConditions = new SafeSet([ + 'require', + 'node', + ...addonConditions, + ...userConditions, + ]); +} -// TODO: Use this set when resolving pkg#exports conditions in loader.js. -const cjsConditions = new SafeSet([ - 'require', - 'node', - ...addonConditions, - ...userConditions, -]); +function getCjsConditions() { + if (cjsConditions === undefined) { + initializeCjsConditions(); + } + return cjsConditions; +} function loadBuiltinModule(filename, request) { const mod = BuiltinModule.map.get(filename); @@ -79,7 +88,7 @@ function makeRequireFunction(mod, redirects) { let require; if (redirects) { const id = mod.filename || mod.id; - const conditions = cjsConditions; + const conditions = getCjsConditions(); const { resolve, reaction } = redirects; require = function require(specifier) { let missing = true; @@ -247,7 +256,8 @@ function hasEsmSyntax(code) { module.exports = { addBuiltinLibsToObject, - cjsConditions, + getCjsConditions, + initializeCjsConditions, hasEsmSyntax, loadBuiltinModule, makeRequireFunction, diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 3f42b8f4bef9ac..277737be590c61 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -540,13 +540,8 @@ function readPolicyFromDisk() { } function initializeCJSLoader() { - const CJSLoader = require('internal/modules/cjs/loader'); - if (!getEmbedderOptions().noGlobalSearchPaths) { - CJSLoader.Module._initPaths(); - } - // TODO(joyeecheung): deprecate this in favor of a proper hook? - CJSLoader.Module.runMain = - require('internal/modules/run_main').executeUserEntryPoint; + const { initializeCJS } = require('internal/modules/cjs/loader'); + initializeCJS(); } function initializeESMLoader() { diff --git a/test/fixtures/errors/force_colors.snapshot b/test/fixtures/errors/force_colors.snapshot index acd77450f02c2f..a360fd0a90497b 100644 --- a/test/fixtures/errors/force_colors.snapshot +++ b/test/fixtures/errors/force_colors.snapshot @@ -4,10 +4,10 @@ throw new Error('Should include grayed stack trace') Error: Should include grayed stack trace at Object. (/test*force_colors.js:1:7) - at Module._compile (node:internal*modules*cjs*loader:1256:14) - at Module._extensions..js (node:internal*modules*cjs*loader:1310:10) - at Module.load (node:internal*modules*cjs*loader:1119:32) - at Module._load (node:internal*modules*cjs*loader:960:12) + at Module._compile (node:internal*modules*cjs*loader:1277:14) + at Module._extensions..js (node:internal*modules*cjs*loader:1331:10) + at Module.load (node:internal*modules*cjs*loader:1135:32) + at Module._load (node:internal*modules*cjs*loader:974:12)  at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:88:12)  at node:internal*main*run_main_module:23:47 diff --git a/test/fixtures/inspector-global-function.js b/test/fixtures/inspector-global-function.mjs similarity index 100% rename from test/fixtures/inspector-global-function.js rename to test/fixtures/inspector-global-function.mjs diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index d99372aa482433..e1c173370d14cf 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -51,26 +51,15 @@ const expectedModules = new Set([ 'NativeModule internal/fs/utils', 'NativeModule internal/idna', 'NativeModule internal/linkedlist', - 'NativeModule internal/modules/helpers', 'NativeModule internal/modules/cjs/loader', - 'NativeModule internal/modules/esm/assert', - 'NativeModule internal/modules/esm/formats', - 'NativeModule internal/modules/esm/get_format', - 'NativeModule internal/modules/esm/initialize_import_meta', - 'NativeModule internal/modules/esm/load', - 'NativeModule internal/modules/esm/loader', - 'NativeModule internal/modules/esm/module_map', - 'NativeModule internal/modules/esm/package_config', - 'NativeModule internal/modules/esm/resolve', - 'NativeModule internal/modules/esm/translators', 'NativeModule internal/modules/esm/utils', + 'NativeModule internal/modules/helpers', 'NativeModule internal/modules/package_json_reader', 'NativeModule internal/modules/run_main', 'NativeModule internal/net', 'NativeModule internal/options', 'NativeModule internal/perf/utils', 'NativeModule internal/priority_queue', - 'NativeModule internal/process/esm_loader', 'NativeModule internal/process/execution', 'NativeModule internal/process/per_thread', 'NativeModule internal/process/pre_execution', @@ -100,7 +89,6 @@ const expectedModules = new Set([ 'NativeModule timers', 'NativeModule url', 'NativeModule internal/v8/startup_snapshot', - 'NativeModule vm', 'NativeModule util', ]); @@ -140,6 +128,11 @@ if (!common.isMainThread) { ].forEach(expectedModules.add.bind(expectedModules)); } +if (common.isWindows) { + // On Windows fs needs SideEffectFreeRegExpPrototypeExec which uses vm. + expectedModules.add('NativeModule vm'); +} + if (common.hasIntl) { expectedModules.add('Internal Binding icu'); } else { diff --git a/test/parallel/test-inspector-break-when-eval.js b/test/parallel/test-inspector-break-when-eval.js index 1e7ab513dadbbb..bd9969e0dcfffd 100644 --- a/test/parallel/test-inspector-break-when-eval.js +++ b/test/parallel/test-inspector-break-when-eval.js @@ -6,7 +6,10 @@ const { NodeInstance } = require('../common/inspector-helper.js'); const fixtures = require('../common/fixtures'); const { pathToFileURL } = require('url'); -const script = fixtures.path('inspector-global-function.js'); +// This needs to be an ES module file to ensure that internal modules are +// loaded before pausing. See +// https://bugs.chromium.org/p/chromium/issues/detail?id=1246905 +const script = fixtures.path('inspector-global-function.mjs'); async function setupDebugger(session) { console.log('[test]', 'Setting up a debugger'); @@ -23,7 +26,7 @@ async function setupDebugger(session) { // NOTE(mmarchini): We wait for the second console.log to ensure we loaded // every internal module before pausing. See - // https://bugs.chromium.org/p/v8/issues/detail?id=10287. + // https://bugs.chromium.org/p/chromium/issues/detail?id=1246905 const waitForReady = session.waitForConsoleOutput('log', 'Ready!'); session.send({ 'method': 'Debugger.resume' }); await waitForReady; From 6c1abed521f2347b500adece754ee79e3204336b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 13 Dec 2022 22:14:59 +0100 Subject: [PATCH 12/13] bootstrap: optimize modules loaded in the built-in snapshot Preload essential modules and lazy-load non-essential ones. After this patch, all modules listed by running this snippet: ``` const list = process.moduleLoadList.join('\n'); require('fs').writeSync(1, list, 'utf-8'); ``` (which is roughly the same list as the one in test-bootstrap-module.js for the main thread) are loaded from the snapshot so no additional compilation cost is incurred. PR-URL: https://github.com/nodejs/node/pull/45849 Reviewed-By: Geoffrey Booth Reviewed-By: Chengzhong Wu --- .../bootstrap/switches/is_main_thread.js | 29 +++++++++++++++++++ lib/internal/process/pre_execution.js | 18 ++++++------ test/parallel/test-bootstrap-modules.js | 3 -- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/internal/bootstrap/switches/is_main_thread.js b/lib/internal/bootstrap/switches/is_main_thread.js index ace7dc1c904018..212a067e3a7058 100644 --- a/lib/internal/bootstrap/switches/is_main_thread.js +++ b/lib/internal/bootstrap/switches/is_main_thread.js @@ -286,3 +286,32 @@ rawMethods.resetStdioForTesting = function() { stdout = undefined; stderr = undefined; }; + +// Needed by the module loader and generally needed everywhere. +require('fs'); +require('util'); +require('url'); + +require('internal/modules/cjs/loader'); +require('internal/modules/esm/utils'); +require('internal/vm/module'); +// Needed to refresh the time origin. +require('internal/perf/utils'); +// Needed to register the async hooks. +if (internalBinding('config').hasInspector) { + require('internal/inspector_async_hook'); +} +// Needed to set the wasm web API callbacks. +internalBinding('wasm_web_api'); +// Needed to detect whether it's on main thread. +internalBinding('worker'); +// Needed to setup source maps. +require('internal/source_map/source_map_cache'); +// Needed by most execution modes. +require('internal/modules/run_main'); +// Needed to refresh DNS configurations. +require('internal/dns/utils'); +// Needed by almost all execution modes. It's fine to +// load them into the snapshot as long as we don't run +// any of the initialization. +require('internal/process/pre_execution'); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 277737be590c61..1e5de6ed3e947b 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -14,7 +14,6 @@ const { const { getOptionValue, - getEmbedderOptions, refreshOptions, } = require('internal/options'); const { reconnectZeroFillToggle } = require('internal/buffer'); @@ -74,6 +73,7 @@ function prepareExecution(options) { initializeReport(); initializeSourceMapsHandlers(); initializeDeprecations(); + require('internal/dns/utils').initializeDns(); setupSymbolDisposePolyfill(); @@ -266,8 +266,9 @@ function setupFetch() { }); // The WebAssembly Web API: https://webassembly.github.io/spec/web-api - const { wasmStreamingCallback } = require('internal/wasm_web_api'); - internalBinding('wasm_web_api').setImplementation(wasmStreamingCallback); + internalBinding('wasm_web_api').setImplementation((streamState, source) => { + require('internal/wasm_web_api').wasmStreamingCallback(streamState, source); + }); } // TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is @@ -324,12 +325,12 @@ function setupStacktracePrinterOnSigint() { } function initializeReport() { - const { report } = require('internal/process/report'); ObjectDefineProperty(process, 'report', { __proto__: null, enumerable: true, configurable: true, get() { + const { report } = require('internal/process/report'); return report; }, }); @@ -344,9 +345,10 @@ function setupDebugEnv() { // This has to be called after initializeReport() is called function initializeReportSignalHandlers() { - const { addSignalHandler } = require('internal/process/report'); - - addSignalHandler(); + if (getOptionValue('--report-on-signal')) { + const { addSignalHandler } = require('internal/process/report'); + addSignalHandler(); + } } function initializeHeapSnapshotSignalHandlers() { @@ -545,8 +547,6 @@ function initializeCJSLoader() { } function initializeESMLoader() { - if (getEmbedderOptions().shouldNotRegisterESMLoader) return; - const { initializeESM } = require('internal/modules/esm/utils'); initializeESM(); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index e1c173370d14cf..3c4b44609c34a3 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -24,7 +24,6 @@ const expectedModules = new Set([ 'Internal Binding options', 'Internal Binding performance', 'Internal Binding process_methods', - 'Internal Binding report', 'Internal Binding string_decoder', 'Internal Binding symbols', 'Internal Binding task_queue', @@ -64,7 +63,6 @@ const expectedModules = new Set([ 'NativeModule internal/process/per_thread', 'NativeModule internal/process/pre_execution', 'NativeModule internal/process/promises', - 'NativeModule internal/process/report', 'NativeModule internal/process/signal', 'NativeModule internal/process/task_queues', 'NativeModule internal/process/warning', @@ -79,7 +77,6 @@ const expectedModules = new Set([ 'NativeModule internal/validators', 'NativeModule internal/vm', 'NativeModule internal/vm/module', - 'NativeModule internal/wasm_web_api', 'NativeModule internal/webidl', 'NativeModule internal/worker/js_transferable', 'Internal Binding blob', From bfa6a2e6b2299169f8b149f533de143b0faa1b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Wed, 8 Nov 2023 08:10:46 +0000 Subject: [PATCH 13/13] deps: V8: cherry-pick d69c7937c99d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [snapshot] Dont defer ByteArray when serializing JSTypedArray needs the base_pointer ByteArray immediately if it's on heap. JSTypedArray's base_pointer was initialized to Smi::uninitialized_deserialization_value at first when deserializing, and if base_pointer was deferred, we will mistakenly check JSTypedArray not on heap. Bug: v8:13149 Change-Id: I104c83ff9a2017de1c8071a9e116baa602f6977d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3813068 Reviewed-by: Jakob Linke Commit-Queue: 王澳 Cr-Commit-Position: refs/heads/main@{#82254} Refs: https://github.com/v8/v8/commit/d69c7937c99da7f67c3877cd63ae81d2c55978f3 --- common.gypi | 2 +- deps/v8/src/snapshot/deserializer.cc | 7 +++- .../src/snapshot/serializer-deserializer.cc | 5 ++- deps/v8/test/cctest/test-serialize.cc | 40 +++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/common.gypi b/common.gypi index 94fae065189043..36bfa41701aef3 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.26', + 'v8_embedder_string': '-node.27', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index e1383ca2b7fe0d..96e8427efa69da 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -427,6 +427,8 @@ void Deserializer::PostProcessNewJSReceiver( reinterpret_cast(backing_store) + data_view.byte_offset()); } else if (InstanceTypeChecker::IsJSTypedArray(instance_type)) { auto typed_array = JSTypedArray::cast(raw_obj); + // Note: ByteArray objects must not be deferred s.t. they are + // available here for is_on_heap(). See also: CanBeDeferred. // Fixup typed array pointers. if (typed_array.is_on_heap()) { typed_array.AddExternalPointerCompensationForDeserialization( @@ -517,7 +519,10 @@ void Deserializer::PostProcessNewObject(Handle map, // to |ObjectDeserializer::CommitPostProcessedObjects()|. new_allocation_sites_.push_back(Handle::cast(obj)); } else { - DCHECK(CanBeDeferred(*obj)); + // We dont defer ByteArray because JSTypedArray needs the base_pointer + // ByteArray immediately if it's on heap. + DCHECK(CanBeDeferred(*obj) || + InstanceTypeChecker::IsByteArray(instance_type)); } } } diff --git a/deps/v8/src/snapshot/serializer-deserializer.cc b/deps/v8/src/snapshot/serializer-deserializer.cc index d32de12ec05932..5d758cd8253498 100644 --- a/deps/v8/src/snapshot/serializer-deserializer.cc +++ b/deps/v8/src/snapshot/serializer-deserializer.cc @@ -51,10 +51,13 @@ bool SerializerDeserializer::CanBeDeferred(HeapObject o) { // 3. JS objects with embedder fields cannot be deferred because the // serialize/deserialize callbacks need the back reference immediately to // identify the object. + // 4. ByteArray cannot be deferred as JSTypedArray needs the base_pointer + // ByteArray immediately if it's on heap. // TODO(leszeks): Could we defer string serialization if forward references // were resolved after object post processing? return !o.IsMap() && !o.IsInternalizedString() && - !(o.IsJSObject() && JSObject::cast(o).GetEmbedderFieldCount() > 0); + !(o.IsJSObject() && JSObject::cast(o).GetEmbedderFieldCount() > 0) && + !o.IsByteArray(); } void SerializerDeserializer::RestoreExternalReferenceRedirector( diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 4252d4bd652034..e7f1dd4760c861 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -4990,6 +4990,46 @@ UNINITIALIZED_TEST(SnapshotCreatorAnonClassWithKeep) { delete[] blob.data; } +UNINITIALIZED_TEST(SnapshotCreatorDontDeferByteArrayForTypedArray) { + DisableAlwaysOpt(); + v8::StartupData blob; + { + v8::SnapshotCreator creator; + v8::Isolate* isolate = creator.GetIsolate(); + { + v8::HandleScope handle_scope(isolate); + + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + CompileRun( + "const z = new Uint8Array(1);\n" + "class A { \n" + " static x() { \n" + " } \n" + "} \n" + "class B extends A {} \n" + "B.foo = ''; \n" + "class C extends B {} \n" + "class D extends C {} \n" + "class E extends B {} \n" + "function F() {} \n" + "Object.setPrototypeOf(F, D); \n"); + creator.SetDefaultContext(context); + } + + blob = + creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); + CHECK(blob.raw_size > 0 && blob.data != nullptr); + } + { + SnapshotCreator creator(nullptr, &blob); + v8::Isolate* isolate = creator.GetIsolate(); + v8::HandleScope scope(isolate); + USE(v8::Context::New(isolate)); + } + delete[] blob.data; +} + class V8_NODISCARD DisableLazySourcePositionScope { public: DisableLazySourcePositionScope()