Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

errors: add internal/errors.js #9265

Closed
wants to merge 9 commits into from
3 changes: 2 additions & 1 deletion lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@
}

if (!NativeModule.exists(id)) {
throw new Error(`No such native module ${id}`);
const errors = require('internal/errors');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: const {Error} = require('internal/errors');

throw new errors.Error('NATIVE_MODULE_NOT_FOUND', id);
}

process.moduleLoadList.push(`NativeModule ${id}`);
Expand Down
27 changes: 14 additions & 13 deletions lib/internal/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
139 changes: 139 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
'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');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbol('error code') ?


class NodeError extends Error {
constructor(key /**, ...args **/) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the overhead of rest parameters is negligible here.
Testcase: https://jsperf.com/custom-error-with-rest-params

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())] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is using the global symbol registry, perhaps we should add a NODE_ prefix to the key.

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);
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');
E('SOCKET_CLOSED_BEFORE_REPLY', 'Socket closed before reply');
5 changes: 3 additions & 2 deletions lib/internal/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion lib/internal/net.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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');
}
6 changes: 4 additions & 2 deletions lib/internal/process/next_tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion lib/internal/socket_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions lib/internal/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
@@ -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'];
Expand Down Expand Up @@ -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');
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
6 changes: 5 additions & 1 deletion test/parallel/test-child-process-stdio.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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);
8 changes: 5 additions & 3 deletions test/parallel/test-child-process-validate-stdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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];
Expand Down
Loading