Skip to content
Closed
19 changes: 19 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,13 @@
default=False,
help='node will load builtin modules from disk instead of from binary')

parser.add_argument('--node-snapshot-main',
action='store',
dest='node_snapshot_main',
default=None,
help='Run a file when building the embedded snapshot. Currently ' +
'experimental.')

# Create compile_commands.json in out/Debug and out/Release.
parser.add_argument('-C',
action='store_true',
Expand Down Expand Up @@ -1216,6 +1223,18 @@ def configure_node(o):

o['variables']['want_separate_host_toolset'] = int(cross_compiling)

if options.node_snapshot_main is not None:
if options.shared:
# This should be possible to fix, but we will need to refactor the
# libnode target to avoid building it twice.
error('--node-snapshot-main is incompatible with --shared')
if options.without_node_snapshot:
error('--node-snapshot-main is incompatible with ' +
'--without-node-snapshot')
if cross_compiling:
error('--node-snapshot-main is incompatible with cross compilation')
o['variables']['node_snapshot_main'] = options.node_snapshot_main

if options.without_node_snapshot or options.node_builtin_modules_path:
o['variables']['node_use_node_snapshot'] = 'false'
else:
Expand Down
29 changes: 26 additions & 3 deletions lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
const {
getOptionValue,
getEmbedderOptions,
refreshOptions,
} = require('internal/options');
const { reconnectZeroFillToggle } = require('internal/buffer');
const {
Expand All @@ -26,7 +27,10 @@ const { Buffer } = require('buffer');
const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes;
const assert = require('internal/assert');

function prepareMainThreadExecution(expandArgv1 = false) {
function prepareMainThreadExecution(expandArgv1 = false,
initialzeModules = true) {
refreshRuntimeOptions();

// TODO(joyeecheung): this is also necessary for workers when they deserialize
// this toggle from the snapshot.
reconnectZeroFillToggle();
Expand Down Expand Up @@ -78,15 +82,23 @@ function prepareMainThreadExecution(expandArgv1 = false) {
initializeSourceMapsHandlers();
initializeDeprecations();
initializeWASI();

if (!initialzeModules) {
return;
}

initializeCJSLoader();
initializeESMLoader();

const CJSLoader = require('internal/modules/cjs/loader');
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
loadPreloadModules();
initializeFrozenIntrinsics();
}

function refreshRuntimeOptions() {
refreshOptions();
}

function patchProcessObject(expandArgv1) {
const binding = internalBinding('process_methods');
binding.patchProcessObject(process);
Expand All @@ -95,9 +107,13 @@ function patchProcessObject(expandArgv1) {

ObjectDefineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
// Only set it to true during snapshot building.
configurable: getOptionValue('--build-snapshot'),
value: process.argv[0]
});

process.exitCode = undefined;
process._exiting = false;
process.argv[0] = process.execPath;

if (expandArgv1 && process.argv[1] &&
Expand All @@ -111,6 +127,12 @@ function patchProcessObject(expandArgv1) {
}
}

// We need to initialize the global console here again with process.stdout
// and friends for snapshot deserialization.
const globalConsole = require('internal/console/global');
const { initializeGlobalConsole } = require('internal/console/constructor');
initializeGlobalConsole(globalConsole);

// TODO(joyeecheung): most of these should be deprecated and removed,
// except some that we need to be able to mutate during run time.
addReadOnlyProcessAlias('_eval', '--eval');
Expand Down Expand Up @@ -556,6 +578,7 @@ function loadPreloadModules() {
}

module.exports = {
refreshRuntimeOptions,
patchProcessObject,
setupCoverageHooks,
setupWarningHandler,
Expand Down
34 changes: 32 additions & 2 deletions lib/internal/bootstrap/switches/is_main_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,27 +122,53 @@ let stdin;
let stdout;
let stderr;

let stdoutDestroy;
let stderrDestroy;

function refreshStdoutOnSigWinch() {
stdout._refreshSize();
}

function refreshStderrOnSigWinch() {
stderr._refreshSize();
}

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', () => stdout._refreshSize());
process.on('SIGWINCH', refreshStdoutOnSigWinch);
}

internalBinding('mksnapshot').cleanups.push(function cleanupStdout() {
stdout._destroy = stdoutDestroy;
stdout.destroy();
process.removeListener('SIGWINCH', refreshStdoutOnSigWinch);
stdout = undefined;
});
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', () => stderr._refreshSize());
process.on('SIGWINCH', refreshStderrOnSigWinch);
}
internalBinding('mksnapshot').cleanups.push(function cleanupStderr() {
stderr._destroy = stderrDestroy;
stderr.destroy();
process.removeListener('SIGWINCH', refreshStderrOnSigWinch);
stderr = undefined;
});
return stderr;
}

Expand Down Expand Up @@ -229,6 +255,10 @@ function getStdin() {
}
}

internalBinding('mksnapshot').cleanups.push(function cleanupStdin() {
stdin.destroy();
stdin = undefined;
});
return stdin;
}

Expand Down
6 changes: 6 additions & 0 deletions lib/internal/console/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -669,9 +669,15 @@ Console.prototype.dirxml = Console.prototype.log;
Console.prototype.error = Console.prototype.warn;
Console.prototype.groupCollapsed = Console.prototype.group;

function initializeGlobalConsole(globalConsole) {
globalConsole[kBindStreamsLazy](process);
globalConsole[kBindProperties](true, 'auto');
}

module.exports = {
Console,
kBindStreamsLazy,
kBindProperties,
initializeGlobalConsole,
formatTime // exported for tests
};
7 changes: 1 addition & 6 deletions lib/internal/console/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ const {
} = primordials;

const {
Console,
kBindStreamsLazy,
kBindProperties
Console
} = require('internal/console/constructor');

const globalConsole = ObjectCreate({});
Expand All @@ -44,9 +42,6 @@ for (const prop of ReflectOwnKeys(Console.prototype)) {
ReflectDefineProperty(globalConsole, prop, desc);
}

globalConsole[kBindStreamsLazy](process);
globalConsole[kBindProperties](true, 'auto');

// This is a legacy feature - the Console constructor is exposed on
// the global console instance.
globalConsole.Console = Console;
Expand Down
142 changes: 142 additions & 0 deletions lib/internal/main/mksnapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
'use strict';

const {
Error,
SafeSet,
SafeArrayIterator
} = primordials;

const binding = internalBinding('mksnapshot');
const { NativeModule } = require('internal/bootstrap/loaders');
const {
compileSnapshotMain,
} = binding;

const {
getOptionValue
} = require('internal/options');

const {
readFileSync
} = require('fs');

const supportedModules = new SafeSet(new SafeArrayIterator([
// '_http_agent',
// '_http_client',
// '_http_common',
// '_http_incoming',
// '_http_outgoing',
// '_http_server',
'_stream_duplex',
'_stream_passthrough',
'_stream_readable',
'_stream_transform',
'_stream_wrap',
'_stream_writable',
// '_tls_common',
// '_tls_wrap',
'assert',
'assert/strict',
// 'async_hooks',
'buffer',
// 'child_process',
// 'cluster',
'console',
'constants',
'crypto',
// 'dgram',
// 'diagnostics_channel',
// 'dns',
// 'dns/promises',
// 'domain',
'events',
'fs',
'fs/promises',
// 'http',
// 'http2',
// 'https',
// 'inspector',
// 'module',
// 'net',
'os',
'path',
'path/posix',
'path/win32',
// 'perf_hooks',
'process',
'punycode',
'querystring',
// 'readline',
// 'repl',
'stream',
'stream/promises',
'string_decoder',
'sys',
'timers',
'timers/promises',
// 'tls',
// 'trace_events',
// 'tty',
'url',
'util',
'util/types',
'v8',
// 'vm',
// 'worker_threads',
// 'zlib',
]));

const warnedModules = new SafeSet();
function supportedInUserSnapshot(id) {
return supportedModules.has(id);
}

function requireForUserSnapshot(id) {
if (!NativeModule.canBeRequiredByUsers(id)) {
// eslint-disable-next-line no-restricted-syntax
const err = new Error(
`Cannot find module '${id}'. `
);
err.code = 'MODULE_NOT_FOUND';
throw err;
}
if (!supportedInUserSnapshot(id)) {
if (!warnedModules.has(id)) {
process.emitWarning(
`built-in module ${id} is not yet supported in user snapshots`);
warnedModules.add(id);
}
}

return require(id);
}

function main() {
const {
prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution');

prepareMainThreadExecution(true, false);
process.once('beforeExit', function runCleanups() {
for (const cleanup of binding.cleanups) {
cleanup();
}
});

const file = process.argv[1];
const path = require('path');
const filename = path.resolve(file);
const dirname = path.dirname(filename);
const source = readFileSync(file, 'utf-8');
const snapshotMainFunction = compileSnapshotMain(filename, source);

if (getOptionValue('--inspect-brk')) {
internalBinding('inspector').callAndPauseOnStart(
snapshotMainFunction, undefined,
requireForUserSnapshot, filename, dirname);
} else {
snapshotMainFunction(requireForUserSnapshot, filename, dirname);
}
}

main();
8 changes: 7 additions & 1 deletion lib/internal/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function getEmbedderOptions() {
return embedderOptions;
}

function refreshOptions() {
optionsMap = undefined;
aliasesMap = undefined;
}

function getOptionValue(optionName) {
const options = getCLIOptionsFromBinding();
if (optionName.startsWith('--no-')) {
Expand Down Expand Up @@ -68,5 +73,6 @@ module.exports = {
},
getOptionValue,
getAllowUnauthorized,
getEmbedderOptions
getEmbedderOptions,
refreshOptions
};
Loading