From f7dd330ba0e7bfa97fad50000a5499faffcd6320 Mon Sep 17 00:00:00 2001 From: Zijian Liu Date: Thu, 26 Nov 2020 03:32:46 +0800 Subject: [PATCH] child_process: refactor to use more primordials PR-URL: https://github.com/nodejs/node/pull/36269 Reviewed-By: Antoine du Hamel --- lib/child_process.js | 77 ++++++++++++++++++++++------------- lib/internal/child_process.js | 58 ++++++++++++++------------ 2 files changed, 81 insertions(+), 54 deletions(-) diff --git a/lib/child_process.js b/lib/child_process.js index 24c5e809ad62c1..a39710b34a2ff0 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -23,13 +23,25 @@ const { ArrayIsArray, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypeJoin, + ArrayPrototypeLastIndexOf, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSort, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, Error, NumberIsInteger, ObjectAssign, ObjectDefineProperty, ObjectPrototypeHasOwnProperty, Promise, - Set, + RegExpPrototypeTest, + SafeSet, + StringPrototypeSlice, + StringPrototypeToUpperCase, } = primordials; const { @@ -94,15 +106,15 @@ function fork(modulePath /* , args, options */) { execArgv = options.execArgv || process.execArgv; if (execArgv === process.execArgv && process._eval != null) { - const index = execArgv.lastIndexOf(process._eval); + const index = ArrayPrototypeLastIndexOf(execArgv, process._eval); if (index > 0) { // Remove the -e switch to avoid fork bombing ourselves. - execArgv = execArgv.slice(); - execArgv.splice(index - 1, 2); + execArgv = ArrayPrototypeSlice(execArgv); + ArrayPrototypeSplice(execArgv, index - 1, 2); } } - args = execArgv.concat([modulePath], args); + args = [...execArgv, modulePath, ...args]; if (typeof options.stdio === 'string') { options.stdio = stdioStringToArray(options.stdio, 'ipc'); @@ -112,7 +124,7 @@ function fork(modulePath /* , args, options */) { options.stdio = stdioStringToArray( options.silent ? 'pipe' : 'inherit', 'ipc'); - } else if (!options.stdio.includes('ipc')) { + } else if (!ArrayPrototypeIncludes(options.stdio, 'ipc')) { throw new ERR_CHILD_PROCESS_IPC_REQUIRED('options.stdio'); } @@ -282,7 +294,7 @@ function execFile(file /* , args, options, callback */) { child.stdout && child.stdout.readableEncoding )) { - stdout = _stdout.join(''); + stdout = ArrayPrototypeJoin(_stdout, ''); } else { stdout = Buffer.concat(_stdout); } @@ -291,7 +303,7 @@ function execFile(file /* , args, options, callback */) { child.stderr && child.stderr.readableEncoding )) { - stderr = _stderr.join(''); + stderr = ArrayPrototypeJoin(_stderr, ''); } else { stderr = Buffer.concat(_stderr); } @@ -302,7 +314,7 @@ function execFile(file /* , args, options, callback */) { } if (args.length !== 0) - cmd += ` ${args.join(' ')}`; + cmd += ` ${ArrayPrototypeJoin(args, ' ')}`; if (!ex) { // eslint-disable-next-line no-restricted-syntax @@ -360,16 +372,18 @@ function execFile(file /* , args, options, callback */) { const length = encoding ? Buffer.byteLength(chunk, encoding) : chunk.length; + const slice = encoding ? StringPrototypeSlice : + (buf, ...args) => buf.slice(...args); stdoutLen += length; if (stdoutLen > options.maxBuffer) { const truncatedLen = options.maxBuffer - (stdoutLen - length); - _stdout.push(chunk.slice(0, truncatedLen)); + ArrayPrototypePush(_stdout, slice(chunk, 0, truncatedLen)); ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stdout'); kill(); } else { - _stdout.push(chunk); + ArrayPrototypePush(_stdout, chunk); } }); } @@ -387,7 +401,8 @@ function execFile(file /* , args, options, callback */) { if (stderrLen > options.maxBuffer) { const truncatedLen = options.maxBuffer - (stderrLen - length); - _stderr.push(chunk.slice(0, truncatedLen)); + ArrayPrototypePush(_stderr, + chunk.slice(0, truncatedLen)); ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stderr'); kill(); @@ -415,7 +430,7 @@ function normalizeSpawnArguments(file, args, options) { throw new ERR_INVALID_ARG_VALUE('file', file, 'cannot be empty'); if (ArrayIsArray(args)) { - args = args.slice(0); + args = ArrayPrototypeSlice(args); } else if (args == null) { args = []; } else if (typeof args !== 'object') { @@ -484,7 +499,7 @@ function normalizeSpawnArguments(file, args, options) { } if (options.shell) { - const command = [file].concat(args).join(' '); + const command = ArrayPrototypeJoin([file, ...args], ' '); // Set the shell, switches, and commands. if (process.platform === 'win32') { if (typeof options.shell === 'string') @@ -492,7 +507,7 @@ function normalizeSpawnArguments(file, args, options) { else file = process.env.comspec || 'cmd.exe'; // '/d /s /c' is used only for cmd.exe. - if (/^(?:.*\\)?cmd(?:\.exe)?$/i.test(file)) { + if (RegExpPrototypeTest(/^(?:.*\\)?cmd(?:\.exe)?$/i, file)) { args = ['/d', '/s', '/c', `"${command}"`]; windowsVerbatimArguments = true; } else { @@ -510,9 +525,9 @@ function normalizeSpawnArguments(file, args, options) { } if (typeof options.argv0 === 'string') { - args.unshift(options.argv0); + ArrayPrototypeUnshift(args, options.argv0); } else { - args.unshift(file); + ArrayPrototypeUnshift(args, file); } const env = options.env || process.env; @@ -528,27 +543,30 @@ function normalizeSpawnArguments(file, args, options) { let envKeys = []; // Prototype values are intentionally included. for (const key in env) { - envKeys.push(key); + ArrayPrototypePush(envKeys, key); } if (process.platform === 'win32') { // On Windows env keys are case insensitive. Filter out duplicates, // keeping only the first one (in lexicographic order) - const sawKey = new Set(); - envKeys = envKeys.sort().filter((key) => { - const uppercaseKey = key.toUpperCase(); - if (sawKey.has(uppercaseKey)) { - return false; + const sawKey = new SafeSet(); + envKeys = ArrayPrototypeFilter( + ArrayPrototypeSort(envKeys), + (key) => { + const uppercaseKey = StringPrototypeToUpperCase(key); + if (sawKey.has(uppercaseKey)) { + return false; + } + sawKey.add(uppercaseKey); + return true; } - sawKey.add(uppercaseKey); - return true; - }); + ); } for (const key of envKeys) { const value = env[key]; if (value !== undefined) { - envPairs.push(`${key}=${value}`); + ArrayPrototypePush(envPairs, `${key}=${value}`); } } @@ -629,7 +647,7 @@ function checkExecSyncError(ret, args, cmd) { err = ret.error; } else if (ret.status !== 0) { let msg = 'Command failed: '; - msg += cmd || args.join(' '); + msg += cmd || ArrayPrototypeJoin(args, ' '); if (ret.stderr && ret.stderr.length > 0) msg += `\n${ret.stderr.toString()}`; // eslint-disable-next-line no-restricted-syntax @@ -646,7 +664,8 @@ function execFileSync(command, args, options) { options = normalizeSpawnArguments(command, args, options); const inheritStderr = !options.stdio; - const ret = spawnSync(options.file, options.args.slice(1), options); + const ret = spawnSync(options.file, + ArrayPrototypeSlice(options.args, 1), options); if (inheritStderr && ret.stderr) process.stderr.write(ret.stderr); diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 76e67317b93357..8dd85d287fc950 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -2,8 +2,15 @@ const { ArrayIsArray, + ArrayPrototypePush, + ArrayPrototypeReduce, + ArrayPrototypeSlice, + FunctionPrototype, + FunctionPrototypeCall, ObjectDefineProperty, ObjectSetPrototypeOf, + ReflectApply, + StringPrototypeSlice, Symbol, Uint8Array, } = primordials; @@ -224,19 +231,19 @@ function stdioStringToArray(stdio, channel) { switch (stdio) { case 'ignore': - case 'pipe': options.push(stdio, stdio, stdio); break; - case 'inherit': options.push(0, 1, 2); break; + case 'pipe': ArrayPrototypePush(options, stdio, stdio, stdio); break; + case 'inherit': ArrayPrototypePush(options, 0, 1, 2); break; default: throw new ERR_INVALID_ARG_VALUE('stdio', stdio); } - if (channel) options.push(channel); + if (channel) ArrayPrototypePush(options, channel); return options; } function ChildProcess() { - EventEmitter.call(this); + FunctionPrototypeCall(EventEmitter, this); this._closesNeeded = 1; this._closesGot = 0; @@ -271,7 +278,7 @@ function ChildProcess() { if (this.spawnfile) err.path = this.spawnfile; - err.spawnargs = this.spawnargs.slice(1); + err.spawnargs = ArrayPrototypeSlice(this.spawnargs, 1); this.emit('error', err); } else { this.emit('exit', this.exitCode, this.signalCode); @@ -361,8 +368,9 @@ ChildProcess.prototype.spawn = function(options) { options.envPairs); } - options.envPairs.push(`NODE_CHANNEL_FD=${ipcFd}`); - options.envPairs.push(`NODE_CHANNEL_SERIALIZATION_MODE=${serialization}`); + ArrayPrototypePush(options.envPairs, `NODE_CHANNEL_FD=${ipcFd}`); + ArrayPrototypePush(options.envPairs, + `NODE_CHANNEL_SERIALIZATION_MODE=${serialization}`); } validateString(options.file, 'options.file'); @@ -454,7 +462,8 @@ ChildProcess.prototype.spawn = function(options) { this.stdio = []; for (i = 0; i < stdio.length; i++) - this.stdio.push(stdio[i].socket === undefined ? null : stdio[i].socket); + ArrayPrototypePush(this.stdio, + stdio[i].socket === undefined ? null : stdio[i].socket); // Add .send() method and start listening for IPC data if (ipc !== undefined) setupChannel(this, ipc, serialization); @@ -768,7 +777,7 @@ function setupChannel(target, channel, serializationMode) { // Queue-up message and handle if we haven't received ACK yet. if (this._handleQueue) { - this._handleQueue.push({ + ArrayPrototypePush(this._handleQueue, { callback: callback, handle: handle, options: options, @@ -780,10 +789,8 @@ function setupChannel(target, channel, serializationMode) { obj = handleConversion[message.type]; // convert TCP object to native handle object - handle = handleConversion[message.type].send.call(target, - message, - handle, - options); + handle = ReflectApply(handleConversion[message.type].send, + target, [message, handle, options]); // If handle was sent twice, or it is impossible to get native handle // out of it - just send a text without the handle. @@ -798,7 +805,7 @@ function setupChannel(target, channel, serializationMode) { !(message && (message.cmd === 'NODE_HANDLE_ACK' || message.cmd === 'NODE_HANDLE_NACK'))) { // Queue request anyway to avoid out-of-order messages. - this._handleQueue.push({ + ArrayPrototypePush(this._handleQueue, { callback: callback, handle: null, options: options, @@ -929,10 +936,11 @@ function isInternal(message) { typeof message === 'object' && typeof message.cmd === 'string' && message.cmd.length > INTERNAL_PREFIX.length && - message.cmd.slice(0, INTERNAL_PREFIX.length) === INTERNAL_PREFIX); + StringPrototypeSlice(message.cmd, 0, INTERNAL_PREFIX.length) === + INTERNAL_PREFIX); } -function nop() { } +const nop = FunctionPrototype; function getValidStdio(stdio, sync) { let ipc; @@ -949,11 +957,11 @@ function getValidStdio(stdio, sync) { // Don't concat() a new Array() because it would be sparse, and // stdio.reduce() would skip the sparse elements of stdio. // See https://stackoverflow.com/a/5501711/3561 - while (stdio.length < 3) stdio.push(undefined); + while (stdio.length < 3) ArrayPrototypePush(stdio, undefined); // Translate stdio into C++-readable form // (i.e. PipeWraps or fds) - stdio = stdio.reduce((acc, stdio, i) => { + stdio = ArrayPrototypeReduce(stdio, (acc, stdio, i) => { function cleanup() { for (let i = 0; i < acc.length; i++) { if ((acc[i].type === 'pipe' || acc[i].type === 'ipc') && acc[i].handle) @@ -967,7 +975,7 @@ function getValidStdio(stdio, sync) { } if (stdio === 'ignore') { - acc.push({ type: 'ignore' }); + ArrayPrototypePush(acc, { type: 'ignore' }); } else if (stdio === 'pipe' || (typeof stdio === 'number' && stdio < 0)) { const a = { type: 'pipe', @@ -978,7 +986,7 @@ function getValidStdio(stdio, sync) { if (!sync) a.handle = new Pipe(PipeConstants.SOCKET); - acc.push(a); + ArrayPrototypePush(acc, a); } else if (stdio === 'ipc') { if (sync || ipc !== undefined) { // Cleanup previously created pipes @@ -992,18 +1000,18 @@ function getValidStdio(stdio, sync) { ipc = new Pipe(PipeConstants.IPC); ipcFd = i; - acc.push({ + ArrayPrototypePush(acc, { type: 'pipe', handle: ipc, ipc: true }); } else if (stdio === 'inherit') { - acc.push({ + ArrayPrototypePush(acc, { type: 'inherit', fd: i }); } else if (typeof stdio === 'number' || typeof stdio.fd === 'number') { - acc.push({ + ArrayPrototypePush(acc, { type: 'fd', fd: typeof stdio === 'number' ? stdio : stdio.fd }); @@ -1013,7 +1021,7 @@ function getValidStdio(stdio, sync) { stdio : getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle; - acc.push({ + ArrayPrototypePush(acc, { type: 'wrap', wrapType: getHandleWrapType(handle), handle: handle, @@ -1073,7 +1081,7 @@ function spawnSync(options) { if (result.error) { result.error = errnoException(result.error, 'spawnSync ' + options.file); result.error.path = options.file; - result.error.spawnargs = options.args.slice(1); + result.error.spawnargs = ArrayPrototypeSlice(options.args, 1); } return result;