Browse Source

errors, child_process: migrate to using internal/errors

PR-URL: https://github.com/nodejs/node/pull/11300
Ref: https://github.com/nodejs/node/issues/11273
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
v6
James M Snell 8 years ago
parent
commit
76327613af
  1. 61
      doc/api/errors.md
  2. 28
      lib/internal/child_process.js
  3. 17
      lib/internal/errors.js
  4. 3
      lib/internal/util.js
  5. 4
      test/parallel/test-child-process-constructor.js
  6. 2
      test/parallel/test-child-process-send-type-error.js
  7. 2
      test/parallel/test-child-process-spawnsync-kill-signal.js
  8. 3
      test/parallel/test-child-process-spawnsync-validation-errors.js
  9. 2
      test/parallel/test-child-process-stdio.js
  10. 18
      test/parallel/test-child-process-validate-stdio.js

61
doc/api/errors.md

@ -599,6 +599,27 @@ Node.js API that consumes `file:` URLs (such as certain functions in the
[`fs`][] module) encounters a file URL with an incompatible path. The exact [`fs`][] module) encounters a file URL with an incompatible path. The exact
semantics for determining whether a path can be used is platform-dependent. semantics for determining whether a path can be used is platform-dependent.
<a id="ERR_INVALID_HANDLE_TYPE"></a>
### ERR_INVALID_HANDLE_TYPE
The '`ERR_INVALID_HANDLE_TYPE`' error code is used when an attempt is made to
send an unsupported "handle" over an IPC communication channel to a child
process. See [`child.send()`] and [`process.send()`] for more information.
<a id="ERR_INVALID_OPT_VALUE"></a>
### ERR_INVALID_OPT_VALUE
The `'ERR_INVALID_OPT_VALUE'` error code is used generically to identify when
an invalid or unexpected value has been passed in an options object.
<a id="ERR_INVALID_SYNC_FORK_INPUT"></a>
### ERR_INVALID_SYNC_FORK_INPUT
The `'ERR_INVALID_SYNC_FORK_INPUT'` error code is used when a `Buffer`,
`Uint8Array` or `string` is provided as stdio input to a synchronous
fork. See the documentation for the [`child_process`](child_process.html)
module for more information.
<a id="ERR_INVALID_THIS"></a> <a id="ERR_INVALID_THIS"></a>
### ERR_INVALID_THIS ### ERR_INVALID_THIS
@ -642,6 +663,36 @@ It is currently only used in the [WHATWG URL API][] support in the [`fs`][]
module (which only accepts URLs with `'file'` scheme), but may be used in other module (which only accepts URLs with `'file'` scheme), but may be used in other
Node.js APIs as well in the future. Node.js APIs as well in the future.
<a id="ERR_IPC_CHANNEL_CLOSED"></a>
### ERR_IPC_CHANNEL_CLOSED
The `'ERR_IPC_CHANNEL_CLOSED'` error code is used when an attempt is made to use
an IPC communication channel that has already been closed.
<a id="ERR_IPC_DISCONNECTED"></a>
### ERR_IPC_DISCONNECTED
The `'ERR_IPC_DISCONNECTED'` error code is used when an attempt is made to
disconnect an already disconnected IPC communication channel between two
Node.js processes. See the documentation for the
[`child_process`](child_process.html) module for more information.
<a id="ERR_IPC_ONE_PIPE"></a>
### ERR_IPC_ONE_PIPE
The `'ERR_IPC_ONE_PIPE'` error code is used when an attempt is made to create
a child Node.js process using more than one IPC communication channel.
See the documentation for the [`child_process`](child_process.html)
module for more information.
<a id="ERR_IPC_SYNC_FORK"></a>
### ERR_IPC_SYNC_FORK
The `'ERR_IPC_SYNC_FORK'` error code is used when an attempt is made to open
an IPC communication channel with a synchronous forked Node.js process.
See the documentation for the [`child_process`](child_process.html)
module for more information.
<a id="ERR_MISSING_ARGS"></a> <a id="ERR_MISSING_ARGS"></a>
### ERR_MISSING_ARGS ### ERR_MISSING_ARGS
@ -674,6 +725,13 @@ kind of internal Node.js error that should not typically be triggered by user
code. Instances of this error point to an internal bug within the Node.js code. Instances of this error point to an internal bug within the Node.js
binary itself. binary itself.
<a id="ERR_UNKNOWN_SIGNAL"></a>
### ERR_UNKNOWN_SIGNAL
The `'ERR_UNKNOWN_SIGNAL`' error code is used when an invalid or unknown
process signal is passed to an API expecting a valid signal (such as
[`child.kill()`][]).
<a id="ERR_UNKNOWN_STDIN_TYPE"></a> <a id="ERR_UNKNOWN_STDIN_TYPE"></a>
### ERR_UNKNOWN_STDIN_TYPE ### ERR_UNKNOWN_STDIN_TYPE
@ -693,6 +751,9 @@ in user code, although it is not impossible. Occurrences of this error are most
likely an indication of a bug within Node.js itself. likely an indication of a bug within Node.js itself.
[`child.kill()`]: child_process.html#child_process_child_kill_signal
[`child.send()`]: child_process.html#child_process_child_send_message_sendhandle_options_callback
[`process.send()`]: process.html#process_process_send_message_sendhandle_options_callback
[`fs.readdir`]: fs.html#fs_fs_readdir_path_options_callback [`fs.readdir`]: fs.html#fs_fs_readdir_path_options_callback
[`fs.readFileSync`]: fs.html#fs_fs_readfilesync_file_options [`fs.readFileSync`]: fs.html#fs_fs_readfilesync_file_options
[`fs.unlink`]: fs.html#fs_fs_unlink_path_callback [`fs.unlink`]: fs.html#fs_fs_unlink_path_callback

28
lib/internal/child_process.js

@ -1,5 +1,6 @@
'use strict'; 'use strict';
const errors = require('internal/errors');
const StringDecoder = require('string_decoder').StringDecoder; const StringDecoder = require('string_decoder').StringDecoder;
const EventEmitter = require('events'); const EventEmitter = require('events');
const net = require('net'); const net = require('net');
@ -367,6 +368,7 @@ function onErrorNT(self, err) {
ChildProcess.prototype.kill = function(sig) { ChildProcess.prototype.kill = function(sig) {
const signal = sig === 0 ? sig : const signal = sig === 0 ? sig :
convertToValidSignal(sig === undefined ? 'SIGTERM' : sig); convertToValidSignal(sig === undefined ? 'SIGTERM' : sig);
@ -538,7 +540,7 @@ function setupChannel(target, channel) {
options = undefined; options = undefined;
} else if (options !== undefined && } else if (options !== undefined &&
(options === null || typeof options !== 'object')) { (options === null || typeof options !== 'object')) {
throw new TypeError('"options" argument must be an object'); throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
} }
options = Object.assign({swallowErrors: false}, options); options = Object.assign({swallowErrors: false}, options);
@ -546,7 +548,7 @@ function setupChannel(target, channel) {
if (this.connected) { if (this.connected) {
return this._send(message, handle, options, callback); return this._send(message, handle, options, callback);
} }
const ex = new Error('channel closed'); const ex = new errors.Error('ERR_IPC_CHANNEL_CLOSED');
if (typeof callback === 'function') { if (typeof callback === 'function') {
process.nextTick(callback, ex); process.nextTick(callback, ex);
} else { } else {
@ -559,7 +561,7 @@ function setupChannel(target, channel) {
assert(this.connected || this.channel); assert(this.connected || this.channel);
if (message === undefined) if (message === undefined)
throw new TypeError('"message" argument cannot be undefined'); throw new errors.TypeError('ERR_MISSING_ARGS', 'message');
// Support legacy function signature // Support legacy function signature
if (typeof options === 'boolean') { if (typeof options === 'boolean') {
@ -586,7 +588,7 @@ function setupChannel(target, channel) {
} else if (handle instanceof UDP) { } else if (handle instanceof UDP) {
message.type = 'dgram.Native'; message.type = 'dgram.Native';
} else { } else {
throw new TypeError('This handle type can\'t be sent'); throw new errors.TypeError('ERR_INVALID_HANDLE_TYPE');
} }
// Queue-up message and handle if we haven't received ACK yet. // Queue-up message and handle if we haven't received ACK yet.
@ -686,7 +688,7 @@ function setupChannel(target, channel) {
target.disconnect = function() { target.disconnect = function() {
if (!this.connected) { if (!this.connected) {
this.emit('error', new Error('IPC channel is already disconnected')); this.emit('error', new errors.Error('ERR_IPC_DISCONNECTED'));
return; return;
} }
@ -766,11 +768,12 @@ function _validateStdio(stdio, sync) {
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break; case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break; case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
case 'inherit': stdio = [0, 1, 2]; break; case 'inherit': stdio = [0, 1, 2]; break;
default: throw new TypeError('Incorrect value of stdio option: ' + stdio); default:
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio', stdio);
} }
} else if (!Array.isArray(stdio)) { } else if (!Array.isArray(stdio)) {
throw new TypeError('Incorrect value of stdio option: ' + throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
util.inspect(stdio)); 'stdio', util.inspect(stdio));
} }
// At least 3 stdio will be created // At least 3 stdio will be created
@ -812,9 +815,9 @@ function _validateStdio(stdio, sync) {
// Cleanup previously created pipes // Cleanup previously created pipes
cleanup(); cleanup();
if (!sync) if (!sync)
throw new Error('Child process can have only one IPC pipe'); throw new errors.Error('ERR_IPC_ONE_PIPE');
else else
throw new Error('You cannot use IPC with synchronous forks'); throw new errors.Error('ERR_IPC_SYNC_FORK');
} }
ipc = new Pipe(true); ipc = new Pipe(true);
@ -849,14 +852,13 @@ function _validateStdio(stdio, sync) {
} else if (isUint8Array(stdio) || typeof stdio === 'string') { } else if (isUint8Array(stdio) || typeof stdio === 'string') {
if (!sync) { if (!sync) {
cleanup(); cleanup();
throw new TypeError('Asynchronous forks do not support ' + throw new errors.TypeError('ERR_INVALID_SYNC_FORK_INPUT',
'Buffer, Uint8Array or string input: ' +
util.inspect(stdio)); util.inspect(stdio));
} }
} else { } else {
// Cleanup // Cleanup
cleanup(); cleanup();
throw new TypeError('Incorrect value for stdio stream: ' + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio',
util.inspect(stdio)); util.inspect(stdio));
} }

17
lib/internal/errors.js

@ -91,17 +91,32 @@ E('ERR_INVALID_ARG_TYPE', invalidArgType);
E('ERR_INVALID_CALLBACK', 'callback must be a function'); E('ERR_INVALID_CALLBACK', 'callback must be a function');
E('ERR_INVALID_FILE_URL_HOST', 'File URL host %s'); E('ERR_INVALID_FILE_URL_HOST', 'File URL host %s');
E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s'); E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s');
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent');
E('ERR_INVALID_OPT_VALUE',
(name, value) => {
return `The value "${String(value)}" is invalid for option "${name}"`;
});
E('ERR_INVALID_SYNC_FORK_INPUT',
(value) => {
return 'Asynchronous forks do not support Buffer, Uint8Array or string' +
`input: ${value}`;
});
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s'); E('ERR_INVALID_THIS', 'Value of "this" must be of type %s');
E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple'); E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple');
E('ERR_INVALID_URL', 'Invalid URL: %s'); E('ERR_INVALID_URL', 'Invalid URL: %s');
E('ERR_INVALID_URL_SCHEME', E('ERR_INVALID_URL_SCHEME',
(expected) => `The URL must be ${oneOf(expected, 'scheme')}`); (expected) => `The URL must be ${oneOf(expected, 'scheme')}`);
E('ERR_IPC_CHANNEL_CLOSED', 'channel closed');
E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected');
E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe');
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks');
E('ERR_MISSING_ARGS', missingArgs); E('ERR_MISSING_ARGS', missingArgs);
E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed'); E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed');
E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed'); E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed');
E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
E('ERR_UNKNOWN_SIGNAL', (signal) => `Unknown signal: ${signal}`);
E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type'); E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type');
E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type'); E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type');
E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
// Add new errors from here... // Add new errors from here...
function invalidArgType(name, expected, actual) { function invalidArgType(name, expected, actual) {

3
lib/internal/util.js

@ -1,5 +1,6 @@
'use strict'; 'use strict';
const errors = require('internal/errors');
const binding = process.binding('util'); const binding = process.binding('util');
const signals = process.binding('constants').os.signals; const signals = process.binding('constants').os.signals;
@ -177,7 +178,7 @@ function convertToValidSignal(signal) {
if (signalName) return signalName; if (signalName) return signalName;
} }
throw new Error('Unknown signal: ' + signal); throw new errors.Error('ERR_UNKNOWN_SIGNAL', signal);
} }
function getConstructorOf(obj) { function getConstructorOf(obj) {

4
test/parallel/test-child-process-constructor.js

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const { ChildProcess } = require('child_process'); const { ChildProcess } = require('child_process');
assert.strictEqual(typeof ChildProcess, 'function'); assert.strictEqual(typeof ChildProcess, 'function');
@ -64,6 +64,6 @@ assert(Number.isInteger(child.pid));
// try killing with invalid signal // try killing with invalid signal
assert.throws(() => { assert.throws(() => {
child.kill('foo'); child.kill('foo');
}, /^Error: Unknown signal: foo$/); }, common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' }));
assert.strictEqual(child.kill(), true); assert.strictEqual(child.kill(), true);

2
test/parallel/test-child-process-send-type-error.js

@ -6,7 +6,7 @@ const cp = require('child_process');
function fail(proc, args) { function fail(proc, args) {
assert.throws(() => { assert.throws(() => {
proc.send.apply(proc, args); proc.send.apply(proc, args);
}, /"options" argument must be an object/); }, common.expectsError({code: 'ERR_INVALID_ARG_TYPE', type: TypeError}));
} }
let target = process; let target = process;

2
test/parallel/test-child-process-spawnsync-kill-signal.js

@ -21,7 +21,7 @@ if (process.argv[2] === 'child') {
// Verify that an error is thrown for unknown signals. // Verify that an error is thrown for unknown signals.
assert.throws(() => { assert.throws(() => {
spawn('SIG_NOT_A_REAL_SIGNAL'); spawn('SIG_NOT_A_REAL_SIGNAL');
}, /Error: Unknown signal: SIG_NOT_A_REAL_SIGNAL/); }, common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' }));
// Verify that the default kill signal is SIGTERM. // Verify that the default kill signal is SIGTERM.
{ {

3
test/parallel/test-child-process-spawnsync-validation-errors.js

@ -185,7 +185,8 @@ if (!common.isWindows) {
{ {
// Validate the killSignal option // Validate the killSignal option
const typeErr = /^TypeError: "killSignal" must be a string or number$/; const typeErr = /^TypeError: "killSignal" must be a string or number$/;
const unknownSignalErr = /^Error: Unknown signal:/; const unknownSignalErr =
common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' });
pass('killSignal', undefined); pass('killSignal', undefined);
pass('killSignal', null); pass('killSignal', null);

2
test/parallel/test-child-process-stdio.js

@ -42,4 +42,4 @@ assert.deepStrictEqual(options, {stdio: 'ignore'});
assert.throws(() => { assert.throws(() => {
common.spawnPwd({stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc']}); common.spawnPwd({stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc']});
}, /^Error: Child process can have only one IPC pipe$/); }, common.expectsError({code: 'ERR_IPC_ONE_PIPE', type: Error}));

18
test/parallel/test-child-process-validate-stdio.js

@ -1,19 +1,18 @@
'use strict'; 'use strict';
// Flags: --expose_internals // Flags: --expose_internals
require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const _validateStdio = require('internal/child_process')._validateStdio; const _validateStdio = require('internal/child_process')._validateStdio;
const expectedError =
common.expectsError({code: 'ERR_INVALID_OPT_VALUE', type: TypeError});
// should throw if string and not ignore, pipe, or inherit // should throw if string and not ignore, pipe, or inherit
assert.throws(function() { assert.throws(() => _validateStdio('foo'), expectedError);
_validateStdio('foo');
}, /Incorrect value of stdio option/);
// should throw if not a string or array // should throw if not a string or array
assert.throws(function() { assert.throws(() => _validateStdio(600), expectedError);
_validateStdio(600);
}, /Incorrect value of stdio option/);
// should populate stdio with undefined if len < 3 // should populate stdio with undefined if len < 3
{ {
@ -27,9 +26,8 @@ assert.throws(function() {
// should throw if stdio has ipc and sync is true // should throw if stdio has ipc and sync is true
const stdio2 = ['ipc', 'ipc', 'ipc']; const stdio2 = ['ipc', 'ipc', 'ipc'];
assert.throws(function() { assert.throws(() => _validateStdio(stdio2, true),
_validateStdio(stdio2, true); common.expectsError({code: 'ERR_IPC_SYNC_FORK', type: Error}));
}, /You cannot use IPC with synchronous forks/);
{ {
const stdio3 = [process.stdin, process.stdout, process.stderr]; const stdio3 = [process.stdin, process.stdout, process.stderr];

Loading…
Cancel
Save