child_process: simplify spawn argument parsing
This commit simplifies the object returned by normalizeSpawnArguments(). This does impact monkey patching, as illustrated by the changes in tests. PR-URL: https://github.com/nodejs/node/pull/27854 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
476c3ec750
commit
4f9cd2770a
@ -460,16 +460,14 @@ function normalizeSpawnArguments(file, args, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate windowsVerbatimArguments, if present.
|
// Validate windowsVerbatimArguments, if present.
|
||||||
if (options.windowsVerbatimArguments != null &&
|
let { windowsVerbatimArguments } = options;
|
||||||
typeof options.windowsVerbatimArguments !== 'boolean') {
|
if (windowsVerbatimArguments != null &&
|
||||||
|
typeof windowsVerbatimArguments !== 'boolean') {
|
||||||
throw new ERR_INVALID_ARG_TYPE('options.windowsVerbatimArguments',
|
throw new ERR_INVALID_ARG_TYPE('options.windowsVerbatimArguments',
|
||||||
'boolean',
|
'boolean',
|
||||||
options.windowsVerbatimArguments);
|
windowsVerbatimArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a shallow copy so we don't clobber the user's options object.
|
|
||||||
options = { ...options };
|
|
||||||
|
|
||||||
if (options.shell) {
|
if (options.shell) {
|
||||||
const command = [file].concat(args).join(' ');
|
const command = [file].concat(args).join(' ');
|
||||||
// Set the shell, switches, and commands.
|
// Set the shell, switches, and commands.
|
||||||
@ -481,7 +479,7 @@ function normalizeSpawnArguments(file, args, options) {
|
|||||||
// '/d /s /c' is used only for cmd.exe.
|
// '/d /s /c' is used only for cmd.exe.
|
||||||
if (/^(?:.*\\)?cmd(?:\.exe)?$/i.test(file)) {
|
if (/^(?:.*\\)?cmd(?:\.exe)?$/i.test(file)) {
|
||||||
args = ['/d', '/s', '/c', `"${command}"`];
|
args = ['/d', '/s', '/c', `"${command}"`];
|
||||||
options.windowsVerbatimArguments = true;
|
windowsVerbatimArguments = true;
|
||||||
} else {
|
} else {
|
||||||
args = ['-c', command];
|
args = ['-c', command];
|
||||||
}
|
}
|
||||||
@ -521,47 +519,35 @@ function normalizeSpawnArguments(file, args, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
file: file,
|
// Make a shallow copy so we don't clobber the user's options object.
|
||||||
args: args,
|
...options,
|
||||||
options: options,
|
args,
|
||||||
envPairs: envPairs
|
detached: !!options.detached,
|
||||||
|
envPairs,
|
||||||
|
file,
|
||||||
|
windowsHide: !!options.windowsHide,
|
||||||
|
windowsVerbatimArguments: !!windowsVerbatimArguments
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var spawn = exports.spawn = function spawn(file, args, options) {
|
var spawn = exports.spawn = function spawn(file, args, options) {
|
||||||
const opts = normalizeSpawnArguments(file, args, options);
|
|
||||||
const child = new ChildProcess();
|
const child = new ChildProcess();
|
||||||
|
|
||||||
options = opts.options;
|
options = normalizeSpawnArguments(file, args, options);
|
||||||
debug('spawn', opts.args, options);
|
debug('spawn', options);
|
||||||
|
child.spawn(options);
|
||||||
child.spawn({
|
|
||||||
file: opts.file,
|
|
||||||
args: opts.args,
|
|
||||||
cwd: options.cwd,
|
|
||||||
windowsHide: !!options.windowsHide,
|
|
||||||
windowsVerbatimArguments: !!options.windowsVerbatimArguments,
|
|
||||||
detached: !!options.detached,
|
|
||||||
envPairs: opts.envPairs,
|
|
||||||
stdio: options.stdio,
|
|
||||||
uid: options.uid,
|
|
||||||
gid: options.gid
|
|
||||||
});
|
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
function spawnSync(file, args, options) {
|
function spawnSync(file, args, options) {
|
||||||
const opts = normalizeSpawnArguments(file, args, options);
|
options = {
|
||||||
|
|
||||||
const defaults = {
|
|
||||||
maxBuffer: MAX_BUFFER,
|
maxBuffer: MAX_BUFFER,
|
||||||
...opts.options
|
...normalizeSpawnArguments(file, args, options)
|
||||||
};
|
};
|
||||||
options = opts.options = defaults;
|
|
||||||
|
|
||||||
debug('spawnSync', opts.args, options);
|
debug('spawnSync', options);
|
||||||
|
|
||||||
// Validate the timeout, if present.
|
// Validate the timeout, if present.
|
||||||
validateTimeout(options.timeout);
|
validateTimeout(options.timeout);
|
||||||
@ -569,10 +555,6 @@ function spawnSync(file, args, options) {
|
|||||||
// Validate maxBuffer, if present.
|
// Validate maxBuffer, if present.
|
||||||
validateMaxBuffer(options.maxBuffer);
|
validateMaxBuffer(options.maxBuffer);
|
||||||
|
|
||||||
options.file = opts.file;
|
|
||||||
options.args = opts.args;
|
|
||||||
options.envPairs = opts.envPairs;
|
|
||||||
|
|
||||||
// Validate and translate the kill signal, if present.
|
// Validate and translate the kill signal, if present.
|
||||||
options.killSignal = sanitizeKillSignal(options.killSignal);
|
options.killSignal = sanitizeKillSignal(options.killSignal);
|
||||||
|
|
||||||
@ -603,7 +585,7 @@ function spawnSync(file, args, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return child_process.spawnSync(opts);
|
return child_process.spawnSync(options);
|
||||||
}
|
}
|
||||||
exports.spawnSync = spawnSync;
|
exports.spawnSync = spawnSync;
|
||||||
|
|
||||||
@ -628,15 +610,15 @@ function checkExecSyncError(ret, args, cmd) {
|
|||||||
|
|
||||||
|
|
||||||
function execFileSync(command, args, options) {
|
function execFileSync(command, args, options) {
|
||||||
const opts = normalizeSpawnArguments(command, args, options);
|
options = normalizeSpawnArguments(command, args, options);
|
||||||
const inheritStderr = !opts.options.stdio;
|
|
||||||
|
|
||||||
const ret = spawnSync(opts.file, opts.args.slice(1), opts.options);
|
const inheritStderr = !options.stdio;
|
||||||
|
const ret = spawnSync(options.file, options.args.slice(1), options);
|
||||||
|
|
||||||
if (inheritStderr && ret.stderr)
|
if (inheritStderr && ret.stderr)
|
||||||
process.stderr.write(ret.stderr);
|
process.stderr.write(ret.stderr);
|
||||||
|
|
||||||
const err = checkExecSyncError(ret, opts.args, undefined);
|
const err = checkExecSyncError(ret, options.args, undefined);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
@ -1022,8 +1022,7 @@ function maybeClose(subprocess) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnSync(opts) {
|
function spawnSync(options) {
|
||||||
const options = opts.options;
|
|
||||||
const result = spawn_sync.spawn(options);
|
const result = spawn_sync.spawn(options);
|
||||||
|
|
||||||
if (result.output && options.encoding && options.encoding !== 'buffer') {
|
if (result.output && options.encoding && options.encoding !== 'buffer') {
|
||||||
@ -1038,9 +1037,9 @@ function spawnSync(opts) {
|
|||||||
result.stderr = result.output && result.output[2];
|
result.stderr = result.output && result.output[2];
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
result.error = errnoException(result.error, 'spawnSync ' + opts.file);
|
result.error = errnoException(result.error, 'spawnSync ' + options.file);
|
||||||
result.error.path = opts.file;
|
result.error.path = options.file;
|
||||||
result.error.spawnargs = opts.args.slice(1);
|
result.error.spawnargs = options.args.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -36,7 +36,7 @@ if (process.argv[2] === 'child') {
|
|||||||
// Verify that the default kill signal is SIGTERM.
|
// Verify that the default kill signal is SIGTERM.
|
||||||
{
|
{
|
||||||
const child = spawn(undefined, (opts) => {
|
const child = spawn(undefined, (opts) => {
|
||||||
assert.strictEqual(opts.options.killSignal, undefined);
|
assert.strictEqual(opts.killSignal, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(child.signal, 'SIGTERM');
|
assert.strictEqual(child.signal, 'SIGTERM');
|
||||||
@ -45,7 +45,7 @@ if (process.argv[2] === 'child') {
|
|||||||
// Verify that a string signal name is handled properly.
|
// Verify that a string signal name is handled properly.
|
||||||
{
|
{
|
||||||
const child = spawn('SIGKILL', (opts) => {
|
const child = spawn('SIGKILL', (opts) => {
|
||||||
assert.strictEqual(opts.options.killSignal, SIGKILL);
|
assert.strictEqual(opts.killSignal, SIGKILL);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(child.signal, 'SIGKILL');
|
assert.strictEqual(child.signal, 'SIGKILL');
|
||||||
@ -56,7 +56,7 @@ if (process.argv[2] === 'child') {
|
|||||||
assert.strictEqual(typeof SIGKILL, 'number');
|
assert.strictEqual(typeof SIGKILL, 'number');
|
||||||
|
|
||||||
const child = spawn(SIGKILL, (opts) => {
|
const child = spawn(SIGKILL, (opts) => {
|
||||||
assert.strictEqual(opts.options.killSignal, SIGKILL);
|
assert.strictEqual(opts.killSignal, SIGKILL);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(child.signal, 'SIGKILL');
|
assert.strictEqual(child.signal, 'SIGKILL');
|
||||||
|
@ -64,12 +64,12 @@ assert.strictEqual(env.stdout.toString().trim(), 'buzz');
|
|||||||
assert.strictEqual(opts.file, shellOutput);
|
assert.strictEqual(opts.file, shellOutput);
|
||||||
assert.deepStrictEqual(opts.args,
|
assert.deepStrictEqual(opts.args,
|
||||||
[shellOutput, ...shellFlags, outputCmd]);
|
[shellOutput, ...shellFlags, outputCmd]);
|
||||||
assert.strictEqual(opts.options.shell, shell);
|
assert.strictEqual(opts.shell, shell);
|
||||||
assert.strictEqual(opts.options.file, opts.file);
|
assert.strictEqual(opts.file, opts.file);
|
||||||
assert.deepStrictEqual(opts.options.args, opts.args);
|
assert.deepStrictEqual(opts.args, opts.args);
|
||||||
assert.strictEqual(opts.options.windowsHide, undefined);
|
assert.strictEqual(opts.windowsHide, false);
|
||||||
assert.strictEqual(opts.options.windowsVerbatimArguments,
|
assert.strictEqual(opts.windowsVerbatimArguments,
|
||||||
windowsVerbatim);
|
!!windowsVerbatim);
|
||||||
});
|
});
|
||||||
cp.spawnSync(cmd, { shell });
|
cp.spawnSync(cmd, { shell });
|
||||||
internalCp.spawnSync = oldSpawnSync;
|
internalCp.spawnSync = oldSpawnSync;
|
||||||
|
@ -19,7 +19,7 @@ internalCp.ChildProcess.prototype.spawn = common.mustCall(function(options) {
|
|||||||
}, 2);
|
}, 2);
|
||||||
|
|
||||||
internalCp.spawnSync = common.mustCall(function(options) {
|
internalCp.spawnSync = common.mustCall(function(options) {
|
||||||
assert.strictEqual(options.options.windowsHide, true);
|
assert.strictEqual(options.windowsHide, true);
|
||||||
return originalSpawnSync.apply(this, arguments);
|
return originalSpawnSync.apply(this, arguments);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user