mirror of
https://github.com/nodejs/node.git
synced 2025-05-08 01:12:09 +00:00

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 <webadmin@geoffreybooth.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
318 lines
8.5 KiB
JavaScript
318 lines
8.5 KiB
JavaScript
'use strict';
|
|
|
|
const { ObjectDefineProperty } = primordials;
|
|
const rawMethods = internalBinding('process_methods');
|
|
const {
|
|
namespace: {
|
|
addSerializeCallback,
|
|
isBuildingSnapshot
|
|
},
|
|
} = require('internal/v8/startup_snapshot');
|
|
// TODO(joyeecheung): deprecate and remove these underscore methods
|
|
process._debugProcess = rawMethods._debugProcess;
|
|
process._debugEnd = rawMethods._debugEnd;
|
|
|
|
// See the discussion in https://github.com/nodejs/node/issues/19009 and
|
|
// https://github.com/nodejs/node/pull/34010 for why these are no-ops.
|
|
// Five word summary: they were broken beyond repair.
|
|
process._startProfilerIdleNotifier = () => {};
|
|
process._stopProfilerIdleNotifier = () => {};
|
|
|
|
function defineStream(name, getter) {
|
|
ObjectDefineProperty(process, name, {
|
|
__proto__: null,
|
|
configurable: true,
|
|
enumerable: true,
|
|
get: getter
|
|
});
|
|
}
|
|
|
|
defineStream('stdout', getStdout);
|
|
defineStream('stdin', getStdin);
|
|
defineStream('stderr', getStderr);
|
|
|
|
// Worker threads don't receive signals.
|
|
const {
|
|
startListeningIfSignal,
|
|
stopListeningIfSignal
|
|
} = require('internal/process/signal');
|
|
process.on('newListener', startListeningIfSignal);
|
|
process.on('removeListener', stopListeningIfSignal);
|
|
|
|
// ---- keep the attachment of the wrappers above so that it's easier to ----
|
|
// ---- compare the setups side-by-side -----
|
|
|
|
const { guessHandleType } = internalBinding('util');
|
|
|
|
function createWritableStdioStream(fd) {
|
|
let stream;
|
|
// Note stream._type is used for test-module-load-list.js
|
|
switch (guessHandleType(fd)) {
|
|
case 'TTY': {
|
|
const tty = require('tty');
|
|
stream = new tty.WriteStream(fd);
|
|
stream._type = 'tty';
|
|
break;
|
|
}
|
|
|
|
case 'FILE': {
|
|
const SyncWriteStream = require('internal/fs/sync_write_stream');
|
|
stream = new SyncWriteStream(fd, { autoClose: false });
|
|
stream._type = 'fs';
|
|
break;
|
|
}
|
|
|
|
case 'PIPE':
|
|
case 'TCP': {
|
|
const net = require('net');
|
|
|
|
// If fd is already being used for the IPC channel, libuv will return
|
|
// an error when trying to use it again. In that case, create the socket
|
|
// using the existing handle instead of the fd.
|
|
if (process.channel && process.channel.fd === fd) {
|
|
const { kChannelHandle } = require('internal/child_process');
|
|
stream = new net.Socket({
|
|
handle: process[kChannelHandle],
|
|
readable: false,
|
|
writable: true
|
|
});
|
|
} else {
|
|
stream = new net.Socket({
|
|
fd,
|
|
readable: false,
|
|
writable: true
|
|
});
|
|
}
|
|
|
|
stream._type = 'pipe';
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// Provide a dummy black-hole output for e.g. non-console
|
|
// Windows applications.
|
|
const { Writable } = require('stream');
|
|
stream = new Writable({
|
|
write(buf, enc, cb) {
|
|
cb();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// For supporting legacy API we put the FD here.
|
|
stream.fd = fd;
|
|
|
|
stream._isStdio = true;
|
|
|
|
return stream;
|
|
}
|
|
|
|
function dummyDestroy(err, cb) {
|
|
cb(err);
|
|
this._undestroy();
|
|
|
|
// We need to emit 'close' anyway so that the closing
|
|
// of the stream is observable. We just make sure we
|
|
// are not going to do it twice.
|
|
// The 'close' event is needed so that finished and
|
|
// pipeline work correctly.
|
|
if (!this._writableState.emitClose) {
|
|
process.nextTick(() => {
|
|
this.emit('close');
|
|
});
|
|
}
|
|
}
|
|
|
|
let stdin;
|
|
let stdout;
|
|
let stderr;
|
|
|
|
let stdoutDestroy;
|
|
let stderrDestroy;
|
|
|
|
function refreshStdoutOnSigWinch() {
|
|
stdout._refreshSize();
|
|
}
|
|
|
|
function refreshStderrOnSigWinch() {
|
|
stderr._refreshSize();
|
|
}
|
|
|
|
function addCleanup(fn) {
|
|
if (isBuildingSnapshot()) {
|
|
addSerializeCallback(fn);
|
|
}
|
|
}
|
|
|
|
function getStdout() {
|
|
if (stdout) return stdout;
|
|
stdout = createWritableStdioStream(1);
|
|
stdout.destroySoon = stdout.destroy;
|
|
// Override _destroy so that the fd is never actually closed.
|
|
stdoutDestroy = stdout._destroy;
|
|
stdout._destroy = dummyDestroy;
|
|
if (stdout.isTTY) {
|
|
process.on('SIGWINCH', refreshStdoutOnSigWinch);
|
|
}
|
|
|
|
addCleanup(function cleanupStdout() {
|
|
stdout._destroy = stdoutDestroy;
|
|
stdout.destroy();
|
|
process.removeListener('SIGWINCH', refreshStdoutOnSigWinch);
|
|
stdout = undefined;
|
|
});
|
|
// No need to add deserialize callback because stdout = undefined above
|
|
// causes the stream to be lazily initialized again later.
|
|
return stdout;
|
|
}
|
|
|
|
function getStderr() {
|
|
if (stderr) return stderr;
|
|
stderr = createWritableStdioStream(2);
|
|
stderr.destroySoon = stderr.destroy;
|
|
stderrDestroy = stderr._destroy;
|
|
// Override _destroy so that the fd is never actually closed.
|
|
stderr._destroy = dummyDestroy;
|
|
if (stderr.isTTY) {
|
|
process.on('SIGWINCH', refreshStderrOnSigWinch);
|
|
}
|
|
addCleanup(function cleanupStderr() {
|
|
stderr._destroy = stderrDestroy;
|
|
stderr.destroy();
|
|
process.removeListener('SIGWINCH', refreshStderrOnSigWinch);
|
|
stderr = undefined;
|
|
});
|
|
// No need to add deserialize callback because stderr = undefined above
|
|
// causes the stream to be lazily initialized again later.
|
|
return stderr;
|
|
}
|
|
|
|
function getStdin() {
|
|
if (stdin) return stdin;
|
|
const fd = 0;
|
|
|
|
switch (guessHandleType(fd)) {
|
|
case 'TTY': {
|
|
const tty = require('tty');
|
|
stdin = new tty.ReadStream(fd);
|
|
break;
|
|
}
|
|
|
|
case 'FILE': {
|
|
const fs = require('fs');
|
|
stdin = new fs.ReadStream(null, { fd: fd, autoClose: false });
|
|
break;
|
|
}
|
|
|
|
case 'PIPE':
|
|
case 'TCP': {
|
|
const net = require('net');
|
|
|
|
// It could be that process has been started with an IPC channel
|
|
// sitting on fd=0, in such case the pipe for this fd is already
|
|
// present and creating a new one will lead to the assertion failure
|
|
// in libuv.
|
|
if (process.channel && process.channel.fd === fd) {
|
|
stdin = new net.Socket({
|
|
handle: process.channel,
|
|
readable: true,
|
|
writable: false,
|
|
manualStart: true
|
|
});
|
|
} else {
|
|
stdin = new net.Socket({
|
|
fd: fd,
|
|
readable: true,
|
|
writable: false,
|
|
manualStart: true
|
|
});
|
|
}
|
|
// Make sure the stdin can't be `.end()`-ed
|
|
stdin._writableState.ended = true;
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// Provide a dummy contentless input for e.g. non-console
|
|
// Windows applications.
|
|
const { Readable } = require('stream');
|
|
stdin = new Readable({ read() {} });
|
|
stdin.push(null);
|
|
}
|
|
}
|
|
|
|
// For supporting legacy API we put the FD here.
|
|
stdin.fd = fd;
|
|
|
|
// `stdin` starts out life in a paused state, but node doesn't
|
|
// know yet. Explicitly to readStop() it to put it in the
|
|
// not-reading state.
|
|
if (stdin._handle && stdin._handle.readStop) {
|
|
stdin._handle.reading = false;
|
|
stdin._readableState.reading = false;
|
|
stdin._handle.readStop();
|
|
}
|
|
|
|
// If the user calls stdin.pause(), then we need to stop reading
|
|
// once the stream implementation does so (one nextTick later),
|
|
// so that the process can close down.
|
|
stdin.on('pause', () => {
|
|
process.nextTick(onpause);
|
|
});
|
|
|
|
function onpause() {
|
|
if (!stdin._handle)
|
|
return;
|
|
if (stdin._handle.reading && !stdin.readableFlowing) {
|
|
stdin._readableState.reading = false;
|
|
stdin._handle.reading = false;
|
|
stdin._handle.readStop();
|
|
}
|
|
}
|
|
|
|
addCleanup(function cleanupStdin() {
|
|
stdin.destroy();
|
|
stdin = undefined;
|
|
});
|
|
// No need to add deserialize callback because stdin = undefined above
|
|
// causes the stream to be lazily initialized again later.
|
|
return stdin;
|
|
}
|
|
|
|
// Used by internal tests.
|
|
rawMethods.resetStdioForTesting = function() {
|
|
stdin = undefined;
|
|
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');
|