process: split execution into main scripts

This patch splits the execution mode selection from the environment
setup in `lib/internal/bootstrap/node.js`, and split the entry point
of different execution mode into main scripts under
`lib/internal/main`:

- `check_syntax.js`: used when `-c`/`--check` which only checks the
  syntax of the input instead of executing it.
- `eval_stdin.js`: used when `-e` is passed without value and stdin
  is not a TTY (e.g. something is piped).
- `eval_string`: used when `-e` is passed along with a string argument
- `inspect.js`: for `node inspect`/`node debug`
- `print_bash_completion.js`: for `--completion-bash`
- `print_help.js`: for `--help`
- `prof_process.js`: for `--prof-process`
- `repl.js`: for the REPL
- `run_main_module.js`: used when a main module is passed
- `run_third_party_main.js`: for the legacy `_third_party_main.js`
  support
- `worker_thread.js`: for workers

This makes the entry points easier to navigate and paves the way
for customized v8 snapshots (that do not need to deserialize
execution mode setup) and better embedder APIs.

As an example, after this patch, for the most common case where
Node.js executes a user module as an entry point, it essentially
goes through:

- `lib/internal/per_context.js` to setup the v8 Context (which is
  also run when setting up contexts for the `vm` module)
- `lib/internal/bootstrap/loaders.js` to set up internal binding
  and builtin module loaders (that are separate from the loaders
  accessible in the user land).
- `lib/internal/bootstrap/node.js`: to set up the rest of the
  environment, including various globals and the process object
- `lib/internal/main/run_main_module.js`: which is selected from
  C++ to prepare execution of the user module.

This patch also removes `NativeModuleLoader::CompileAndCall` and
exposes `NativeModuleLoader::LookupAndCompile` directly so that
we can handle syntax errors and runtime errors of bootstrap
scripts differently.

PR-URL: https://github.com/nodejs/node/pull/25667
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Joyee Cheung 2019-01-15 23:12:21 +08:00
parent feebdc5bc5
commit 6967f91368
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
37 changed files with 861 additions and 727 deletions

View File

@ -13,7 +13,7 @@ const { hasTracing, hasInspector } = process.binding('config');
// Modules with source code compiled in js2c that
// cannot be compiled with the code cache.
const cannotUseCache = [
const cannotBeRequired = [
'sys', // Deprecated.
'internal/v8_prof_polyfill',
'internal/v8_prof_processor',
@ -21,8 +21,7 @@ const cannotUseCache = [
'internal/per_context',
'internal/test/binding',
// TODO(joyeecheung): update the C++ side so that
// the code cache is also used when compiling these two files.
'internal/bootstrap/loaders',
'internal/bootstrap/node'
];
@ -30,16 +29,16 @@ const cannotUseCache = [
// Skip modules that cannot be required when they are not
// built into the binary.
if (!hasInspector) {
cannotUseCache.push(
cannotBeRequired.push(
'inspector',
'internal/util/inspector',
);
}
if (!hasTracing) {
cannotUseCache.push('trace_events');
cannotBeRequired.push('trace_events');
}
if (!process.versions.openssl) {
cannotUseCache.push(
cannotBeRequired.push(
'crypto',
'https',
'http2',
@ -67,10 +66,10 @@ if (!process.versions.openssl) {
const cachableBuiltins = [];
for (const id of NativeModule.map.keys()) {
if (id.startsWith('internal/deps')) {
cannotUseCache.push(id);
if (id.startsWith('internal/deps') || id.startsWith('internal/main')) {
cannotBeRequired.push(id);
}
if (!cannotUseCache.includes(id)) {
if (!cannotBeRequired.includes(id)) {
cachableBuiltins.push(id);
}
}
@ -79,5 +78,5 @@ module.exports = {
cachableBuiltins,
getCodeCache,
compileFunction,
cannotUseCache
cannotBeRequired
};

View File

@ -160,7 +160,12 @@ internalBinding('module_wrap').callbackMap = new WeakMap();
// Think of this as module.exports in this file even though it is not
// written in CommonJS style.
const loaderExports = { internalBinding, NativeModule };
const loaderExports = {
internalBinding,
NativeModule,
require: nativeModuleRequire
};
const loaderId = 'internal/bootstrap/loaders';
// Set up NativeModule.
@ -194,7 +199,7 @@ for (var i = 0; i < moduleIds.length; ++i) {
NativeModule.map.set(id, mod);
}
NativeModule.require = function(id) {
function nativeModuleRequire(id) {
if (id === loaderId) {
return loaderExports;
}
@ -218,8 +223,9 @@ NativeModule.require = function(id) {
moduleLoadList.push(`NativeModule ${id}`);
mod.compile();
return mod.exports;
};
}
NativeModule.require = nativeModuleRequire;
NativeModule.exists = function(id) {
return NativeModule.map.has(id);
};

View File

@ -21,546 +21,294 @@ const { internalBinding, NativeModule } = loaderExports;
const { getOptionValue } = NativeModule.require('internal/options');
const config = internalBinding('config');
function startup() {
setupTraceCategoryState();
setupTraceCategoryState();
setupProcessObject();
// TODO(joyeecheung): this does not have to done so early, any fatal errors
// thrown before user code execution should simply crash the process
// and we do not care about any clean up at that point. We don't care
// about emitting any events if the process crash upon bootstrap either.
{
const {
fatalException,
setUncaughtExceptionCaptureCallback,
hasUncaughtExceptionCaptureCallback
} = NativeModule.require('internal/process/execution');
process._fatalException = fatalException;
process.setUncaughtExceptionCaptureCallback =
setUncaughtExceptionCaptureCallback;
process.hasUncaughtExceptionCaptureCallback =
hasUncaughtExceptionCaptureCallback;
}
setupGlobalVariables();
// Bootstrappers for all threads, including worker threads and main thread
const perThreadSetup = NativeModule.require('internal/process/per_thread');
// Bootstrappers for the main thread only
let mainThreadSetup;
// Bootstrappers for the worker threads only
let workerThreadSetup;
if (isMainThread) {
mainThreadSetup = NativeModule.require(
'internal/process/main_thread_only'
);
} else {
workerThreadSetup = NativeModule.require(
'internal/process/worker_thread_only'
);
}
// process.config is serialized config.gypi
process.config = JSON.parse(internalBinding('native_module').config);
const rawMethods = internalBinding('process_methods');
// Set up methods and events on the process object for the main thread
if (isMainThread) {
// This depends on process being an event emitter
mainThreadSetup.setupSignalHandlers(internalBinding);
process.abort = rawMethods.abort;
const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods);
process.umask = wrapped.umask;
process.chdir = wrapped.chdir;
// TODO(joyeecheung): deprecate and remove these underscore methods
process._debugProcess = rawMethods._debugProcess;
process._debugEnd = rawMethods._debugEnd;
process._startProfilerIdleNotifier =
rawMethods._startProfilerIdleNotifier;
process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
} else {
const wrapped = workerThreadSetup.wrapProcessMethods(rawMethods);
process.umask = wrapped.umask;
}
// Set up methods on the process object for all threads
{
process.cwd = rawMethods.cwd;
process.dlopen = rawMethods.dlopen;
process.uptime = rawMethods.uptime;
// TODO(joyeecheung): either remove them or make them public
process._getActiveRequests = rawMethods._getActiveRequests;
process._getActiveHandles = rawMethods._getActiveHandles;
// TODO(joyeecheung): remove these
process.reallyExit = rawMethods.reallyExit;
process._kill = rawMethods._kill;
const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);
process._rawDebug = wrapped._rawDebug;
process.hrtime = wrapped.hrtime;
process.hrtime.bigint = wrapped.hrtimeBigInt;
process.cpuUsage = wrapped.cpuUsage;
process.memoryUsage = wrapped.memoryUsage;
process.kill = wrapped.kill;
process.exit = wrapped.exit;
}
setupProcessObject();
// TODO(joyeecheung): this does not have to done so early, any fatal errors
// thrown before user code execution should simply crash the process
// and we do not care about any clean up at that point. We don't care
// about emitting any events if the process crash upon bootstrap either.
{
const {
onWarning,
emitWarning
} = NativeModule.require('internal/process/warning');
if (!process.noProcessWarnings && process.env.NODE_NO_WARNINGS !== '1') {
process.on('warning', onWarning);
}
process.emitWarning = emitWarning;
fatalException,
setUncaughtExceptionCaptureCallback,
hasUncaughtExceptionCaptureCallback
} = NativeModule.require('internal/process/execution');
const {
nextTick,
runNextTicks
} = NativeModule.require('internal/process/next_tick').setup();
process._fatalException = fatalException;
process.setUncaughtExceptionCaptureCallback =
setUncaughtExceptionCaptureCallback;
process.hasUncaughtExceptionCaptureCallback =
hasUncaughtExceptionCaptureCallback;
}
process.nextTick = nextTick;
// Used to emulate a tick manually in the JS land.
// A better name for this function would be `runNextTicks` but
// it has been exposed to the process object so we keep this legacy name
// TODO(joyeecheung): either remove it or make it public
process._tickCallback = runNextTicks;
setupGlobalVariables();
const credentials = internalBinding('credentials');
if (credentials.implementsPosixCredentials) {
process.getuid = credentials.getuid;
process.geteuid = credentials.geteuid;
process.getgid = credentials.getgid;
process.getegid = credentials.getegid;
process.getgroups = credentials.getgroups;
// Bootstrappers for all threads, including worker threads and main thread
const perThreadSetup = NativeModule.require('internal/process/per_thread');
// Bootstrappers for the main thread only
let mainThreadSetup;
// Bootstrappers for the worker threads only
let workerThreadSetup;
if (isMainThread) {
mainThreadSetup = NativeModule.require(
'internal/process/main_thread_only'
);
} else {
workerThreadSetup = NativeModule.require(
'internal/process/worker_thread_only'
);
}
if (isMainThread) {
const wrapped = mainThreadSetup.wrapPosixCredentialSetters(credentials);
process.initgroups = wrapped.initgroups;
process.setgroups = wrapped.setgroups;
process.setegid = wrapped.setegid;
process.seteuid = wrapped.seteuid;
process.setgid = wrapped.setgid;
process.setuid = wrapped.setuid;
}
}
// process.config is serialized config.gypi
process.config = JSON.parse(internalBinding('native_module').config);
const rawMethods = internalBinding('process_methods');
// Set up methods and events on the process object for the main thread
if (isMainThread) {
// This depends on process being an event emitter
mainThreadSetup.setupSignalHandlers(internalBinding);
process.abort = rawMethods.abort;
const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods);
process.umask = wrapped.umask;
process.chdir = wrapped.chdir;
// TODO(joyeecheung): deprecate and remove these underscore methods
process._debugProcess = rawMethods._debugProcess;
process._debugEnd = rawMethods._debugEnd;
process._startProfilerIdleNotifier =
rawMethods._startProfilerIdleNotifier;
process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
} else {
const wrapped = workerThreadSetup.wrapProcessMethods(rawMethods);
process.umask = wrapped.umask;
}
// Set up methods on the process object for all threads
{
process.cwd = rawMethods.cwd;
process.dlopen = rawMethods.dlopen;
process.uptime = rawMethods.uptime;
// TODO(joyeecheung): either remove them or make them public
process._getActiveRequests = rawMethods._getActiveRequests;
process._getActiveHandles = rawMethods._getActiveHandles;
// TODO(joyeecheung): remove these
process.reallyExit = rawMethods.reallyExit;
process._kill = rawMethods._kill;
const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);
process._rawDebug = wrapped._rawDebug;
process.hrtime = wrapped.hrtime;
process.hrtime.bigint = wrapped.hrtimeBigInt;
process.cpuUsage = wrapped.cpuUsage;
process.memoryUsage = wrapped.memoryUsage;
process.kill = wrapped.kill;
process.exit = wrapped.exit;
}
const {
onWarning,
emitWarning
} = NativeModule.require('internal/process/warning');
if (!process.noProcessWarnings && process.env.NODE_NO_WARNINGS !== '1') {
process.on('warning', onWarning);
}
process.emitWarning = emitWarning;
const {
nextTick,
runNextTicks
} = NativeModule.require('internal/process/next_tick').setup();
process.nextTick = nextTick;
// Used to emulate a tick manually in the JS land.
// A better name for this function would be `runNextTicks` but
// it has been exposed to the process object so we keep this legacy name
// TODO(joyeecheung): either remove it or make it public
process._tickCallback = runNextTicks;
const credentials = internalBinding('credentials');
if (credentials.implementsPosixCredentials) {
process.getuid = credentials.getuid;
process.geteuid = credentials.geteuid;
process.getgid = credentials.getgid;
process.getegid = credentials.getegid;
process.getgroups = credentials.getgroups;
if (isMainThread) {
const { getStdout, getStdin, getStderr } =
NativeModule.require('internal/process/stdio').getMainThreadStdio();
setupProcessStdio(getStdout, getStdin, getStderr);
} else {
const { getStdout, getStdin, getStderr } =
workerThreadSetup.initializeWorkerStdio();
setupProcessStdio(getStdout, getStdin, getStderr);
}
if (config.hasInspector) {
const {
enable,
disable
} = NativeModule.require('internal/inspector_async_hook');
internalBinding('inspector').registerAsyncHook(enable, disable);
}
// If the process is spawned with env NODE_CHANNEL_FD, it's probably
// spawned by our child_process module, then initialize IPC.
// This attaches some internal event listeners and creates:
// process.send(), process.channel, process.connected,
// process.disconnect()
if (isMainThread && process.env.NODE_CHANNEL_FD) {
mainThreadSetup.setupChildProcessIpcChannel();
}
// TODO(joyeecheung): move this down further to get better snapshotting
const experimentalPolicy = getOptionValue('--experimental-policy');
if (isMainThread && experimentalPolicy) {
process.emitWarning('Policies are experimental.',
'ExperimentalWarning');
const { pathToFileURL, URL } = NativeModule.require('url');
// URL here as it is slightly different parsing
// no bare specifiers for now
let manifestURL;
if (NativeModule.require('path').isAbsolute(experimentalPolicy)) {
manifestURL = new URL(`file:///${experimentalPolicy}`);
} else {
const cwdURL = pathToFileURL(process.cwd());
cwdURL.pathname += '/';
manifestURL = new URL(experimentalPolicy, cwdURL);
}
const fs = NativeModule.require('fs');
const src = fs.readFileSync(manifestURL, 'utf8');
NativeModule.require('internal/process/policy')
.setup(src, manifestURL.href);
}
const browserGlobals = !process._noBrowserGlobals;
if (browserGlobals) {
setupGlobalTimeouts();
setupGlobalConsole();
setupGlobalURL();
setupGlobalEncoding();
setupQueueMicrotask();
}
setupDOMException();
// On OpenBSD process.execPath will be relative unless we
// get the full path before process.execPath is used.
if (process.platform === 'openbsd') {
const { realpathSync } = NativeModule.require('fs');
process.execPath = realpathSync.native(process.execPath);
}
Object.defineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
value: process.argv[0]
});
process.argv[0] = process.execPath;
// Handle `--debug*` deprecation and invalidation.
if (process._invalidDebug) {
process.emitWarning(
'`node --debug` and `node --debug-brk` are invalid. ' +
'Please use `node --inspect` or `node --inspect-brk` instead.',
'DeprecationWarning', 'DEP0062', startup, true);
process.exit(9);
} else if (process._deprecatedDebugBrk) {
process.emitWarning(
'`node --inspect --debug-brk` is deprecated. ' +
'Please use `node --inspect-brk` instead.',
'DeprecationWarning', 'DEP0062', startup, true);
}
const { deprecate } = NativeModule.require('internal/util');
{
// Install legacy getters on the `util` binding for typechecking.
// TODO(addaleax): Turn into a full runtime deprecation.
const pendingDeprecation = getOptionValue('--pending-deprecation');
const utilBinding = internalBinding('util');
const types = NativeModule.require('internal/util/types');
for (const name of [
'isArrayBuffer', 'isArrayBufferView', 'isAsyncFunction',
'isDataView', 'isDate', 'isExternal', 'isMap', 'isMapIterator',
'isNativeError', 'isPromise', 'isRegExp', 'isSet', 'isSetIterator',
'isTypedArray', 'isUint8Array', 'isAnyArrayBuffer'
]) {
utilBinding[name] = pendingDeprecation ?
deprecate(types[name],
'Accessing native typechecking bindings of Node ' +
'directly is deprecated. ' +
`Please use \`util.types.${name}\` instead.`,
'DEP0103') :
types[name];
}
}
// process.allowedNodeEnvironmentFlags
Object.defineProperty(process, 'allowedNodeEnvironmentFlags', {
get() {
const flags = perThreadSetup.buildAllowedFlags();
process.allowedNodeEnvironmentFlags = flags;
return process.allowedNodeEnvironmentFlags;
},
// If the user tries to set this to another value, override
// this completely to that value.
set(value) {
Object.defineProperty(this, 'allowedNodeEnvironmentFlags', {
value,
configurable: true,
enumerable: true,
writable: true
});
},
enumerable: true,
configurable: true
});
// process.assert
process.assert = deprecate(
perThreadSetup.assert,
'process.assert() is deprecated. Please use the `assert` module instead.',
'DEP0100');
// TODO(joyeecheung): this property has not been well-maintained, should we
// deprecate it in favor of a better API?
const { isDebugBuild, hasOpenSSL } = config;
Object.defineProperty(process, 'features', {
enumerable: true,
writable: false,
configurable: false,
value: {
debug: isDebugBuild,
uv: true,
ipv6: true, // TODO(bnoordhuis) ping libuv
tls_alpn: hasOpenSSL,
tls_sni: hasOpenSSL,
tls_ocsp: hasOpenSSL,
tls: hasOpenSSL
}
});
// User-facing NODE_V8_COVERAGE environment variable that writes
// ScriptCoverage to a specified file.
if (process.env.NODE_V8_COVERAGE) {
const originalReallyExit = process.reallyExit;
const cwd = NativeModule.require('internal/process/execution').tryGetCwd();
const { resolve } = NativeModule.require('path');
// Resolve the coverage directory to an absolute path, and
// overwrite process.env so that the original path gets passed
// to child processes even when they switch cwd.
const coverageDirectory = resolve(cwd, process.env.NODE_V8_COVERAGE);
process.env.NODE_V8_COVERAGE = coverageDirectory;
const {
writeCoverage,
setCoverageDirectory
} = NativeModule.require('internal/coverage-gen/with_profiler');
setCoverageDirectory(coverageDirectory);
process.on('exit', writeCoverage);
process.reallyExit = (code) => {
writeCoverage();
originalReallyExit(code);
};
}
const perf = internalBinding('performance');
const {
NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE,
} = perf.constants;
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
if (getOptionValue('--experimental-report')) {
NativeModule.require('internal/process/report').setup();
}
if (isMainThread) {
return startMainThreadExecution;
} else {
return startWorkerThreadExecution;
const wrapped = mainThreadSetup.wrapPosixCredentialSetters(credentials);
process.initgroups = wrapped.initgroups;
process.setgroups = wrapped.setgroups;
process.setegid = wrapped.setegid;
process.seteuid = wrapped.seteuid;
process.setgid = wrapped.setgid;
process.setuid = wrapped.setuid;
}
}
function startWorkerThreadExecution() {
// If we are in a worker thread, execute the script sent through the
// message port.
const {
getEnvMessagePort,
threadId
} = internalBinding('worker');
const {
createMessageHandler,
createWorkerFatalExeception
} = NativeModule.require('internal/process/worker_thread_only');
// Set up the message port and start listening
const debug = NativeModule.require('util').debuglog('worker');
debug(`[${threadId}] is setting up worker child environment`);
const port = getEnvMessagePort();
port.on('message', createMessageHandler(
port,
prepareUserCodeExecution));
port.start();
// Overwrite fatalException
process._fatalException = createWorkerFatalExeception(port);
if (isMainThread) {
const { getStdout, getStdin, getStderr } =
NativeModule.require('internal/process/stdio').getMainThreadStdio();
setupProcessStdio(getStdout, getStdin, getStderr);
} else {
const { getStdout, getStdin, getStderr } =
workerThreadSetup.initializeWorkerStdio();
setupProcessStdio(getStdout, getStdin, getStderr);
}
// There are various modes that Node can run in. The most common two
// are running from a script and running the REPL - but there are a few
// others like the debugger or running --eval arguments. Here we decide
// which mode we run in.
function startMainThreadExecution(mainScript) {
if (mainScript) {
process.nextTick(() => {
NativeModule.require(mainScript);
if (config.hasInspector) {
const {
enable,
disable
} = NativeModule.require('internal/inspector_async_hook');
internalBinding('inspector').registerAsyncHook(enable, disable);
}
// If the process is spawned with env NODE_CHANNEL_FD, it's probably
// spawned by our child_process module, then initialize IPC.
// This attaches some internal event listeners and creates:
// process.send(), process.channel, process.connected,
// process.disconnect()
if (isMainThread && process.env.NODE_CHANNEL_FD) {
mainThreadSetup.setupChildProcessIpcChannel();
}
const browserGlobals = !process._noBrowserGlobals;
if (browserGlobals) {
setupGlobalTimeouts();
setupGlobalConsole();
setupGlobalURL();
setupGlobalEncoding();
setupQueueMicrotask();
}
setupDOMException();
// On OpenBSD process.execPath will be relative unless we
// get the full path before process.execPath is used.
if (process.platform === 'openbsd') {
const { realpathSync } = NativeModule.require('fs');
process.execPath = realpathSync.native(process.execPath);
}
Object.defineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
value: process.argv[0]
});
process.argv[0] = process.execPath;
// Handle `--debug*` deprecation and invalidation.
if (process._invalidDebug) {
process.emitWarning(
'`node --debug` and `node --debug-brk` are invalid. ' +
'Please use `node --inspect` or `node --inspect-brk` instead.',
'DeprecationWarning', 'DEP0062', undefined, true);
process.exit(9);
} else if (process._deprecatedDebugBrk) {
process.emitWarning(
'`node --inspect --debug-brk` is deprecated. ' +
'Please use `node --inspect-brk` instead.',
'DeprecationWarning', 'DEP0062', undefined, true);
}
const { deprecate } = NativeModule.require('internal/util');
{
// Install legacy getters on the `util` binding for typechecking.
// TODO(addaleax): Turn into a full runtime deprecation.
const pendingDeprecation = getOptionValue('--pending-deprecation');
const utilBinding = internalBinding('util');
const types = NativeModule.require('internal/util/types');
for (const name of [
'isArrayBuffer', 'isArrayBufferView', 'isAsyncFunction',
'isDataView', 'isDate', 'isExternal', 'isMap', 'isMapIterator',
'isNativeError', 'isPromise', 'isRegExp', 'isSet', 'isSetIterator',
'isTypedArray', 'isUint8Array', 'isAnyArrayBuffer'
]) {
utilBinding[name] = pendingDeprecation ?
deprecate(types[name],
'Accessing native typechecking bindings of Node ' +
'directly is deprecated. ' +
`Please use \`util.types.${name}\` instead.`,
'DEP0103') :
types[name];
}
}
// process.allowedNodeEnvironmentFlags
Object.defineProperty(process, 'allowedNodeEnvironmentFlags', {
get() {
const flags = perThreadSetup.buildAllowedFlags();
process.allowedNodeEnvironmentFlags = flags;
return process.allowedNodeEnvironmentFlags;
},
// If the user tries to set this to another value, override
// this completely to that value.
set(value) {
Object.defineProperty(this, 'allowedNodeEnvironmentFlags', {
value,
configurable: true,
enumerable: true,
writable: true
});
return;
},
enumerable: true,
configurable: true
});
// process.assert
process.assert = deprecate(
perThreadSetup.assert,
'process.assert() is deprecated. Please use the `assert` module instead.',
'DEP0100');
// TODO(joyeecheung): this property has not been well-maintained, should we
// deprecate it in favor of a better API?
const { isDebugBuild, hasOpenSSL } = config;
Object.defineProperty(process, 'features', {
enumerable: true,
writable: false,
configurable: false,
value: {
debug: isDebugBuild,
uv: true,
ipv6: true, // TODO(bnoordhuis) ping libuv
tls_alpn: hasOpenSSL,
tls_sni: hasOpenSSL,
tls_ocsp: hasOpenSSL,
tls: hasOpenSSL
}
});
// `node inspect ...` or `node debug ...`
if (process.argv[1] === 'inspect' || process.argv[1] === 'debug') {
if (process.argv[1] === 'debug') {
process.emitWarning(
'`node debug` is deprecated. Please use `node inspect` instead.',
'DeprecationWarning', 'DEP0068');
}
// Start the debugger agent.
process.nextTick(() => {
NativeModule.require('internal/deps/node-inspect/lib/_inspect').start();
});
return;
}
// node --help
if (getOptionValue('--help')) {
NativeModule.require('internal/print_help').print(process.stdout);
return;
}
// e.g. node --completion-bash >> ~/.bashrc
if (getOptionValue('--completion-bash')) {
NativeModule.require('internal/bash_completion').print(process.stdout);
return;
}
// `node --prof-process`
if (getOptionValue('--prof-process')) {
NativeModule.require('internal/v8_prof_processor');
return;
}
// There is user code to be run.
prepareUserCodeExecution();
executeUserCode();
// User-facing NODE_V8_COVERAGE environment variable that writes
// ScriptCoverage to a specified file.
if (process.env.NODE_V8_COVERAGE) {
const originalReallyExit = process.reallyExit;
const cwd = NativeModule.require('internal/process/execution').tryGetCwd();
const { resolve } = NativeModule.require('path');
// Resolve the coverage directory to an absolute path, and
// overwrite process.env so that the original path gets passed
// to child processes even when they switch cwd.
const coverageDirectory = resolve(cwd, process.env.NODE_V8_COVERAGE);
process.env.NODE_V8_COVERAGE = coverageDirectory;
const {
writeCoverage,
setCoverageDirectory
} = NativeModule.require('internal/coverage-gen/with_profiler');
setCoverageDirectory(coverageDirectory);
process.on('exit', writeCoverage);
process.reallyExit = (code) => {
writeCoverage();
originalReallyExit(code);
};
}
function prepareUserCodeExecution() {
// If this is a worker in cluster mode, start up the communication
// channel. This needs to be done before any user code gets executed
// (including preload modules).
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
const cluster = NativeModule.require('cluster');
cluster._setupWorker();
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}
const experimentalModules = getOptionValue('--experimental-modules');
const experimentalVMModules = getOptionValue('--experimental-vm-modules');
if (experimentalModules || experimentalVMModules) {
if (experimentalModules) {
process.emitWarning(
'The ESM module loader is experimental.',
'ExperimentalWarning', undefined);
}
const {
setImportModuleDynamicallyCallback,
setInitializeImportMetaObjectCallback
} = internalBinding('module_wrap');
const esm = NativeModule.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 userLoader = getOptionValue('--loader');
// If --loader is specified, create a loader with user hooks. Otherwise
// create the default loader.
esm.initializeLoader(process.cwd(), userLoader);
}
// For user code, we preload modules if `-r` is passed
const preloadModules = getOptionValue('--require');
if (preloadModules.length) {
const {
_preloadModules
} = NativeModule.require('internal/modules/cjs/loader');
_preloadModules(preloadModules);
}
}
function executeUserCode() {
// User passed `-e` or `--eval` arguments to Node without `-i` or
// `--interactive`.
// Note that the name `forceRepl` is merely an alias of `interactive`
// in code.
if (getOptionValue('[has_eval_string]') && !getOptionValue('--interactive')) {
const {
addBuiltinLibsToObject
} = NativeModule.require('internal/modules/cjs/helpers');
addBuiltinLibsToObject(global);
const source = getOptionValue('--eval');
const { evalScript } = NativeModule.require('internal/process/execution');
evalScript('[eval]', source, process._breakFirstLine);
return;
}
// If the first argument is a file name, run it as a main script
if (process.argv[1] && process.argv[1] !== '-') {
// Expand process.argv[1] into a full path.
const path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);
const CJSModule = NativeModule.require('internal/modules/cjs/loader');
// If user passed `-c` or `--check` arguments to Node, check its syntax
// instead of actually running the file.
if (getOptionValue('--check')) {
const fs = NativeModule.require('fs');
// Read the source.
const filename = CJSModule._resolveFilename(process.argv[1]);
const source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
process.exit(0);
}
// Note: this actually tries to run the module as a ESM first if
// --experimental-modules is on.
// TODO(joyeecheung): can we move that logic to here? Note that this
// is an undocumented method available via `require('module').runMain`
CJSModule.runMain();
return;
}
// Create the REPL if `-i` or `--interactive` is passed, or if
// stdin is a TTY.
// Note that the name `forceRepl` is merely an alias of `interactive`
// in code.
if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
const cliRepl = NativeModule.require('internal/repl');
cliRepl.createInternalRepl(process.env, (err, repl) => {
if (err) {
throw err;
}
repl.on('exit', () => {
if (repl._flushing) {
repl.pause();
return repl.once('flushHistory', () => {
process.exit();
});
}
process.exit();
});
});
// User passed '-e' or '--eval' along with `-i` or `--interactive`
if (process._eval != null) {
const { evalScript } = NativeModule.require('internal/process/execution');
evalScript('[eval]', process._eval, process._breakFirstLine);
}
return;
}
// Stdin is not a TTY, we will read it and execute it.
readAndExecuteStdin();
}
function readAndExecuteStdin() {
process.stdin.setEncoding('utf8');
let code = '';
process.stdin.on('data', (d) => {
code += d;
});
process.stdin.on('end', () => {
if (process._syntax_check_only != null) {
checkScriptSyntax(code, '[stdin]');
} else {
process._eval = code;
const { evalScript } = NativeModule.require('internal/process/execution');
evalScript('[stdin]', process._eval, process._breakFirstLine);
}
});
if (getOptionValue('--experimental-report')) {
NativeModule.require('internal/process/report').setup();
}
function setupTraceCategoryState() {
@ -795,22 +543,3 @@ function setupDOMException() {
const { registerDOMException } = internalBinding('messaging');
registerDOMException(DOMException);
}
function checkScriptSyntax(source, filename) {
const CJSModule = NativeModule.require('internal/modules/cjs/loader');
const vm = NativeModule.require('vm');
const {
stripShebang, stripBOM
} = NativeModule.require('internal/modules/cjs/helpers');
// Remove Shebang.
source = stripShebang(source);
// Remove BOM.
source = stripBOM(source);
// Wrap it.
source = CJSModule.wrap(source);
// Compile the script, this will throw if it fails.
new vm.Script(source, { displayErrors: true, filename });
}
return startup();

View File

@ -0,0 +1,82 @@
'use strict';
const { getOptionValue } = require('internal/options');
function initializeClusterIPC() {
// If this is a worker in cluster mode, start up the communication
// channel. This needs to be done before any user code gets executed
// (including preload modules).
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
const cluster = require('cluster');
cluster._setupWorker();
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}
}
function initializePolicy() {
const experimentalPolicy = getOptionValue('--experimental-policy');
if (experimentalPolicy) {
process.emitWarning('Policies are experimental.',
'ExperimentalWarning');
const { pathToFileURL, URL } = require('url');
// URL here as it is slightly different parsing
// no bare specifiers for now
let manifestURL;
if (require('path').isAbsolute(experimentalPolicy)) {
manifestURL = new URL(`file:///${experimentalPolicy}`);
} else {
const cwdURL = pathToFileURL(process.cwd());
cwdURL.pathname += '/';
manifestURL = new URL(experimentalPolicy, cwdURL);
}
const fs = require('fs');
const src = fs.readFileSync(manifestURL, 'utf8');
require('internal/process/policy')
.setup(src, manifestURL.href);
}
}
function initializeESMLoader() {
const experimentalModules = getOptionValue('--experimental-modules');
const experimentalVMModules = getOptionValue('--experimental-vm-modules');
if (experimentalModules || experimentalVMModules) {
if (experimentalModules) {
process.emitWarning(
'The ESM module loader is experimental.',
'ExperimentalWarning', undefined);
}
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 userLoader = getOptionValue('--loader');
// If --loader is specified, create a loader with user hooks. Otherwise
// create the default loader.
esm.initializeLoader(process.cwd(), userLoader);
}
}
function loadPreloadModules() {
// For user code, we preload modules if `-r` is passed
const preloadModules = getOptionValue('--require');
if (preloadModules) {
const {
_preloadModules
} = require('internal/modules/cjs/loader');
_preloadModules(preloadModules);
}
}
module.exports = {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
};

View File

@ -0,0 +1,2 @@
globals:
markBootstrapComplete: true

View File

@ -0,0 +1,56 @@
'use strict';
// If user passed `-c` or `--check` arguments to Node, check its syntax
// instead of actually running the file.
const {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
const {
readStdin
} = require('internal/process/execution');
const CJSModule = require('internal/modules/cjs/loader');
const vm = require('vm');
const {
stripShebang, stripBOM
} = require('internal/modules/cjs/helpers');
// TODO(joyeecheung): not every one of these are necessary
initializeClusterIPC();
initializePolicy();
initializeESMLoader();
loadPreloadModules();
markBootstrapComplete();
if (process.argv[1] && process.argv[1] !== '-') {
// Expand process.argv[1] into a full path.
const path = require('path');
process.argv[1] = path.resolve(process.argv[1]);
// Read the source.
const filename = CJSModule._resolveFilename(process.argv[1]);
const fs = require('fs');
const source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
} else {
readStdin((code) => {
checkScriptSyntax(code, '[stdin]');
});
}
function checkScriptSyntax(source, filename) {
// Remove Shebang.
source = stripShebang(source);
// Remove BOM.
source = stripBOM(source);
// Wrap it.
source = CJSModule.wrap(source);
// Compile the script, this will throw if it fails.
new vm.Script(source, { displayErrors: true, filename });
}

View File

@ -0,0 +1,26 @@
'use strict';
// Stdin is not a TTY, we will read it and execute it.
const {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
const {
evalScript,
readStdin
} = require('internal/process/execution');
initializeClusterIPC();
initializePolicy();
initializeESMLoader();
loadPreloadModules();
markBootstrapComplete();
readStdin((code) => {
process._eval = code;
evalScript('[stdin]', process._eval, process._breakFirstLine);
});

View File

@ -0,0 +1,22 @@
'use strict';
// User passed `-e` or `--eval` arguments to Node without `-i` or
// `--interactive`.
const {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
const { evalScript } = require('internal/process/execution');
const { addBuiltinLibsToObject } = require('internal/modules/cjs/helpers');
const source = require('internal/options').getOptionValue('--eval');
initializeClusterIPC();
initializePolicy();
initializeESMLoader();
loadPreloadModules();
addBuiltinLibsToObject(global);
markBootstrapComplete();
evalScript('[eval]', source, process._breakFirstLine);

View File

@ -0,0 +1,16 @@
'use strict';
// `node inspect ...` or `node debug ...`
if (process.argv[1] === 'debug') {
process.emitWarning(
'`node debug` is deprecated. Please use `node inspect` instead.',
'DeprecationWarning', 'DEP0068');
}
markBootstrapComplete();
// Start the debugger agent.
process.nextTick(() => {
require('internal/deps/node-inspect/lib/_inspect').start();
});

View File

@ -18,6 +18,6 @@ function print(stream) {
complete -F _node_complete node node_g`);
}
module.exports = {
print
};
markBootstrapComplete();
print(process.stdout);

View File

@ -172,6 +172,6 @@ function print(stream) {
stream.write('\nDocumentation can be found at https://nodejs.org/\n');
}
module.exports = {
print
};
markBootstrapComplete();
print(process.stdout);

View File

@ -0,0 +1,4 @@
'use strict';
markBootstrapComplete();
require('internal/v8_prof_processor');

44
lib/internal/main/repl.js Normal file
View File

@ -0,0 +1,44 @@
'use strict';
// Create the REPL if `-i` or `--interactive` is passed, or if
// the main module is not specified and stdin is a TTY.
const {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
const {
evalScript
} = require('internal/process/execution');
initializeClusterIPC();
initializePolicy();
initializeESMLoader();
loadPreloadModules();
const cliRepl = require('internal/repl');
cliRepl.createInternalRepl(process.env, (err, repl) => {
if (err) {
throw err;
}
repl.on('exit', () => {
if (repl._flushing) {
repl.pause();
return repl.once('flushHistory', () => {
process.exit();
});
}
process.exit();
});
});
// If user passed '-e' or '--eval' along with `-i` or `--interactive`,
// evaluate the code in the current context.
if (process._eval != null) {
evalScript('[eval]', process._eval, process._breakFirstLine);
}
markBootstrapComplete();

View File

@ -0,0 +1,27 @@
'use strict';
const {
initializeClusterIPC,
initializePolicy,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
initializeClusterIPC();
initializePolicy();
initializeESMLoader();
loadPreloadModules();
// Expand process.argv[1] into a full path.
const path = require('path');
process.argv[1] = path.resolve(process.argv[1]);
const CJSModule = require('internal/modules/cjs/loader');
markBootstrapComplete();
// Note: this actually tries to run the module as a ESM first if
// --experimental-modules is on.
// TODO(joyeecheung): can we move that logic to here? Note that this
// is an undocumented method available via `require('module').runMain`
CJSModule.runMain();

View File

@ -0,0 +1,9 @@
'use strict';
// Legacy _third_party_main.js support
markBootstrapComplete();
process.nextTick(() => {
require('_third_party_main');
});

View File

@ -0,0 +1,39 @@
'use strict';
// In worker threads, execute the script sent through the
// message port.
const {
initializeClusterIPC,
initializeESMLoader,
loadPreloadModules
} = require('internal/bootstrap/pre_execution');
const {
getEnvMessagePort,
threadId
} = internalBinding('worker');
const {
createMessageHandler,
createWorkerFatalExeception
} = require('internal/process/worker_thread_only');
const debug = require('util').debuglog('worker');
debug(`[${threadId}] is setting up worker child environment`);
function prepareUserCodeExecution() {
initializeClusterIPC();
initializeESMLoader();
loadPreloadModules();
}
// Set up the message port and start listening
const port = getEnvMessagePort();
port.on('message', createMessageHandler(port, prepareUserCodeExecution));
port.start();
// Overwrite fatalException
process._fatalException = createWorkerFatalExeception(port);
markBootstrapComplete();

View File

@ -165,7 +165,21 @@ function createFatalException() {
};
}
function readStdin(callback) {
process.stdin.setEncoding('utf8');
let code = '';
process.stdin.on('data', (d) => {
code += d;
});
process.stdin.on('end', () => {
callback(code);
});
}
module.exports = {
readStdin,
tryGetCwd,
evalScript,
fatalException: createFatalException(),

View File

@ -29,6 +29,7 @@
'lib/internal/bootstrap/cache.js',
'lib/internal/bootstrap/loaders.js',
'lib/internal/bootstrap/node.js',
'lib/internal/bootstrap/pre_execution.js',
'lib/async_hooks.js',
'lib/assert.js',
'lib/buffer.js',
@ -85,7 +86,6 @@
'lib/zlib.js',
'lib/internal/assert.js',
'lib/internal/async_hooks.js',
'lib/internal/bash_completion.js',
'lib/internal/buffer.js',
'lib/internal/cli_table.js',
'lib/internal/child_process.js',
@ -130,6 +130,17 @@
'lib/internal/inspector_async_hook.js',
'lib/internal/js_stream_socket.js',
'lib/internal/linkedlist.js',
'lib/internal/main/check_syntax.js',
'lib/internal/main/eval_string.js',
'lib/internal/main/eval_stdin.js',
'lib/internal/main/inspect.js',
'lib/internal/main/print_bash_completion.js',
'lib/internal/main/print_help.js',
'lib/internal/main/prof_process.js',
'lib/internal/main/repl.js',
'lib/internal/main/run_main_module.js',
'lib/internal/main/run_third_party_main.js',
'lib/internal/main/worker_thread.js',
'lib/internal/modules/cjs/helpers.js',
'lib/internal/modules/cjs/loader.js',
'lib/internal/modules/esm/loader.js',
@ -141,9 +152,8 @@
'lib/internal/safe_globals.js',
'lib/internal/net.js',
'lib/internal/options.js',
'lib/internal/policy/sri.js',
'lib/internal/policy/manifest.js',
'lib/internal/print_help.js',
'lib/internal/policy/sri.js',
'lib/internal/priority_queue.js',
'lib/internal/process/esm_loader.js',
'lib/internal/process/execution.js',

View File

@ -332,9 +332,20 @@ void Environment::Start(bool start_profiler_idle_notifier) {
uv_key_set(&thread_local_env, this);
}
MaybeLocal<Object> Environment::CreateProcessObject(
MaybeLocal<Object> Environment::ProcessCliArgs(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
if (args.size() > 1) {
std::string first_arg = args[1];
if (first_arg == "inspect") {
execution_mode_ = ExecutionMode::kInspect;
} else if (first_arg == "debug") {
execution_mode_ = ExecutionMode::kDebug;
} else if (first_arg != "-") {
execution_mode_ = ExecutionMode::kRunMainModule;
}
}
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
TRACING_CATEGORY_NODE1(environment)) != 0) {
auto traced_value = tracing::TracedValue::Create();

View File

@ -315,7 +315,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(write_host_object_string, "_writeHostObject") \
V(write_queue_size_string, "writeQueueSize") \
V(x_forwarded_string, "x-forwarded-for") \
V(zero_return_string, "ZERO_RETURN") \
V(zero_return_string, "ZERO_RETURN")
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
V(as_external, v8::External) \
@ -355,11 +355,13 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(http2session_on_stream_trailers_function, v8::Function) \
V(http2settings_constructor_template, v8::ObjectTemplate) \
V(http2stream_constructor_template, v8::ObjectTemplate) \
V(internal_binding_loader, v8::Function) \
V(immediate_callback_function, v8::Function) \
V(inspector_console_extension_installer, v8::Function) \
V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \
V(message_port, v8::Object) \
V(message_port_constructor_template, v8::FunctionTemplate) \
V(native_module_require, v8::Function) \
V(performance_entry_callback, v8::Function) \
V(performance_entry_template, v8::Function) \
V(pipe_constructor_template, v8::FunctionTemplate) \
@ -371,7 +373,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(script_data_constructor_function, v8::Function) \
V(secure_context_constructor_template, v8::FunctionTemplate) \
V(shutdown_wrap_template, v8::ObjectTemplate) \
V(start_execution_function, v8::Function) \
V(tcp_constructor_template, v8::FunctionTemplate) \
V(tick_callback_function, v8::Function) \
V(timers_callback_function, v8::Function) \
@ -611,7 +612,7 @@ class Environment {
~Environment();
void Start(bool start_profiler_idle_notifier);
v8::MaybeLocal<v8::Object> CreateProcessObject(
v8::MaybeLocal<v8::Object> ProcessCliArgs(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args);
@ -928,6 +929,24 @@ class Environment {
inline std::shared_ptr<EnvironmentOptions> options();
inline std::shared_ptr<HostPort> inspector_host_port();
enum class ExecutionMode {
kDefault,
kInspect, // node inspect
kDebug, // node debug
kPrintHelp, // node --help
kPrintBashCompletion, // node --completion-bash
kProfProcess, // node --prof-process
kEvalString, // node --eval without --interactive
kCheckSyntax, // node --check (incompatible with --eval)
kRepl,
kEvalStdin,
kRunMainModule
};
inline ExecutionMode execution_mode() { return execution_mode_; }
inline void set_execution_mode(ExecutionMode mode) { execution_mode_ = mode; }
private:
inline void CreateImmediate(native_immediate_callback cb,
void* data,
@ -937,6 +956,7 @@ class Environment {
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
const char* errmsg);
ExecutionMode execution_mode_ = ExecutionMode::kDefault;
std::list<binding::DLib> loaded_addons_;
v8::Isolate* const isolate_;
IsolateData* const isolate_data_;

View File

@ -100,6 +100,7 @@
#if defined(_MSC_VER)
#include <direct.h>
#include <io.h>
#define STDIN_FILENO 0
#else
#include <pthread.h>
#include <sys/resource.h> // getrlimit, setrlimit
@ -114,6 +115,7 @@ using v8::Array;
using v8::Boolean;
using v8::Context;
using v8::DEFAULT;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::Function;
using v8::FunctionCallbackInfo;
@ -605,8 +607,20 @@ static MaybeLocal<Value> ExecuteBootstrapper(
const char* id,
std::vector<Local<String>>* parameters,
std::vector<Local<Value>>* arguments) {
MaybeLocal<Value> ret = per_process::native_module_loader.CompileAndCall(
env->context(), id, parameters, arguments, env);
EscapableHandleScope scope(env->isolate());
MaybeLocal<Function> maybe_fn =
per_process::native_module_loader.LookupAndCompile(
env->context(), id, parameters, env);
if (maybe_fn.IsEmpty()) {
return MaybeLocal<Value>();
}
Local<Function> fn = maybe_fn.ToLocalChecked();
MaybeLocal<Value> result = fn->Call(env->context(),
Undefined(env->isolate()),
arguments->size(),
arguments->data());
// If there was an error during bootstrap then it was either handled by the
// FatalException handler or it's unrecoverable (e.g. max call stack
@ -615,44 +629,17 @@ static MaybeLocal<Value> ExecuteBootstrapper(
// There are only two ways to have a stack size > 1: 1) the user manually
// called MakeCallback or 2) user awaited during bootstrap, which triggered
// _tickCallback().
if (ret.IsEmpty()) {
if (result.IsEmpty()) {
env->async_hooks()->clear_async_id_stack();
}
return ret;
return scope.EscapeMaybe(result);
}
void LoadEnvironment(Environment* env) {
RunBootstrapping(env);
// To allow people to extend Node in different ways, this hook allows
// one to drop a file lib/_third_party_main.js into the build
// directory which will be executed instead of Node's normal loading.
if (per_process::native_module_loader.Exists("_third_party_main")) {
StartExecution(env, "_third_party_main");
} else {
// TODO(joyeecheung): create different scripts for different
// execution modes:
// - `main_thread_main.js` when env->is_main_thread()
// - `worker_thread_main.js` when !env->is_main_thread()
// - `run_third_party_main.js` for `_third_party_main`
// - `inspect_main.js` for `node inspect`
// - `mkcodecache_main.js` for the code cache generator
// - `print_help_main.js` for --help
// - `bash_completion_main.js` for --completion-bash
// - `internal/v8_prof_processor` for --prof-process
// And leave bootstrap/node.js dedicated to the setup of the environment.
// We may want to move this switch out of LoadEnvironment, especially for
// the per-process options.
StartExecution(env, nullptr);
}
}
void RunBootstrapping(Environment* env) {
MaybeLocal<Value> RunBootstrapping(Environment* env) {
CHECK(!env->has_run_bootstrapping_code());
env->set_has_run_bootstrapping_code(true);
HandleScope handle_scope(env->isolate());
EscapableHandleScope scope(env->isolate());
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
@ -702,14 +689,24 @@ void RunBootstrapping(Environment* env) {
Boolean::New(isolate,
env->options()->expose_internals)};
MaybeLocal<Value> loader_exports;
// Bootstrap internal loaders
loader_exports = ExecuteBootstrapper(
MaybeLocal<Value> loader_exports = ExecuteBootstrapper(
env, "internal/bootstrap/loaders", &loaders_params, &loaders_args);
if (loader_exports.IsEmpty()) {
return;
return MaybeLocal<Value>();
}
Local<Object> loader_exports_obj =
loader_exports.ToLocalChecked().As<Object>();
Local<Value> internal_binding_loader =
loader_exports_obj->Get(context, env->internal_binding_string())
.ToLocalChecked();
env->set_internal_binding_loader(internal_binding_loader.As<Function>());
Local<Value> require =
loader_exports_obj->Get(context, env->require_string()).ToLocalChecked();
env->set_native_module_require(require.As<Function>());
// process, loaderExports, isMainThread
std::vector<Local<String>> node_params = {
env->process_string(),
@ -717,43 +714,107 @@ void RunBootstrapping(Environment* env) {
FIXED_ONE_BYTE_STRING(isolate, "isMainThread")};
std::vector<Local<Value>> node_args = {
process,
loader_exports.ToLocalChecked(),
loader_exports_obj,
Boolean::New(isolate, env->is_main_thread())};
Local<Value> start_execution;
if (!ExecuteBootstrapper(
env, "internal/bootstrap/node", &node_params, &node_args)
.ToLocal(&start_execution)) {
return;
}
MaybeLocal<Value> result = ExecuteBootstrapper(
env, "internal/bootstrap/node", &node_params, &node_args);
if (start_execution->IsFunction())
env->set_start_execution_function(start_execution.As<Function>());
env->set_has_run_bootstrapping_code(true);
return scope.EscapeMaybe(result);
}
void StartExecution(Environment* env, const char* main_script_id) {
HandleScope handle_scope(env->isolate());
// We have to use Local<>::New because of the optimized way in which we access
// the object in the env->...() getters, which does not play well with
// resetting the handle while we're accessing the object through the Local<>.
Local<Function> start_execution =
Local<Function>::New(env->isolate(), env->start_execution_function());
env->set_start_execution_function(Local<Function>());
void MarkBootstrapComplete(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
env->performance_state()->Mark(
performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
}
if (start_execution.IsEmpty()) return;
MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
EscapableHandleScope scope(env->isolate());
CHECK_NE(main_script_id, nullptr);
Local<Value> main_script_v;
if (main_script_id == nullptr) {
// TODO(joyeecheung): make this mandatory - we may also create an overload
// for main_script that is a Local<Function>.
main_script_v = Undefined(env->isolate());
} else {
main_script_v = OneByteString(env->isolate(), main_script_id);
std::vector<Local<String>> parameters = {
env->process_string(),
env->require_string(),
env->internal_binding_string(),
FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete")};
std::vector<Local<Value>> arguments = {
env->process_object(),
env->native_module_require(),
env->internal_binding_loader(),
env->NewFunctionTemplate(MarkBootstrapComplete)
->GetFunction(env->context())
.ToLocalChecked()};
MaybeLocal<Value> result =
ExecuteBootstrapper(env, main_script_id, &parameters, &arguments);
return scope.EscapeMaybe(result);
}
MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
// To allow people to extend Node in different ways, this hook allows
// one to drop a file lib/_third_party_main.js into the build
// directory which will be executed instead of Node's normal loading.
if (per_process::native_module_loader.Exists("_third_party_main")) {
return StartExecution(env, "internal/main/run_third_party_main");
}
Local<Value> argv[] = {main_script_v};
USE(start_execution->Call(
env->context(), Undefined(env->isolate()), arraysize(argv), argv));
if (env->execution_mode() == Environment::ExecutionMode::kInspect ||
env->execution_mode() == Environment::ExecutionMode::kDebug) {
return StartExecution(env, "internal/main/inspect");
}
if (per_process::cli_options->print_help) {
env->set_execution_mode(Environment::ExecutionMode::kPrintHelp);
return StartExecution(env, "internal/main/print_help");
}
if (per_process::cli_options->print_bash_completion) {
env->set_execution_mode(Environment::ExecutionMode::kPrintBashCompletion);
return StartExecution(env, "internal/main/print_bash_completion");
}
if (env->options()->prof_process) {
env->set_execution_mode(Environment::ExecutionMode::kPrintBashCompletion);
return StartExecution(env, "internal/main/prof_process");
}
// -e/--eval without -i/--interactive
if (env->options()->has_eval_string && !env->options()->force_repl) {
env->set_execution_mode(Environment::ExecutionMode::kEvalString);
return StartExecution(env, "internal/main/eval_string");
}
if (env->options()->syntax_check_only) {
env->set_execution_mode(Environment::ExecutionMode::kCheckSyntax);
return StartExecution(env, "internal/main/check_syntax");
}
if (env->execution_mode() == Environment::ExecutionMode::kRunMainModule) {
return StartExecution(env, "internal/main/run_main_module");
}
if (env->options()->force_repl || uv_guess_handle(STDIN_FILENO) == UV_TTY) {
env->set_execution_mode(Environment::ExecutionMode::kRepl);
return StartExecution(env, "internal/main/repl");
}
env->set_execution_mode(Environment::ExecutionMode::kEvalStdin);
return StartExecution(env, "internal/main/eval_stdin");
}
void LoadEnvironment(Environment* env) {
CHECK(env->is_main_thread());
// TODO(joyeecheung): Not all of the execution modes in
// StartMainThreadExecution() make sense for embedders. Pick the
// useful ones out, and allow embedders to customize the entry
// point more directly without using _third_party_main.js
if (!RunBootstrapping(env).IsEmpty()) {
USE(StartMainThreadExecution(env));
}
}
@ -1180,7 +1241,7 @@ Environment* CreateEnvironment(IsolateData* isolate_data,
std::vector<std::string> exec_args(exec_argv, exec_argv + exec_argc);
Environment* env = new Environment(isolate_data, context);
env->Start(per_process::v8_is_profiling);
env->CreateProcessObject(args, exec_args);
env->ProcessCliArgs(args, exec_args);
return env;
}
@ -1220,7 +1281,7 @@ void FreePlatform(MultiIsolatePlatform* platform) {
Local<Context> NewContext(Isolate* isolate,
Local<ObjectTemplate> object_template) {
auto context = Context::New(isolate, nullptr, object_template);
Local<Context> context = Context::New(isolate, nullptr, object_template);
if (context.IsEmpty()) return context;
HandleScope handle_scope(isolate);
@ -1233,12 +1294,19 @@ Local<Context> NewContext(Isolate* isolate,
std::vector<Local<String>> parameters = {
FIXED_ONE_BYTE_STRING(isolate, "global")};
std::vector<Local<Value>> arguments = {context->Global()};
MaybeLocal<Value> result = per_process::native_module_loader.CompileAndCall(
context, "internal/per_context", &parameters, &arguments, nullptr);
Local<Value> arguments[] = {context->Global()};
MaybeLocal<Function> maybe_fn =
per_process::native_module_loader.LookupAndCompile(
context, "internal/per_context", &parameters, nullptr);
if (maybe_fn.IsEmpty()) {
return Local<Context>();
}
Local<Function> fn = maybe_fn.ToLocalChecked();
MaybeLocal<Value> result =
fn->Call(context, Undefined(isolate), arraysize(arguments), arguments);
// Execution failed during context creation.
// TODO(joyeecheung): deprecate this signature and return a MaybeLocal.
if (result.IsEmpty()) {
// Execution failed during context creation.
// TODO(joyeecheung): deprecate this signature and return a MaybeLocal.
return Local<Context>();
}
}
@ -1255,7 +1323,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
Context::Scope context_scope(context);
Environment env(isolate_data, context);
env.Start(per_process::v8_is_profiling);
env.CreateProcessObject(args, exec_args);
env.ProcessCliArgs(args, exec_args);
#if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM
CHECK(!env.inspector_agent()->IsListening());

View File

@ -268,8 +268,9 @@ bool SafeGetenv(const char* key, std::string* text);
void DefineZlibConstants(v8::Local<v8::Object> target);
void RunBootstrapping(Environment* env);
void StartExecution(Environment* env, const char* main_script_id);
v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env);
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
const char* main_script_id);
} // namespace node

View File

@ -175,26 +175,6 @@ void NativeModuleLoader::CompileFunction(
}
}
// TODO(joyeecheung): it should be possible to generate the argument names
// from some special comments for the bootstrapper case.
MaybeLocal<Value> NativeModuleLoader::CompileAndCall(
Local<Context> context,
const char* id,
std::vector<Local<String>>* parameters,
std::vector<Local<Value>>* arguments,
Environment* optional_env) {
Isolate* isolate = context->GetIsolate();
MaybeLocal<Function> compiled =
per_process::native_module_loader.LookupAndCompile(
context, id, parameters, nullptr);
if (compiled.IsEmpty()) {
return MaybeLocal<Value>();
}
Local<Function> fn = compiled.ToLocalChecked().As<Function>();
return fn->Call(
context, v8::Null(isolate), arguments->size(), arguments->data());
}
MaybeLocal<Function> NativeModuleLoader::CompileAsModule(Environment* env,
const char* id) {
std::vector<Local<String>> parameters = {env->exports_string(),

View File

@ -42,20 +42,17 @@ class NativeModuleLoader {
// Returns config.gypi as a JSON string
v8::Local<v8::String> GetConfigString(v8::Isolate* isolate) const;
// Run a script with JS source bundled inside the binary as if it's wrapped
// in a function called with a null receiver and arguments specified in C++.
// The returned value is empty if an exception is encountered.
// JS code run with this method can assume that their top-level
// declarations won't affect the global scope.
v8::MaybeLocal<v8::Value> CompileAndCall(
bool Exists(const char* id);
// For bootstrappers optional_env may be a nullptr.
// If an exception is encountered (e.g. source code contains
// syntax error), the returned value is empty.
v8::MaybeLocal<v8::Function> LookupAndCompile(
v8::Local<v8::Context> context,
const char* id,
std::vector<v8::Local<v8::String>>* parameters,
std::vector<v8::Local<v8::Value>>* arguments,
Environment* optional_env);
bool Exists(const char* id);
private:
static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args);
// Passing ids of builtin module source code into JS land as
@ -87,15 +84,6 @@ class NativeModuleLoader {
static v8::MaybeLocal<v8::Function> CompileAsModule(Environment* env,
const char* id);
// For bootstrappers optional_env may be a nullptr.
// If an exception is encountered (e.g. source code contains
// syntax error), the returned value is empty.
v8::MaybeLocal<v8::Function> LookupAndCompile(
v8::Local<v8::Context> context,
const char* id,
std::vector<v8::Local<v8::String>>* parameters,
Environment* optional_env);
NativeModuleRecordMap source_;
NativeModuleCacheMap code_cache_;
UnionBytes config_;

View File

@ -133,8 +133,8 @@ Worker::Worker(Environment* env,
env_->set_thread_id(thread_id_);
env_->Start(env->profiler_idle_notifier_started());
env_->CreateProcessObject(std::vector<std::string>{},
std::vector<std::string>{});
env_->ProcessCliArgs(std::vector<std::string>{},
std::vector<std::string>{});
// Done while on the parent thread
AddWorkerInspector(env, env_.get(), thread_id_, url_);
}
@ -192,10 +192,10 @@ void Worker::Run() {
HandleScope handle_scope(isolate_);
Environment::AsyncCallbackScope callback_scope(env_.get());
env_->async_hooks()->push_async_ids(1, 0);
RunBootstrapping(env_.get());
// TODO(joyeecheung): create a main script for worker threads
// that starts listening on the message port.
StartExecution(env_.get(), nullptr);
if (!RunBootstrapping(env_.get()).IsEmpty()) {
USE(StartExecution(env_.get(), "internal/main/worker_thread"));
}
env_->async_hooks()->pop_async_id(1);
Debug(this, "Loaded environment for worker %llu", thread_id_);

View File

@ -9,7 +9,7 @@ const { isMainThread } = require('../common');
const assert = require('assert');
const {
cachableBuiltins,
cannotUseCache
cannotBeRequired
} = require('internal/bootstrap/cache');
const {
@ -60,7 +60,7 @@ if (process.config.variables.node_code_cache_path === undefined) {
);
for (const key of loadedModules) {
if (cannotUseCache.includes(key)) {
if (cannotBeRequired.includes(key)) {
assert(compiledWithoutCache.has(key),
`"${key}" should've been compiled without code cache`);
} else {

View File

@ -17,4 +17,3 @@ AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
at *
at *
at *
at *

View File

@ -12,4 +12,4 @@ RangeError: Invalid input
at tryModuleLoad (internal/modules/cjs/loader.js:*:*)
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -14,5 +14,4 @@ AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
at tryModuleLoad (internal/modules/cjs/loader.js:*:*)
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -9,8 +9,7 @@ SyntaxError: Strict mode code may not include a with statement
at Object.<anonymous> ([eval]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/eval_string.js:*:*
42
42
[eval]:1
@ -24,8 +23,7 @@ Error: hello
at Object.<anonymous> ([eval]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/eval_string.js:*:*
[eval]:1
throw new Error("hello")
@ -38,8 +36,7 @@ Error: hello
at Object.<anonymous> ([eval]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/eval_string.js:*:*
100
[eval]:1
var x = 100; y = x;
@ -52,8 +49,7 @@ ReferenceError: y is not defined
at Object.<anonymous> ([eval]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/eval_string.js:*:*
[eval]:1
var ______________________________________________; throw 10

View File

@ -12,11 +12,10 @@ Error: foo:bar
at tryModuleLoad (internal/modules/cjs/loader.js:*:*)
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*
Emitted 'error' event at:
at quux (*events_unhandled_error_common_trace.js:*:*)
at Object.<anonymous> (*events_unhandled_error_common_trace.js:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
[... lines matching original stack trace ...]
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -10,12 +10,10 @@ Error
at tryModuleLoad (internal/modules/cjs/loader.js:*:*)
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*
Emitted 'error' event at:
at process.nextTick (*events_unhandled_error_nexttick.js:*:*)
at processTicksAndRejections (internal/process/next_tick.js:*:*)
at process.runNextTicks [as _tickCallback] (internal/process/next_tick.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -10,10 +10,9 @@ Error
at tryModuleLoad (internal/modules/cjs/loader.js:*:*)
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*
Emitted 'error' event at:
at Object.<anonymous> (*events_unhandled_error_sameline.js:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
[... lines matching original stack trace ...]
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -7,5 +7,4 @@ ReferenceError: undefined_reference_error_maker is not defined
at processTicksAndRejections (internal/process/next_tick.js:*:*)
at process.runNextTicks [as _tickCallback] (internal/process/next_tick.js:*:*)
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
at executeUserCode (internal/bootstrap/node.js:*:*)
at startMainThreadExecution (internal/bootstrap/node.js:*:*)
at internal/main/run_main_module.js:*:*

View File

@ -2,6 +2,7 @@
[stdin]:1
with(this){__filename}
^^^^
SyntaxError: Strict mode code may not include a with statement
at new Script (vm.js:*)
at createScript (vm.js:*)
@ -9,10 +10,10 @@ SyntaxError: Strict mode code may not include a with statement
at Object.<anonymous> ([stdin]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at Socket.process.stdin.on (internal/bootstrap/node.js:*:*)
at readStdin (internal/main/eval_stdin.js:*:*)
at Socket.process.stdin.on (internal/process/execution.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at processTicksAndRejections (internal/process/next_tick.js:*:*)
42
42
[stdin]:1
@ -26,10 +27,10 @@ Error: hello
at Object.<anonymous> ([stdin]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at Socket.process.stdin.on (internal/bootstrap/node.js:*:*)
at readStdin (internal/main/eval_stdin.js:*:*)
at Socket.process.stdin.on (internal/process/execution.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at processTicksAndRejections (internal/process/next_tick.js:*:*)
[stdin]:1
throw new Error("hello")
^
@ -41,10 +42,10 @@ Error: hello
at Object.<anonymous> ([stdin]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at Socket.process.stdin.on (internal/bootstrap/node.js:*:*)
at readStdin (internal/main/eval_stdin.js:*:*)
at Socket.process.stdin.on (internal/process/execution.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at processTicksAndRejections (internal/process/next_tick.js:*:*)
100
[stdin]:1
var x = 100; y = x;
@ -57,10 +58,10 @@ ReferenceError: y is not defined
at Object.<anonymous> ([stdin]-wrapper:*:*)
at Module._compile (internal/modules/cjs/loader.js:*:*)
at evalScript (internal/process/execution.js:*:*)
at Socket.process.stdin.on (internal/bootstrap/node.js:*:*)
at readStdin (internal/main/eval_stdin.js:*:*)
at Socket.process.stdin.on (internal/process/execution.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at processTicksAndRejections (internal/process/next_tick.js:*:*)
[stdin]:1
var ______________________________________________; throw 10

View File

@ -13,8 +13,6 @@
at *
at *
at *
at *
at *
(node:*) Error: This was rejected
at * (*test*message*unhandled_promise_trace_warnings.js:*)
at *
@ -24,7 +22,6 @@
at *
at *
at *
at *
(node:*) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
at *
at *
@ -33,7 +30,6 @@
at *
at *
at *
at *
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
at handledRejection (internal/process/promises.js:*)
at promiseRejectHandler (internal/process/promises.js:*)

View File

@ -9,7 +9,6 @@
at *
at *
at *
at *
nested:
{ err:
Error: foo
@ -22,7 +21,6 @@
at *
at *
at *
at * } }
{
err: Error: foo
bar
@ -34,7 +32,6 @@
at *
at *
at *
at *,
nested: {
err: Error: foo
bar
@ -46,7 +43,6 @@
at *
at *
at *
at *
}
}
{ Error: foo
@ -59,5 +55,4 @@ bar
at *
at *
at *
at *
foo: 'bar' }