From 0496a78e164bcd9b5ee26a6555dc4bd4ae667ac4 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 13:09:34 -0700 Subject: [PATCH 1/9] errors: add internal/errors.js Add the internal/errors.js core mechanism. --- lib/internal/errors.js | 120 ++++++++++++++++++++++++++ node.gyp | 1 + test/parallel/test-internal-errors.js | 37 ++++++++ 3 files changed, 158 insertions(+) create mode 100644 lib/internal/errors.js create mode 100644 test/parallel/test-internal-errors.js diff --git a/lib/internal/errors.js b/lib/internal/errors.js new file mode 100644 index 00000000000000..e3fa38de4144b3 --- /dev/null +++ b/lib/internal/errors.js @@ -0,0 +1,120 @@ +'use strict'; + +// The whole point behind this internal module is to allow Node.js to no +// longer be forced to treat every error message change as a semver-major +// change. The NodeError classes here all expose a `code` property whose +// value identifies the error. + +// Note: the lib/_stream_readable.js and lib/_stream_writable.js files +// include their own alternatives to this because those need to be portable +// to the readable-stream standalone module. If the logic here changes at all, +// then those also need to be checked. + +const kCode = Symbol('code'); + +class NodeError extends Error { + constructor(key /**, ...args **/) { + const args = Array(arguments.length - 1); + for (var n = 1; n < arguments.length; n++) + args[n - 1] = arguments[n]; + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeError); + } + + get name() { + return `Error[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +class NodeTypeError extends TypeError { + constructor(key /**, ...args **/) { + const args = Array(arguments.length - 1); + for (var n = 1; n < arguments.length; n++) + args[n - 1] = arguments[n]; + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeTypeError); + } + + get name() { + return `TypeError[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +class NodeRangeError extends RangeError { + constructor(key /**, ...args **/) { + const args = Array(arguments.length - 1); + for (var n = 1; n < arguments.length; n++) + args[n - 1] = arguments[n]; + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeRangeError); + } + + get name() { + return `RangeError[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +var assert, util; +function lazyAssert() { + if (!assert) + assert = require('assert'); + return assert; +} + +function lazyUtil() { + if (!util) + util = require('util'); + return util; +} + +function message(key, args) { + const assert = lazyAssert(); + const util = lazyUtil(); + const msg = exports[Symbol.for(key)]; + assert(msg, `An invalid error message key was used: ${key}.`); + let fmt = util.format; + if (typeof msg === 'function') { + fmt = msg; + } else { + if (args === undefined || args.length === 0) + return msg; + args.unshift(msg); + } + return String(fmt.apply(null, args)); +} + +exports.message = message; +exports.Error = NodeError; +exports.TypeError = NodeTypeError; +exports.RangeError = NodeRangeError; + +// Utility function for registering the error codes. +function E(sym, val) { + exports[Symbol.for(String(sym).toUpperCase())] = + typeof val === 'function' ? val : String(val); +} + +// To declare an error message, use the E(sym, val) function above. The sym +// must be an upper case string. The val can be either a function or a string. +// The return value of the function must be a string. +// Examples: +// E('EXAMPLE_KEY1', 'This is the error value'); +// E('EXAMPLE_KEY2', (a, b) => return `${a} ${b}`); + +// Note: Please try to keep these in alphabetical order +E('ASSERTION_ERROR', (msg) => msg); diff --git a/node.gyp b/node.gyp index 59bdc0d3a7dfc1..1043d6420bb9dc 100644 --- a/node.gyp +++ b/node.gyp @@ -77,6 +77,7 @@ 'lib/internal/buffer.js', 'lib/internal/child_process.js', 'lib/internal/cluster.js', + 'lib/internal/errors.js', 'lib/internal/freelist.js', 'lib/internal/fs.js', 'lib/internal/linkedlist.js', diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js new file mode 100644 index 00000000000000..7602dc2ab65f66 --- /dev/null +++ b/test/parallel/test-internal-errors.js @@ -0,0 +1,37 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const errors = require('internal/errors'); +const assert = require('assert'); + +errors[Symbol.for('TEST_ERROR_1')] = 'Error for testing purposes: %s'; +errors[Symbol.for('TEST_ERROR_2')] = (a, b) => `${a} ${b}`; + +const err1 = new errors.Error('TEST_ERROR_1', 'test'); +const err2 = new errors.TypeError('TEST_ERROR_1', 'test'); +const err3 = new errors.RangeError('TEST_ERROR_1', 'test'); +const err4 = new errors.Error('TEST_ERROR_2', 'abc', 'xyz'); + +assert(err1 instanceof Error); +assert.strictEqual(err1.name, 'Error[TEST_ERROR_1]'); +assert.strictEqual(err1.message, 'Error for testing purposes: test'); +assert.strictEqual(err1.code, 'TEST_ERROR_1'); + +assert(err2 instanceof TypeError); +assert.strictEqual(err2.name, 'TypeError[TEST_ERROR_1]'); +assert.strictEqual(err2.message, 'Error for testing purposes: test'); +assert.strictEqual(err2.code, 'TEST_ERROR_1'); + +assert(err3 instanceof RangeError); +assert.strictEqual(err3.name, 'RangeError[TEST_ERROR_1]'); +assert.strictEqual(err3.message, 'Error for testing purposes: test'); +assert.strictEqual(err3.code, 'TEST_ERROR_1'); + +assert(err4 instanceof Error); +assert.strictEqual(err4.name, 'Error[TEST_ERROR_2]'); +assert.strictEqual(err4.message, 'abc xyz'); +assert.strictEqual(err4.code, 'TEST_ERROR_2'); + +assert.throws(() => new errors.Error('TEST_FOO_KEY'), + /An invalid error message key was used: TEST_FOO_KEY./); From 9bbef400928cc7159d019b92399671975eec44cf Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 13:23:40 -0700 Subject: [PATCH 2/9] errors: update bootstrap_node.js to use internal/errors --- lib/internal/bootstrap_node.js | 3 ++- lib/internal/errors.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 8db79aa33fb32a..5c597db16983e2 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -415,7 +415,8 @@ } if (!NativeModule.exists(id)) { - throw new Error(`No such native module ${id}`); + const errors = require('internal/errors'); + throw new errors.Error('NATIVE_MODULE_NOT_FOUND', id); } process.moduleLoadList.push(`NativeModule ${id}`); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index e3fa38de4144b3..db88a91cde1d2e 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -118,3 +118,4 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); +E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); From 5c78767af483be1e9284b3ae972b2c8b01341237 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 13:30:13 -0700 Subject: [PATCH 3/9] errors: update internal/util to use internal/errors --- lib/internal/errors.js | 1 + lib/internal/util.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index db88a91cde1d2e..67cb0d140ed38d 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -119,3 +119,4 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); +E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); diff --git a/lib/internal/util.js b/lib/internal/util.js index 4ada8dd0cc16f0..61704b30d28369 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -1,6 +1,7 @@ 'use strict'; const binding = process.binding('util'); +const errors = require('internal/errors'); const prefix = `(${process.release.name}:${process.pid}) `; const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol']; @@ -98,8 +99,9 @@ exports.objectToString = function objectToString(o) { const noCrypto = !process.versions.openssl; exports.assertCrypto = function(exports) { - if (noCrypto) - throw new Error('Node.js is not compiled with openssl crypto support'); + if (noCrypto) { + throw new errors.Error('NO_CRYPTO'); + } }; exports.kIsEncodingSymbol = Symbol('node.isEncoding'); From 753b3efe8fc21208ce131f55adcfca34966bce0d Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 13:38:15 -0700 Subject: [PATCH 4/9] errors: update internal/socket_list to use internal/errors --- lib/internal/errors.js | 7 ++++--- lib/internal/socket_list.js | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 67cb0d140ed38d..4a3743c3ee6242 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -19,7 +19,7 @@ class NodeError extends Error { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, NodeError); + Error.captureStackTrace(this, new.target); } get name() { @@ -38,7 +38,7 @@ class NodeTypeError extends TypeError { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, NodeTypeError); + Error.captureStackTrace(this, new.target); } get name() { @@ -57,7 +57,7 @@ class NodeRangeError extends RangeError { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, NodeRangeError); + Error.captureStackTrace(this, new.target); } get name() { @@ -120,3 +120,4 @@ function E(sym, val) { E('ASSERTION_ERROR', (msg) => msg); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); +E('SOCKET_CLOSED_BEFORE_REPLY', 'Socket closed before reply'); diff --git a/lib/internal/socket_list.js b/lib/internal/socket_list.js index 0f4be6df239dd6..8f0091593882ae 100644 --- a/lib/internal/socket_list.js +++ b/lib/internal/socket_list.js @@ -4,6 +4,7 @@ module.exports = {SocketListSend, SocketListReceive}; const EventEmitter = require('events'); const util = require('util'); +const errors = require('internal/errors'); // This object keep track of the socket there are sended function SocketListSend(slave, key) { @@ -22,7 +23,7 @@ SocketListSend.prototype._request = function(msg, cmd, callback) { function onclose() { self.slave.removeListener('internalMessage', onreply); - callback(new Error('Slave closed before reply')); + callback(new errors.Error('SOCKET_CLOSED_BEFORE_REPLY')); } function onreply(msg) { From c9b2093e3d059f80afc68c8b820201e189063225 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 13:57:27 -0700 Subject: [PATCH 5/9] errors: update internal/net.js to use internal/errors --- lib/internal/errors.js | 7 ++++--- lib/internal/net.js | 3 ++- test/parallel/test-net-listen-port-option.js | 7 +++++-- test/parallel/test-regress-GH-5727.js | 4 +++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 4a3743c3ee6242..e76d7bbb8171ef 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -19,7 +19,7 @@ class NodeError extends Error { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, new.target); + Error.captureStackTrace(this, NodeError); } get name() { @@ -38,7 +38,7 @@ class NodeTypeError extends TypeError { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, new.target); + Error.captureStackTrace(this, NodeTypeError); } get name() { @@ -57,7 +57,7 @@ class NodeRangeError extends RangeError { args[n - 1] = arguments[n]; super(message(key, args)); this[kCode] = key; - Error.captureStackTrace(this, new.target); + Error.captureStackTrace(this, NodeRangeError); } get name() { @@ -118,6 +118,7 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); +E('INVALID_PORT', 'port must be a number >= 0 and <= 65535'); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); E('SOCKET_CLOSED_BEFORE_REPLY', 'Socket closed before reply'); diff --git a/lib/internal/net.js b/lib/internal/net.js index d19bc4c219a796..cd2255edc345e3 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); module.exports = { isLegalPort, assertPort }; // Check that the port number is not NaN when coerced to a number, @@ -14,5 +15,5 @@ function isLegalPort(port) { function assertPort(port) { if (typeof port !== 'undefined' && !isLegalPort(port)) - throw new RangeError('"port" argument must be >= 0 and < 65536'); + throw new errors.RangeError('INVALID_PORT'); } diff --git a/test/parallel/test-net-listen-port-option.js b/test/parallel/test-net-listen-port-option.js index 028ecdde784440..98f3387e36ba3c 100644 --- a/test/parallel/test-net-listen-port-option.js +++ b/test/parallel/test-net-listen-port-option.js @@ -1,8 +1,12 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); +const errors = require('internal/errors'); const assert = require('assert'); const net = require('net'); +const invalid_port = new RegExp(`${errors.message('INVALID_PORT')}`); + function close() { this.close(); } // From lib/net.js @@ -35,8 +39,7 @@ listenVariants.forEach((listenVariant, i) => { // skip this, because listen(port) can also be listen(path) return; } - assert.throws(() => listenVariant(port, common.fail), - /"port" argument must be >= 0 and < 65536/i); + assert.throws(() => listenVariant(port, common.fail), invalid_port); }); [null, true, false].forEach((port) => diff --git a/test/parallel/test-regress-GH-5727.js b/test/parallel/test-regress-GH-5727.js index 3f52c55e7aaca6..8a30de731bd685 100644 --- a/test/parallel/test-regress-GH-5727.js +++ b/test/parallel/test-regress-GH-5727.js @@ -1,10 +1,12 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); const assert = require('assert'); const net = require('net'); +const errors = require('internal/errors'); const invalidPort = -1 >>> 0; -const errorMessage = /"port" argument must be >= 0 and < 65536/; +const errorMessage = new RegExp(`${errors.message('INVALID_PORT')}`); net.Server().listen(common.PORT, function() { const address = this.address(); From 5ae2086ccd65829c4c882bd37a319dcc06af778c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 14:10:44 -0700 Subject: [PATCH 6/9] errors: update internal/next_tick.js to use internals/errors --- lib/internal/errors.js | 1 + lib/internal/process/next_tick.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index e76d7bbb8171ef..24afce50b86950 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -118,6 +118,7 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); +E('INVALID_CALLBACK', 'callback is not a function'); E('INVALID_PORT', 'port must be a number >= 0 and <= 65535'); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index f27ef622a96e6a..d6ffb65642f6ba 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -132,8 +132,10 @@ function setupNextTick() { } function nextTick(callback) { - if (typeof callback !== 'function') - throw new TypeError('callback is not a function'); + if (typeof callback !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALID_CALLBACK'); + } // on the way out, don't bother. it won't get fired anyway. if (process._exiting) return; From 9def513757b99615f622c217dc4ac01a6dabcf80 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 14:20:21 -0700 Subject: [PATCH 7/9] errors: update internal/fs.js to use internal/errors --- lib/internal/errors.js | 2 ++ lib/internal/fs.js | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 24afce50b86950..4ff32e7101e00a 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -119,6 +119,8 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); E('INVALID_CALLBACK', 'callback is not a function'); +E('INVALID_ENCODING', (encoding) => `Unknown encoding: ${encoding}`); +E('INVALID_FILE_OPEN_FLAG', (flag) => `Unknown file open flag: ${flag}`); E('INVALID_PORT', 'port must be a number >= 0 and <= 65535'); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); diff --git a/lib/internal/fs.js b/lib/internal/fs.js index 89ad30b78ce916..2735956e8f3669 100644 --- a/lib/internal/fs.js +++ b/lib/internal/fs.js @@ -5,6 +5,7 @@ const Writable = require('stream').Writable; const fs = require('fs'); const util = require('util'); const constants = process.binding('constants').fs; +const errors = require('internal/errors'); const O_APPEND = constants.O_APPEND | 0; const O_CREAT = constants.O_CREAT | 0; @@ -17,7 +18,7 @@ const O_WRONLY = constants.O_WRONLY | 0; function assertEncoding(encoding) { if (encoding && !Buffer.isEncoding(encoding)) { - throw new Error(`Unknown encoding: ${encoding}`); + throw new errors.Error('INVALID_ENCODING', encoding); } } exports.assertEncoding = assertEncoding; @@ -52,7 +53,7 @@ function stringToFlags(flag) { case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL; } - throw new Error('Unknown file open flag: ' + flag); + throw new errors.Error('INVALID_FILE_OPEN_FLAG', flag); } exports.stringToFlags = stringToFlags; From 586642520f666cc684e2e59eef0fc6303d6ef8d2 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 14:23:43 -0700 Subject: [PATCH 8/9] errors: update internal/url.js to use internal/errors --- lib/internal/errors.js | 1 + lib/internal/url.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 4ff32e7101e00a..c1d8b0d5d6b0d9 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -122,6 +122,7 @@ E('INVALID_CALLBACK', 'callback is not a function'); E('INVALID_ENCODING', (encoding) => `Unknown encoding: ${encoding}`); E('INVALID_FILE_OPEN_FLAG', (flag) => `Unknown file open flag: ${flag}`); E('INVALID_PORT', 'port must be a number >= 0 and <= 65535'); +E('INVALID_URL', 'Invalid URL'); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); E('SOCKET_CLOSED_BEFORE_REPLY', 'Socket closed before reply'); diff --git a/lib/internal/url.js b/lib/internal/url.js index 79b9e1cb0f46bb..06c35021fe2e26 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -14,6 +14,7 @@ const cannotBeBase = Symbol('cannot-be-base'); const special = Symbol('special'); const searchParams = Symbol('query'); const querystring = require('querystring'); +const errors = require('internal/errors'); const kScheme = Symbol('scheme'); const kHost = Symbol('host'); @@ -81,8 +82,9 @@ class URL { binding.parse(input.trim(), -1, base_context, undefined, (flags, protocol, username, password, host, port, path, query, fragment) => { - if (flags & binding.URL_FLAGS_FAILED) - throw new TypeError('Invalid URL'); + if (flags & binding.URL_FLAGS_FAILED) { + throw new errors.TypeError('INVALID_URL'); + } this[context].flags = flags; this[context].scheme = protocol; this[context].username = username; From 48792910646d1a9619880d918400c093a7fdb7b9 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Oct 2016 16:28:33 -0700 Subject: [PATCH 9/9] errors: update internal/child_process.js to use internal/errors Update most of the errors to use internal/errors. Purposefully leaves two errors in place to be updated later. --- lib/internal/child_process.js | 27 ++++++++++--------- lib/internal/errors.js | 11 ++++++++ test/parallel/test-child-process-stdio.js | 6 ++++- .../test-child-process-validate-stdio.js | 8 +++--- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index f79657048cc489..9da8c5f7d5a807 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -8,6 +8,7 @@ const dgram = require('dgram'); const util = require('util'); const constants = process.binding('constants').os.signals; const assert = require('assert'); +const errors = require('internal/errors'); const Process = process.binding('process_wrap').Process; const WriteWrap = process.binding('stream_wrap').WriteWrap; @@ -372,7 +373,7 @@ ChildProcess.prototype.kill = function(sig) { } if (signal === undefined) { - throw new Error('Unknown signal: ' + sig); + throw new errors.Error('INVALID_SIGNAL', sig); } if (this._handle) { @@ -537,7 +538,7 @@ function setupChannel(target, channel) { if (this.connected) { return this._send(message, handle, options, callback); } - const ex = new Error('channel closed'); + const ex = new errors.Error('CHANNEL_CLOSED'); if (typeof callback === 'function') { process.nextTick(callback, ex); } else { @@ -577,7 +578,7 @@ function setupChannel(target, channel) { } else if (handle instanceof UDP) { message.type = 'dgram.Native'; } else { - throw new TypeError('This handle type can\'t be sent'); + throw new errors.TypeError('INVALID_HANDLE_TYPE'); } // Queue-up message and handle if we haven't received ACK yet. @@ -677,7 +678,7 @@ function setupChannel(target, channel) { target.disconnect = function() { if (!this.connected) { - this.emit('error', new Error('IPC channel is already disconnected')); + this.emit('error', new errors.Error('CHILD_PROCESS_IPC_DISCONNECTED')); return; } @@ -757,11 +758,11 @@ function _validateStdio(stdio, sync) { case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break; case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break; case 'inherit': stdio = [0, 1, 2]; break; - default: throw new TypeError('Incorrect value of stdio option: ' + stdio); + default: throw new errors.TypeError('CHILD_PROCESS_INVALID_STDIO', stdio); } } else if (!Array.isArray(stdio)) { - throw new TypeError('Incorrect value of stdio option: ' + - util.inspect(stdio)); + throw new errors.TypeError('CHILD_PROCESS_INVALID_STDIO', + util.inspect(stdio)); } // At least 3 stdio will be created @@ -805,9 +806,9 @@ function _validateStdio(stdio, sync) { // Cleanup previously created pipes cleanup(); if (!sync) - throw new Error('Child process can have only one IPC pipe'); + throw new errors.Error('CHILD_PROCESS_ONE_IPC'); else - throw new Error('You cannot use IPC with synchronous forks'); + throw new errors.Error('CHILD_PROCESS_SYNCHRONOUS_FORK_NO_IPC'); } ipc = new Pipe(true); @@ -842,14 +843,14 @@ function _validateStdio(stdio, sync) { } else if (stdio instanceof Buffer || typeof stdio === 'string') { if (!sync) { cleanup(); - throw new TypeError('Asynchronous forks do not support Buffer input: ' + - util.inspect(stdio)); + throw new errors.TypeError('CHILD_PROCESS_ASYNC_NO_BUFFER', + util.inspect(stdio)); } } else { // Cleanup cleanup(); - throw new TypeError('Incorrect value for stdio stream: ' + - util.inspect(stdio)); + throw new errors.TypeError('CHILD_PROCESS_INVALID_STDIO', + util.inspect(stdio)); } return acc; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index c1d8b0d5d6b0d9..9760fc17735cd1 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -118,10 +118,21 @@ function E(sym, val) { // Note: Please try to keep these in alphabetical order E('ASSERTION_ERROR', (msg) => msg); +E('CHANNEL_CLOSED', 'channel closed'); +E('CHILD_PROCESS_ASYNC_NO_BUFFER', + (value) => `Asynchronous forks do not support Buffer input: ${value}`); +E('CHILD_PROCESS_INVALID_STDIO', + (value) => `Incorrect value of stdio option: ${value}`); +E('CHILD_PROCESS_IPC_DISCONNECTED', 'IPC channel is already disconnected'); +E('CHILD_PROCESS_ONE_IPC', 'Child process can have only one IPC pipe'); +E('CHILD_PROCESS_SYNCHRONOUS_FORK_NO_IPC', + 'IPC cannot be used with synchronous forks'); E('INVALID_CALLBACK', 'callback is not a function'); E('INVALID_ENCODING', (encoding) => `Unknown encoding: ${encoding}`); E('INVALID_FILE_OPEN_FLAG', (flag) => `Unknown file open flag: ${flag}`); +E('INVALID_HANDLE_TYPE', 'This handle type cannot be sent'); E('INVALID_PORT', 'port must be a number >= 0 and <= 65535'); +E('INVALID_SIGNAL', (signal) => `Unknown signal: ${signal}`); E('INVALID_URL', 'Invalid URL'); E('NATIVE_MODULE_NOT_FOUND', (module) => `No such native module ${module}`); E('NO_CRYPTO', 'Node.js is not compiled with openssl crypto support'); diff --git a/test/parallel/test-child-process-stdio.js b/test/parallel/test-child-process-stdio.js index 3c67b3c3fe37a0..205093cdc2679b 100644 --- a/test/parallel/test-child-process-stdio.js +++ b/test/parallel/test-child-process-stdio.js @@ -1,6 +1,10 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); const assert = require('assert'); +const errors = require('internal/errors'); + +const one_ipc_err = new RegExp(errors.message('CHILD_PROCESS_ONE_IPC')); let options = {stdio: ['pipe']}; let child = common.spawnPwd(options); @@ -20,4 +24,4 @@ assert.deepStrictEqual(options, {stdio: 'ignore'}); assert.throws(() => { common.spawnPwd({stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc']}); -}, /^Error: Child process can have only one IPC pipe$/); +}, one_ipc_err); diff --git a/test/parallel/test-child-process-validate-stdio.js b/test/parallel/test-child-process-validate-stdio.js index 09d000467404b6..3b3112279934f3 100644 --- a/test/parallel/test-child-process-validate-stdio.js +++ b/test/parallel/test-child-process-validate-stdio.js @@ -4,6 +4,10 @@ require('../common'); const assert = require('assert'); const _validateStdio = require('internal/child_process')._validateStdio; +const errors = require('internal/errors'); + +const no_ipc_err = + new RegExp(errors.message('CHILD_PROCESS_SYNCHRONOUS_FORK_NO_IPC')); // should throw if string and not ignore, pipe, or inherit assert.throws(function() { @@ -27,9 +31,7 @@ assert.throws(function() { // should throw if stdio has ipc and sync is true const stdio2 = ['ipc', 'ipc', 'ipc']; -assert.throws(function() { - _validateStdio(stdio2, true); -}, /You cannot use IPC with synchronous forks/); +assert.throws(() => _validateStdio(stdio2, true), no_ipc_err); { const stdio3 = [process.stdin, process.stdout, process.stderr];