mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
bootstrap: use different scripts to setup different configurations
This patch splits the handling of `isMainThread` and `ownsProcessState` from conditionals in `lib/internal/bootstrap/node.js` into different scripts under `lib/internal/bootstrap/switches/`, and call them accordingly from C++ after `node.js` is run. This: - Creates a common denominator of the main thread and the worker thread bootstrap that can be snapshotted and shared by both. - Makes it possible to override the configurations on-the-fly. PR-URL: https://github.com/nodejs/node/pull/30862 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
@@ -3,9 +3,7 @@
|
||||
// This file is invoked by `node::RunBootstrapping()` in `src/node.cc`, and is
|
||||
// responsible for setting up node.js core before executing main scripts
|
||||
// under `lib/internal/main/`.
|
||||
// This file is currently run to bootstrap both the main thread and the worker
|
||||
// threads. Some setups are conditional, controlled with isMainThread and
|
||||
// ownsProcessState.
|
||||
//
|
||||
// This file is expected not to perform any asynchronous operations itself
|
||||
// when being executed - those should be done in either
|
||||
// `lib/internal/bootstrap/pre_execution.js` or in main scripts. The majority
|
||||
@@ -22,16 +20,21 @@
|
||||
// module loaders, including `process.binding()`, `process._linkedBinding()`,
|
||||
// `internalBinding()` and `NativeModule`.
|
||||
//
|
||||
// After this file is run, one of the main scripts under `lib/internal/main/`
|
||||
// will be selected by C++ to start the actual execution. The main scripts may
|
||||
// run additional setups exported by `lib/internal/bootstrap/pre_execution.js`,
|
||||
// depending on the execution mode.
|
||||
// This file is run to bootstrap both the main thread and the worker threads.
|
||||
// After this file is run, certain properties are setup according to the
|
||||
// configuration of the Node.js instance using the files in
|
||||
// `lib/internal/bootstrap/switches/`.
|
||||
//
|
||||
// Then, depending on how the Node.js instance is launched, one of the main
|
||||
// scripts in `lib/internal/main` will be selected by C++ to start the actual
|
||||
// execution. They may run additional setups exported by
|
||||
// `lib/internal/bootstrap/pre_execution.js` depending on the runtime states.
|
||||
|
||||
'use strict';
|
||||
|
||||
// This file is compiled as if it's wrapped in a function with arguments
|
||||
// passed by node::RunBootstrapping()
|
||||
/* global process, require, internalBinding, isMainThread, ownsProcessState */
|
||||
/* global process, require, internalBinding */
|
||||
|
||||
setupPrepareStackTrace();
|
||||
|
||||
@@ -54,48 +57,12 @@ setupBuffer();
|
||||
process.domain = null;
|
||||
process._exiting = false;
|
||||
|
||||
// Bootstrappers for all threads, including worker threads and main thread
|
||||
const perThreadSetup = require('internal/process/per_thread');
|
||||
// Bootstrappers for the main thread only
|
||||
let mainThreadSetup;
|
||||
// Bootstrappers for the worker threads only
|
||||
let workerThreadSetup;
|
||||
if (ownsProcessState) {
|
||||
mainThreadSetup = require(
|
||||
'internal/process/main_thread_only'
|
||||
);
|
||||
} else {
|
||||
workerThreadSetup = require(
|
||||
'internal/process/worker_thread_only'
|
||||
);
|
||||
}
|
||||
|
||||
// process.config is serialized config.gypi
|
||||
process.config = JSONParse(internalBinding('native_module').config);
|
||||
|
||||
// Bootstrappers for all threads, including worker threads and main thread
|
||||
const perThreadSetup = require('internal/process/per_thread');
|
||||
const rawMethods = internalBinding('process_methods');
|
||||
// Set up methods and events on the process object for the main thread
|
||||
if (isMainThread) {
|
||||
process.abort = rawMethods.abort;
|
||||
const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods);
|
||||
process.umask = wrapped.umask;
|
||||
process.chdir = wrapped.chdir;
|
||||
process.cwd = wrapped.cwd;
|
||||
|
||||
// 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.abort = workerThreadSetup.unavailable('process.abort()');
|
||||
process.chdir = workerThreadSetup.unavailable('process.chdir()');
|
||||
process.umask = wrapped.umask;
|
||||
process.cwd = rawMethods.cwd;
|
||||
}
|
||||
|
||||
// Set up methods on the process object for all threads
|
||||
{
|
||||
@@ -119,6 +86,11 @@ if (isMainThread) {
|
||||
process.memoryUsage = wrapped.memoryUsage;
|
||||
process.kill = wrapped.kill;
|
||||
process.exit = wrapped.exit;
|
||||
|
||||
process.openStdin = function() {
|
||||
process.stdin.resume();
|
||||
return process.stdin;
|
||||
};
|
||||
}
|
||||
|
||||
const credentials = internalBinding('credentials');
|
||||
@@ -128,34 +100,6 @@ if (credentials.implementsPosixCredentials) {
|
||||
process.getgid = credentials.getgid;
|
||||
process.getegid = credentials.getegid;
|
||||
process.getgroups = credentials.getgroups;
|
||||
|
||||
if (ownsProcessState) {
|
||||
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;
|
||||
} else {
|
||||
process.initgroups =
|
||||
workerThreadSetup.unavailable('process.initgroups()');
|
||||
process.setgroups = workerThreadSetup.unavailable('process.setgroups()');
|
||||
process.setegid = workerThreadSetup.unavailable('process.setegid()');
|
||||
process.seteuid = workerThreadSetup.unavailable('process.seteuid()');
|
||||
process.setgid = workerThreadSetup.unavailable('process.setgid()');
|
||||
process.setuid = workerThreadSetup.unavailable('process.setuid()');
|
||||
}
|
||||
}
|
||||
|
||||
if (isMainThread) {
|
||||
const { getStdout, getStdin, getStderr } =
|
||||
require('internal/process/stdio').getMainThreadStdio();
|
||||
setupProcessStdio(getStdout, getStdin, getStderr);
|
||||
} else {
|
||||
const { getStdout, getStdin, getStderr } =
|
||||
workerThreadSetup.createStdioGetters();
|
||||
setupProcessStdio(getStdout, getStdin, getStderr);
|
||||
}
|
||||
|
||||
// Setup the callbacks that node::AsyncWrap will call when there are hooks to
|
||||
@@ -343,31 +287,6 @@ function setupProcessObject() {
|
||||
});
|
||||
}
|
||||
|
||||
function setupProcessStdio(getStdout, getStdin, getStderr) {
|
||||
ObjectDefineProperty(process, 'stdout', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getStdout
|
||||
});
|
||||
|
||||
ObjectDefineProperty(process, 'stderr', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getStderr
|
||||
});
|
||||
|
||||
ObjectDefineProperty(process, 'stdin', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getStdin
|
||||
});
|
||||
|
||||
process.openStdin = function() {
|
||||
process.stdin.resume();
|
||||
return process.stdin;
|
||||
};
|
||||
}
|
||||
|
||||
function setupGlobalProxy() {
|
||||
ObjectDefineProperty(global, SymbolToStringTag, {
|
||||
value: 'global',
|
||||
|
||||
@@ -36,9 +36,6 @@ function prepareMainThreadExecution(expandArgv1 = false) {
|
||||
|
||||
setupDebugEnv();
|
||||
|
||||
// Only main thread receives signals.
|
||||
setupSignalHandlers();
|
||||
|
||||
// Process initial diagnostic reporting configuration, if present.
|
||||
initializeReport();
|
||||
initializeReportSignalHandlers(); // Main-thread-only.
|
||||
@@ -174,20 +171,7 @@ function setupDebugEnv() {
|
||||
}
|
||||
}
|
||||
|
||||
function setupSignalHandlers() {
|
||||
const {
|
||||
createSignalHandlers
|
||||
} = require('internal/process/main_thread_only');
|
||||
const {
|
||||
startListeningIfSignal,
|
||||
stopListeningIfSignal
|
||||
} = createSignalHandlers();
|
||||
process.on('newListener', startListeningIfSignal);
|
||||
process.on('removeListener', stopListeningIfSignal);
|
||||
}
|
||||
|
||||
// This has to be called after both initializeReport() and
|
||||
// setupSignalHandlers() are called
|
||||
// This has to be called after initializeReport() is called
|
||||
function initializeReportSignalHandlers() {
|
||||
if (!getOptionValue('--experimental-report')) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const credentials = internalBinding('credentials');
|
||||
|
||||
if (credentials.implementsPosixCredentials) {
|
||||
// TODO: this should be detached from ERR_WORKER_UNSUPPORTED_OPERATION
|
||||
const { unavailable } = require('internal/process/worker_thread_only');
|
||||
|
||||
process.initgroups = unavailable('process.initgroups()');
|
||||
process.setgroups = unavailable('process.setgroups()');
|
||||
process.setegid = unavailable('process.setegid()');
|
||||
process.seteuid = unavailable('process.seteuid()');
|
||||
process.setgid = unavailable('process.setgid()');
|
||||
process.setuid = unavailable('process.setuid()');
|
||||
}
|
||||
|
||||
// ---- keep the attachment of the wrappers above so that it's easier to ----
|
||||
// ---- compare the setups side-by-side -----
|
||||
96
lib/internal/bootstrap/switches/does_own_process_state.js
Normal file
96
lib/internal/bootstrap/switches/does_own_process_state.js
Normal file
@@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
const credentials = internalBinding('credentials');
|
||||
|
||||
if (credentials.implementsPosixCredentials) {
|
||||
const wrapped = 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;
|
||||
}
|
||||
|
||||
// ---- keep the attachment of the wrappers above so that it's easier to ----
|
||||
// ---- compare the setups side-by-side -----
|
||||
|
||||
function wrapPosixCredentialSetters(credentials) {
|
||||
const {
|
||||
ArrayIsArray,
|
||||
} = primordials;
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_UNKNOWN_CREDENTIAL
|
||||
}
|
||||
} = require('internal/errors');
|
||||
const {
|
||||
validateUint32
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
initgroups: _initgroups,
|
||||
setgroups: _setgroups,
|
||||
setegid: _setegid,
|
||||
seteuid: _seteuid,
|
||||
setgid: _setgid,
|
||||
setuid: _setuid
|
||||
} = credentials;
|
||||
|
||||
function initgroups(user, extraGroup) {
|
||||
validateId(user, 'user');
|
||||
validateId(extraGroup, 'extraGroup');
|
||||
// Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
|
||||
const result = _initgroups(user, extraGroup);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('User', user);
|
||||
} else if (result === 2) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
|
||||
}
|
||||
}
|
||||
|
||||
function setgroups(groups) {
|
||||
if (!ArrayIsArray(groups)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
|
||||
}
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
validateId(groups[i], `groups[${i}]`);
|
||||
}
|
||||
// Result is 0 on success. A positive integer indicates that the
|
||||
// corresponding group was not found.
|
||||
const result = _setgroups(groups);
|
||||
if (result > 0) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function wrapIdSetter(type, method) {
|
||||
return function(id) {
|
||||
validateId(id, 'id');
|
||||
// Result is 0 on success, 1 if credential is unknown.
|
||||
const result = method(id);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function validateId(id, name) {
|
||||
if (typeof id === 'number') {
|
||||
validateUint32(id, name);
|
||||
} else if (typeof id !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initgroups,
|
||||
setgroups,
|
||||
setegid: wrapIdSetter('Group', _setegid),
|
||||
seteuid: wrapIdSetter('User', _seteuid),
|
||||
setgid: wrapIdSetter('Group', _setgid),
|
||||
setuid: wrapIdSetter('User', _setuid)
|
||||
};
|
||||
}
|
||||
263
lib/internal/bootstrap/switches/is_main_thread.js
Normal file
263
lib/internal/bootstrap/switches/is_main_thread.js
Normal file
@@ -0,0 +1,263 @@
|
||||
'use strict';
|
||||
|
||||
const { ObjectDefineProperty } = primordials;
|
||||
const rawMethods = internalBinding('process_methods');
|
||||
|
||||
process.abort = rawMethods.abort;
|
||||
process.umask = wrappedUmask;
|
||||
process.chdir = wrappedChdir;
|
||||
process.cwd = wrappedCwd;
|
||||
|
||||
// TODO(joyeecheung): deprecate and remove these underscore methods
|
||||
process._debugProcess = rawMethods._debugProcess;
|
||||
process._debugEnd = rawMethods._debugEnd;
|
||||
process._startProfilerIdleNotifier = rawMethods._startProfilerIdleNotifier;
|
||||
process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
|
||||
|
||||
function defineStream(name, getter) {
|
||||
ObjectDefineProperty(process, name, {
|
||||
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');
|
||||
const {
|
||||
parseMode,
|
||||
validateString
|
||||
} = require('internal/validators');
|
||||
|
||||
// Cache the working directory to prevent lots of lookups. If the working
|
||||
// directory is changed by `chdir`, it'll be updated.
|
||||
let cachedCwd = '';
|
||||
|
||||
function wrappedChdir(directory) {
|
||||
validateString(directory, 'directory');
|
||||
rawMethods.chdir(directory);
|
||||
// Mark cache that it requires an update.
|
||||
cachedCwd = '';
|
||||
}
|
||||
|
||||
function wrappedUmask(mask) {
|
||||
if (mask !== undefined) {
|
||||
mask = parseMode(mask, 'mask');
|
||||
}
|
||||
return rawMethods.umask(mask);
|
||||
}
|
||||
|
||||
function wrappedCwd() {
|
||||
if (cachedCwd === '')
|
||||
cachedCwd = rawMethods.cwd();
|
||||
return cachedCwd;
|
||||
}
|
||||
|
||||
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) {
|
||||
stream = new net.Socket({
|
||||
handle: process.channel,
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
function getStdout() {
|
||||
if (stdout) return stdout;
|
||||
stdout = createWritableStdioStream(1);
|
||||
stdout.destroySoon = stdout.destroy;
|
||||
// Override _destroy so that the fd is never actually closed.
|
||||
stdout._destroy = dummyDestroy;
|
||||
if (stdout.isTTY) {
|
||||
process.on('SIGWINCH', () => stdout._refreshSize());
|
||||
}
|
||||
return stdout;
|
||||
}
|
||||
|
||||
function getStderr() {
|
||||
if (stderr) return stderr;
|
||||
stderr = createWritableStdioStream(2);
|
||||
stderr.destroySoon = stderr.destroy;
|
||||
// Override _destroy so that the fd is never actually closed.
|
||||
stderr._destroy = dummyDestroy;
|
||||
if (stderr.isTTY) {
|
||||
process.on('SIGWINCH', () => stderr._refreshSize());
|
||||
}
|
||||
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, {
|
||||
highWaterMark: 0,
|
||||
readable: true,
|
||||
writable: false
|
||||
});
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return stdin;
|
||||
}
|
||||
|
||||
// Used by internal tests.
|
||||
rawMethods.resetStdioForTesting = function() {
|
||||
stdin = undefined;
|
||||
stdout = undefined;
|
||||
stderr = undefined;
|
||||
};
|
||||
68
lib/internal/bootstrap/switches/is_not_main_thread.js
Normal file
68
lib/internal/bootstrap/switches/is_not_main_thread.js
Normal file
@@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
const { ObjectDefineProperty } = primordials;
|
||||
const rawMethods = internalBinding('process_methods');
|
||||
const {
|
||||
unavailable
|
||||
} = require('internal/process/worker_thread_only');
|
||||
|
||||
process.abort = unavailable('process.abort()');
|
||||
process.chdir = unavailable('process.chdir()');
|
||||
process.umask = wrappedUmask;
|
||||
process.cwd = rawMethods.cwd;
|
||||
|
||||
delete process._debugProcess;
|
||||
delete process._debugEnd;
|
||||
delete process._startProfilerIdleNotifier;
|
||||
delete process._stopProfilerIdleNotifier;
|
||||
|
||||
function defineStream(name, getter) {
|
||||
ObjectDefineProperty(process, name, {
|
||||
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.removeListener('newListener', startListeningIfSignal);
|
||||
process.removeListener('removeListener', stopListeningIfSignal);
|
||||
|
||||
// ---- keep the attachment of the wrappers above so that it's easier to ----
|
||||
// ---- compare the setups side-by-side -----
|
||||
|
||||
const {
|
||||
createWorkerStdio
|
||||
} = require('internal/worker/io');
|
||||
const {
|
||||
codes: { ERR_WORKER_UNSUPPORTED_OPERATION }
|
||||
} = require('internal/errors');
|
||||
|
||||
let workerStdio;
|
||||
function lazyWorkerStdio() {
|
||||
if (!workerStdio) workerStdio = createWorkerStdio();
|
||||
return workerStdio;
|
||||
}
|
||||
|
||||
function getStdout() { return lazyWorkerStdio().stdout; }
|
||||
|
||||
function getStderr() { return lazyWorkerStdio().stderr; }
|
||||
|
||||
function getStdin() { return lazyWorkerStdio().stdin; }
|
||||
|
||||
function wrappedUmask(mask) {
|
||||
// process.umask() is a read-only operation in workers.
|
||||
if (mask !== undefined) {
|
||||
throw new ERR_WORKER_UNSUPPORTED_OPERATION('Setting process.umask()');
|
||||
}
|
||||
|
||||
return rawMethods.umask(mask);
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// This file contains process bootstrappers that can only be
|
||||
// run in the main thread
|
||||
|
||||
const {
|
||||
ArrayIsArray,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
errnoException,
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_UNKNOWN_CREDENTIAL
|
||||
}
|
||||
} = require('internal/errors');
|
||||
const {
|
||||
parseMode,
|
||||
validateUint32,
|
||||
validateString
|
||||
} = require('internal/validators');
|
||||
|
||||
const { signals } = internalBinding('constants').os;
|
||||
|
||||
// The execution of this function itself should not cause any side effects.
|
||||
function wrapProcessMethods(binding) {
|
||||
// Cache the working directory to prevent lots of lookups. If the working
|
||||
// directory is changed by `chdir`, it'll be updated.
|
||||
let cachedCwd = '';
|
||||
|
||||
function chdir(directory) {
|
||||
validateString(directory, 'directory');
|
||||
binding.chdir(directory);
|
||||
// Mark cache that it requires an update.
|
||||
cachedCwd = '';
|
||||
}
|
||||
|
||||
function umask(mask) {
|
||||
if (mask !== undefined) {
|
||||
mask = parseMode(mask, 'mask');
|
||||
}
|
||||
return binding.umask(mask);
|
||||
}
|
||||
|
||||
function cwd() {
|
||||
if (cachedCwd === '')
|
||||
cachedCwd = binding.cwd();
|
||||
return cachedCwd;
|
||||
}
|
||||
|
||||
return {
|
||||
chdir,
|
||||
umask,
|
||||
cwd
|
||||
};
|
||||
}
|
||||
|
||||
function wrapPosixCredentialSetters(credentials) {
|
||||
const {
|
||||
initgroups: _initgroups,
|
||||
setgroups: _setgroups,
|
||||
setegid: _setegid,
|
||||
seteuid: _seteuid,
|
||||
setgid: _setgid,
|
||||
setuid: _setuid
|
||||
} = credentials;
|
||||
|
||||
function initgroups(user, extraGroup) {
|
||||
validateId(user, 'user');
|
||||
validateId(extraGroup, 'extraGroup');
|
||||
// Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
|
||||
const result = _initgroups(user, extraGroup);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('User', user);
|
||||
} else if (result === 2) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
|
||||
}
|
||||
}
|
||||
|
||||
function setgroups(groups) {
|
||||
if (!ArrayIsArray(groups)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
|
||||
}
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
validateId(groups[i], `groups[${i}]`);
|
||||
}
|
||||
// Result is 0 on success. A positive integer indicates that the
|
||||
// corresponding group was not found.
|
||||
const result = _setgroups(groups);
|
||||
if (result > 0) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function wrapIdSetter(type, method) {
|
||||
return function(id) {
|
||||
validateId(id, 'id');
|
||||
// Result is 0 on success, 1 if credential is unknown.
|
||||
const result = method(id);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function validateId(id, name) {
|
||||
if (typeof id === 'number') {
|
||||
validateUint32(id, name);
|
||||
} else if (typeof id !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initgroups,
|
||||
setgroups,
|
||||
setegid: wrapIdSetter('Group', _setegid),
|
||||
seteuid: wrapIdSetter('User', _seteuid),
|
||||
setgid: wrapIdSetter('Group', _setgid),
|
||||
setuid: wrapIdSetter('User', _setuid)
|
||||
};
|
||||
}
|
||||
|
||||
let Signal;
|
||||
function isSignal(event) {
|
||||
return typeof event === 'string' && signals[event] !== undefined;
|
||||
}
|
||||
|
||||
// Worker threads don't receive signals.
|
||||
function createSignalHandlers() {
|
||||
const signalWraps = new Map();
|
||||
|
||||
// Detect presence of a listener for the special signal types
|
||||
function startListeningIfSignal(type) {
|
||||
if (isSignal(type) && !signalWraps.has(type)) {
|
||||
if (Signal === undefined)
|
||||
Signal = internalBinding('signal_wrap').Signal;
|
||||
const wrap = new Signal();
|
||||
|
||||
wrap.unref();
|
||||
|
||||
wrap.onsignal = process.emit.bind(process, type, type);
|
||||
|
||||
const signum = signals[type];
|
||||
const err = wrap.start(signum);
|
||||
if (err) {
|
||||
wrap.close();
|
||||
throw errnoException(err, 'uv_signal_start');
|
||||
}
|
||||
|
||||
signalWraps.set(type, wrap);
|
||||
}
|
||||
}
|
||||
|
||||
function stopListeningIfSignal(type) {
|
||||
const wrap = signalWraps.get(type);
|
||||
if (wrap !== undefined && process.listenerCount(type) === 0) {
|
||||
wrap.close();
|
||||
signalWraps.delete(type);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
startListeningIfSignal,
|
||||
stopListeningIfSignal
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapProcessMethods,
|
||||
createSignalHandlers,
|
||||
wrapPosixCredentialSetters
|
||||
};
|
||||
49
lib/internal/process/signal.js
Normal file
49
lib/internal/process/signal.js
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
errnoException,
|
||||
} = require('internal/errors');
|
||||
|
||||
const { signals } = internalBinding('constants').os;
|
||||
|
||||
let Signal;
|
||||
const signalWraps = new Map();
|
||||
|
||||
function isSignal(event) {
|
||||
return typeof event === 'string' && signals[event] !== undefined;
|
||||
}
|
||||
|
||||
// Detect presence of a listener for the special signal types
|
||||
function startListeningIfSignal(type) {
|
||||
if (isSignal(type) && !signalWraps.has(type)) {
|
||||
if (Signal === undefined)
|
||||
Signal = internalBinding('signal_wrap').Signal;
|
||||
const wrap = new Signal();
|
||||
|
||||
wrap.unref();
|
||||
|
||||
wrap.onsignal = process.emit.bind(process, type, type);
|
||||
|
||||
const signum = signals[type];
|
||||
const err = wrap.start(signum);
|
||||
if (err) {
|
||||
wrap.close();
|
||||
throw errnoException(err, 'uv_signal_start');
|
||||
}
|
||||
|
||||
signalWraps.set(type, wrap);
|
||||
}
|
||||
}
|
||||
|
||||
function stopListeningIfSignal(type) {
|
||||
const wrap = signalWraps.get(type);
|
||||
if (wrap !== undefined && process.listenerCount(type) === 0) {
|
||||
wrap.close();
|
||||
signalWraps.delete(type);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
startListeningIfSignal,
|
||||
stopListeningIfSignal
|
||||
};
|
||||
@@ -1,206 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { guessHandleType } = internalBinding('util');
|
||||
exports.getMainThreadStdio = getMainThreadStdio;
|
||||
|
||||
function dummyDestroy(err, cb) {
|
||||
cb(err);
|
||||
|
||||
// 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');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getMainThreadStdio() {
|
||||
let stdin;
|
||||
let stdout;
|
||||
let stderr;
|
||||
|
||||
function getStdout() {
|
||||
if (stdout) return stdout;
|
||||
stdout = createWritableStdioStream(1);
|
||||
stdout.destroySoon = stdout.destroy;
|
||||
// Override _destroy so that the fd is never actually closed.
|
||||
stdout._destroy = dummyDestroy;
|
||||
if (stdout.isTTY) {
|
||||
process.on('SIGWINCH', () => stdout._refreshSize());
|
||||
}
|
||||
return stdout;
|
||||
}
|
||||
|
||||
function getStderr() {
|
||||
if (stderr) return stderr;
|
||||
stderr = createWritableStdioStream(2);
|
||||
stderr.destroySoon = stderr.destroy;
|
||||
// Override _destroy so that the fd is never actually closed.
|
||||
stderr._destroy = dummyDestroy;
|
||||
if (stderr.isTTY) {
|
||||
process.on('SIGWINCH', () => stderr._refreshSize());
|
||||
}
|
||||
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, {
|
||||
highWaterMark: 0,
|
||||
readable: true,
|
||||
writable: false
|
||||
});
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return stdin;
|
||||
}
|
||||
|
||||
exports.resetStdioForTesting = function() {
|
||||
stdin = undefined;
|
||||
stdout = undefined;
|
||||
stderr = undefined;
|
||||
};
|
||||
|
||||
return {
|
||||
getStdout,
|
||||
getStderr,
|
||||
getStdin
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
stream = new net.Socket({
|
||||
handle: process.channel,
|
||||
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;
|
||||
}
|
||||
@@ -3,42 +3,10 @@
|
||||
// This file contains process bootstrappers that can only be
|
||||
// run in the worker thread.
|
||||
|
||||
const {
|
||||
createWorkerStdio
|
||||
} = require('internal/worker/io');
|
||||
|
||||
const {
|
||||
codes: { ERR_WORKER_UNSUPPORTED_OPERATION }
|
||||
} = require('internal/errors');
|
||||
|
||||
let workerStdio;
|
||||
function lazyWorkerStdio() {
|
||||
if (!workerStdio) workerStdio = createWorkerStdio();
|
||||
return workerStdio;
|
||||
}
|
||||
|
||||
function createStdioGetters() {
|
||||
return {
|
||||
getStdout() { return lazyWorkerStdio().stdout; },
|
||||
getStderr() { return lazyWorkerStdio().stderr; },
|
||||
getStdin() { return lazyWorkerStdio().stdin; }
|
||||
};
|
||||
}
|
||||
|
||||
// The execution of this function itself should not cause any side effects.
|
||||
function wrapProcessMethods(binding) {
|
||||
function umask(mask) {
|
||||
// process.umask() is a read-only operation in workers.
|
||||
if (mask !== undefined) {
|
||||
throw new ERR_WORKER_UNSUPPORTED_OPERATION('Setting process.umask()');
|
||||
}
|
||||
|
||||
return binding.umask(mask);
|
||||
}
|
||||
|
||||
return { umask };
|
||||
}
|
||||
|
||||
function unavailable(name) {
|
||||
function unavailableInWorker() {
|
||||
throw new ERR_WORKER_UNSUPPORTED_OPERATION(name);
|
||||
@@ -49,7 +17,5 @@ function unavailable(name) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createStdioGetters,
|
||||
unavailable,
|
||||
wrapProcessMethods
|
||||
unavailable
|
||||
};
|
||||
|
||||
7
node.gyp
7
node.gyp
@@ -30,6 +30,10 @@
|
||||
'lib/internal/bootstrap/loaders.js',
|
||||
'lib/internal/bootstrap/node.js',
|
||||
'lib/internal/bootstrap/pre_execution.js',
|
||||
'lib/internal/bootstrap/switches/does_own_process_state.js',
|
||||
'lib/internal/bootstrap/switches/does_not_own_process_state.js',
|
||||
'lib/internal/bootstrap/switches/is_main_thread.js',
|
||||
'lib/internal/bootstrap/switches/is_not_main_thread.js',
|
||||
'lib/internal/per_context/primordials.js',
|
||||
'lib/internal/per_context/domexception.js',
|
||||
'lib/async_hooks.js',
|
||||
@@ -164,14 +168,13 @@
|
||||
'lib/internal/priority_queue.js',
|
||||
'lib/internal/process/esm_loader.js',
|
||||
'lib/internal/process/execution.js',
|
||||
'lib/internal/process/main_thread_only.js',
|
||||
'lib/internal/process/per_thread.js',
|
||||
'lib/internal/process/policy.js',
|
||||
'lib/internal/process/promises.js',
|
||||
'lib/internal/process/stdio.js',
|
||||
'lib/internal/process/warning.js',
|
||||
'lib/internal/process/worker_thread_only.js',
|
||||
'lib/internal/process/report.js',
|
||||
'lib/internal/process/signal.js',
|
||||
'lib/internal/process/task_queues.js',
|
||||
'lib/internal/querystring.js',
|
||||
'lib/internal/readline/utils.js',
|
||||
|
||||
47
src/node.cc
47
src/node.cc
@@ -128,7 +128,6 @@ namespace node {
|
||||
|
||||
using native_module::NativeModuleEnv;
|
||||
|
||||
using v8::Boolean;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
@@ -297,26 +296,60 @@ MaybeLocal<Value> Environment::BootstrapNode() {
|
||||
global->Set(context(), FIXED_ONE_BYTE_STRING(isolate_, "global"), global)
|
||||
.Check();
|
||||
|
||||
// process, require, internalBinding, isMainThread,
|
||||
// ownsProcessState, primordials
|
||||
// process, require, internalBinding, primordials
|
||||
std::vector<Local<String>> node_params = {
|
||||
process_string(),
|
||||
require_string(),
|
||||
internal_binding_string(),
|
||||
FIXED_ONE_BYTE_STRING(isolate_, "isMainThread"),
|
||||
FIXED_ONE_BYTE_STRING(isolate_, "ownsProcessState"),
|
||||
primordials_string()};
|
||||
std::vector<Local<Value>> node_args = {
|
||||
process_object(),
|
||||
native_module_require(),
|
||||
internal_binding_loader(),
|
||||
Boolean::New(isolate_, is_main_thread()),
|
||||
Boolean::New(isolate_, owns_process_state()),
|
||||
primordials()};
|
||||
|
||||
MaybeLocal<Value> result = ExecuteBootstrapper(
|
||||
this, "internal/bootstrap/node", &node_params, &node_args);
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return scope.EscapeMaybe(result);
|
||||
}
|
||||
|
||||
if (is_main_thread()) {
|
||||
result = ExecuteBootstrapper(this,
|
||||
"internal/bootstrap/switches/is_main_thread",
|
||||
&node_params,
|
||||
&node_args);
|
||||
} else {
|
||||
result =
|
||||
ExecuteBootstrapper(this,
|
||||
"internal/bootstrap/switches/is_not_main_thread",
|
||||
&node_params,
|
||||
&node_args);
|
||||
}
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return scope.EscapeMaybe(result);
|
||||
}
|
||||
|
||||
if (owns_process_state()) {
|
||||
result = ExecuteBootstrapper(
|
||||
this,
|
||||
"internal/bootstrap/switches/does_own_process_state",
|
||||
&node_params,
|
||||
&node_args);
|
||||
} else {
|
||||
result = ExecuteBootstrapper(
|
||||
this,
|
||||
"internal/bootstrap/switches/does_not_own_process_state",
|
||||
&node_params,
|
||||
&node_args);
|
||||
}
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
return scope.EscapeMaybe(result);
|
||||
}
|
||||
|
||||
Local<Object> env_var_proxy;
|
||||
if (!CreateEnvVarProxy(context(), isolate_, as_callback_data())
|
||||
.ToLocal(&env_var_proxy) ||
|
||||
|
||||
@@ -61,6 +61,7 @@ const expectedModules = new Set([
|
||||
'NativeModule internal/process/execution',
|
||||
'NativeModule internal/process/per_thread',
|
||||
'NativeModule internal/process/promises',
|
||||
'NativeModule internal/process/signal',
|
||||
'NativeModule internal/process/task_queues',
|
||||
'NativeModule internal/process/warning',
|
||||
'NativeModule internal/querystring',
|
||||
@@ -79,10 +80,7 @@ const expectedModules = new Set([
|
||||
'NativeModule vm',
|
||||
]);
|
||||
|
||||
if (common.isMainThread) {
|
||||
expectedModules.add('NativeModule internal/process/main_thread_only');
|
||||
expectedModules.add('NativeModule internal/process/stdio');
|
||||
} else {
|
||||
if (!common.isMainThread) {
|
||||
expectedModules.add('Internal Binding messaging');
|
||||
expectedModules.add('Internal Binding symbols');
|
||||
expectedModules.add('Internal Binding worker');
|
||||
|
||||
@@ -9,8 +9,10 @@ if (common.isWindows)
|
||||
function runTest(fd, streamName, testOutputStream, expectedName) {
|
||||
const result = child_process.spawnSync(process.execPath, [
|
||||
'--expose-internals',
|
||||
'-e', `
|
||||
require('internal/process/stdio').resetStdioForTesting();
|
||||
'--no-warnings',
|
||||
'-e',
|
||||
`const { internalBinding } = require('internal/test/binding');
|
||||
internalBinding('process_methods').resetStdioForTesting();
|
||||
fs.closeSync(${fd});
|
||||
const ctorName = process.${streamName}.constructor.name;
|
||||
process.${testOutputStream}.write(ctorName);
|
||||
|
||||
Reference in New Issue
Block a user