Skip to content

Commit

Permalink
bootstrap: lazy load non-essential modules
Browse files Browse the repository at this point in the history
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: nodejs#45659
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Reviewed-By: Tierney Cyren <hello@bnb.im>
  • Loading branch information
joyeecheung authored and panva committed Jan 31, 2023
1 parent a7232cb commit 7d6cc28
Show file tree
Hide file tree
Showing 26 changed files with 321 additions and 337 deletions.
26 changes: 13 additions & 13 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ const {
isInsideNodeModules,
lazyDOMException,
normalizeEncoding,
kIsEncodingSymbol
kIsEncodingSymbol,
defineLazyProperties,
} = require('internal/util');
const {
isAnyArrayBuffer,
Expand Down Expand Up @@ -123,15 +124,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);
Expand Down Expand Up @@ -1333,9 +1325,6 @@ function isUtf8(input) {
}

module.exports = {
Blob,
File,
resolveObjectURL,
Buffer,
SlowBuffer,
transcode,
Expand Down Expand Up @@ -1363,3 +1352,14 @@ ObjectDefineProperties(module.exports, {
set(val) { INSPECT_MAX_BYTES = val; }
}
});

defineLazyProperties(
module.exports,
'internal/blob',
['Blob', 'resolveObjectURL']
);
defineLazyProperties(
module.exports,
'internal/file',
['File']
);
26 changes: 12 additions & 14 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const {
custom: kCustomPromisifiedSymbol,
},
SideEffectFreeRegExpPrototypeExec,
defineLazyProperties,
} = require('internal/util');
const {
constants: {
Expand Down Expand Up @@ -122,11 +123,6 @@ const {
validatePrimitiveStringAfterArrayBufferView,
warnOnNonPortableTemplate
} = require('internal/fs/utils');
const {
Dir,
opendir,
opendirSync
} = require('internal/fs/dir');
const {
CHAR_FORWARD_SLASH,
CHAR_BACKWARD_SLASH,
Expand All @@ -143,9 +139,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;

Expand Down Expand Up @@ -388,6 +381,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

Expand Down Expand Up @@ -2311,12 +2305,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);
}
Expand Down Expand Up @@ -2374,7 +2369,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,
Expand All @@ -2400,7 +2395,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);
Expand Down Expand Up @@ -3004,8 +2999,6 @@ module.exports = fs = {
mkdtempSync,
open,
openSync,
opendir,
opendirSync,
readdir,
readdirSync,
read,
Expand Down Expand Up @@ -3043,7 +3036,6 @@ module.exports = fs = {
writeSync,
writev,
writevSync,
Dir,
Dirent,
Stats,

Expand Down Expand Up @@ -3089,6 +3081,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 },
Expand Down
3 changes: 1 addition & 2 deletions lib/internal/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -382,6 +380,7 @@ function updatePromiseHookMode() {
initHook = destroyTracking;
}
if (stopPromiseHook) stopPromiseHook();
const promiseHooks = require('internal/promise_hooks');
stopPromiseHook = promiseHooks.createHook({
init: initHook,
before: promiseBeforeHook,
Expand Down
161 changes: 62 additions & 99 deletions lib/internal/bootstrap/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const {
defineOperation,
exposeInterface,
lazyDOMExceptionClass,
defineLazyProperties,
defineReplaceableLazyAttribute,
exposeLazyInterfaces,
} = require('internal/util');
const config = internalBinding('config');

Expand All @@ -28,55 +31,39 @@ 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);
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 =
Expand Down Expand Up @@ -114,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',
]);
Loading

0 comments on commit 7d6cc28

Please sign in to comment.