Skip to content

Commit 39f4158

Browse files
committed
lib: move extra properties into error creation
This encapsulates the Node.js errors more by adding extra properties to an error inside of the function to create the error message instead of adding the properties at the call site. That simplifies the usage of our errors and makes sure the expected properties are always set. PR-URL: #26752 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent df0870d commit 39f4158

File tree

9 files changed

+78
-47
lines changed

9 files changed

+78
-47
lines changed

lib/_stream_readable.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -309,15 +309,13 @@ function addChunk(stream, state, chunk, addToFront) {
309309
}
310310

311311
function chunkInvalid(state, chunk) {
312-
var er;
313312
if (!Stream._isUint8Array(chunk) &&
314313
typeof chunk !== 'string' &&
315314
chunk !== undefined &&
316315
!state.objectMode) {
317-
er = new ERR_INVALID_ARG_TYPE(
316+
return new ERR_INVALID_ARG_TYPE(
318317
'chunk', ['string', 'Buffer', 'Uint8Array'], chunk);
319318
}
320-
return er;
321319
}
322320

323321

lib/internal/encoding.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,7 @@ function makeTextDecoderICU() {
394394

395395
const ret = _decode(this[kHandle], input, flags);
396396
if (typeof ret === 'number') {
397-
const err = new ERR_ENCODING_INVALID_ENCODED_DATA(this.encoding);
398-
err.errno = ret;
399-
throw err;
397+
throw new ERR_ENCODING_INVALID_ENCODED_DATA(this.encoding, ret);
400398
}
401399
return ret.toString('ucs2');
402400
}

lib/internal/errors.js

+56-13
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ function lazyBuffer() {
4747
// and may have .path and .dest.
4848
class SystemError extends Error {
4949
constructor(key, context) {
50-
const prefix = getMessage(key, []);
50+
super();
51+
const prefix = getMessage(key, [], this);
5152
let message = `${prefix}: ${context.syscall} returned ` +
5253
`${context.code} (${context.message})`;
5354

@@ -56,7 +57,12 @@ class SystemError extends Error {
5657
if (context.dest !== undefined)
5758
message += ` => ${context.dest}`;
5859

59-
super(message);
60+
Object.defineProperty(this, 'message', {
61+
value: message,
62+
enumerable: false,
63+
writable: true,
64+
configurable: true
65+
});
6066
Object.defineProperty(this, kInfo, {
6167
configurable: false,
6268
enumerable: false,
@@ -150,7 +156,14 @@ let useOriginalName = false;
150156
function makeNodeErrorWithCode(Base, key) {
151157
return class NodeError extends Base {
152158
constructor(...args) {
153-
super(getMessage(key, args));
159+
super();
160+
const message = getMessage(key, args, this);
161+
Object.defineProperty(this, 'message', {
162+
value: message,
163+
enumerable: false,
164+
writable: true,
165+
configurable: true
166+
});
154167
}
155168

156169
get name() {
@@ -204,7 +217,7 @@ function E(sym, val, def, ...otherClasses) {
204217
codes[sym] = def;
205218
}
206219

207-
function getMessage(key, args) {
220+
function getMessage(key, args, self) {
208221
const msg = messages.get(key);
209222

210223
if (util === undefined) util = require('util');
@@ -216,7 +229,7 @@ function getMessage(key, args) {
216229
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
217230
`match the required ones (${msg.length}).`
218231
);
219-
return msg.apply(null, args);
232+
return msg.apply(self, args);
220233
}
221234

222235
const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length;
@@ -608,8 +621,10 @@ E('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE',
608621
'The `domain` module is in use, which is mutually exclusive with calling ' +
609622
'process.setUncaughtExceptionCaptureCallback()',
610623
Error);
611-
E('ERR_ENCODING_INVALID_ENCODED_DATA',
612-
'The encoded data was not valid for encoding %s', TypeError);
624+
E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
625+
this.errno = ret;
626+
return `The encoded data was not valid for encoding ${encoding}`;
627+
}, TypeError);
613628
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
614629
RangeError);
615630
E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value', Error);
@@ -652,7 +667,16 @@ E('ERR_HTTP2_INVALID_PSEUDOHEADER',
652667
'"%s" is an invalid pseudoheader or is used incorrectly', TypeError);
653668
E('ERR_HTTP2_INVALID_SESSION', 'The session has been destroyed', Error);
654669
E('ERR_HTTP2_INVALID_SETTING_VALUE',
655-
'Invalid value for setting "%s": %s', TypeError, RangeError);
670+
// Using default arguments here is important so the arguments are not counted
671+
// towards `Function#length`.
672+
function(name, actual, min = undefined, max = undefined) {
673+
this.actual = actual;
674+
if (min !== undefined) {
675+
this.min = min;
676+
this.max = max;
677+
}
678+
return `Invalid value for setting "${name}": ${actual}`;
679+
}, TypeError, RangeError);
656680
E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed', Error);
657681
E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
658682
'Maximum number of pending settings acknowledgements', Error);
@@ -685,7 +709,15 @@ E('ERR_HTTP2_SOCKET_UNBOUND',
685709
E('ERR_HTTP2_STATUS_101',
686710
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2', Error);
687711
E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s', RangeError);
688-
E('ERR_HTTP2_STREAM_CANCEL', 'The pending stream has been canceled', Error);
712+
E('ERR_HTTP2_STREAM_CANCEL', function(error) {
713+
let msg = 'The pending stream has been canceled';
714+
if (error) {
715+
this.cause = error;
716+
if (typeof error.message === 'string')
717+
msg += ` (caused by: ${error.message})`;
718+
}
719+
return msg;
720+
}, Error);
689721
E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s', Error);
690722
E('ERR_HTTP2_STREAM_SELF_DEPENDENCY',
691723
'A stream cannot depend on itself', Error);
@@ -709,7 +741,11 @@ E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
709741
E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error);
710742
E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error);
711743
E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error);
712-
E('ERR_INVALID_ADDRESS_FAMILY', 'Invalid address family: %s', RangeError);
744+
E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
745+
this.host = host;
746+
this.port = port;
747+
return `Invalid address family: ${addressType} ${host}:${port}`;
748+
}, RangeError);
713749
E('ERR_INVALID_ARG_TYPE',
714750
(name, expected, actual) => {
715751
assert(typeof name === 'string', "'name' must be a string");
@@ -812,7 +848,10 @@ E('ERR_INVALID_SYNC_FORK_INPUT',
812848
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError);
813849
E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError);
814850
E('ERR_INVALID_URI', 'URI malformed', URIError);
815-
E('ERR_INVALID_URL', 'Invalid URL: %s', TypeError);
851+
E('ERR_INVALID_URL', function(input) {
852+
this.input = input;
853+
return `Invalid URL: ${input}`;
854+
}, TypeError);
816855
E('ERR_INVALID_URL_SCHEME',
817856
(expected) => `The URL must be ${oneOf(expected, 'scheme')}`, TypeError);
818857
E('ERR_IPC_CHANNEL_CLOSED', 'Channel closed', Error);
@@ -926,8 +965,12 @@ E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error);
926965
E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error);
927966
E('ERR_SYNTHETIC', 'JavaScript Callstack', Error);
928967
E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError);
929-
E('ERR_TLS_CERT_ALTNAME_INVALID',
930-
'Hostname/IP does not match certificate\'s altnames: %s', Error);
968+
E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) {
969+
this.reason = reason;
970+
this.host = host;
971+
this.cert = cert;
972+
return `Hostname/IP does not match certificate's altnames: ${reason}`;
973+
}, Error);
931974
E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error);
932975
E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error);
933976
E('ERR_TLS_INVALID_PROTOCOL_VERSION',

lib/internal/http2/core.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,6 @@ function validateSettings(settings) {
809809
typeof settings.enablePush !== 'boolean') {
810810
const err = new ERR_HTTP2_INVALID_SETTING_VALUE('enablePush',
811811
settings.enablePush);
812-
err.actual = settings.enablePush;
813812
Error.captureStackTrace(err, 'validateSettings');
814813
throw err;
815814
}
@@ -1216,12 +1215,7 @@ class Http2Session extends EventEmitter {
12161215
this.removeAllListeners('timeout');
12171216

12181217
// Destroy any pending and open streams
1219-
const cancel = new ERR_HTTP2_STREAM_CANCEL();
1220-
if (error) {
1221-
cancel.cause = error;
1222-
if (typeof error.message === 'string')
1223-
cancel.message += ` (caused by: ${error.message})`;
1224-
}
1218+
const cancel = new ERR_HTTP2_STREAM_CANCEL(error);
12251219
state.pendingStreams.forEach((stream) => stream.destroy(cancel));
12261220
state.streams.forEach((stream) => stream.destroy(error));
12271221

lib/internal/http2/util.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,8 @@ function assertIsObject(value, name, types) {
515515
function assertWithinRange(name, value, min = 0, max = Infinity) {
516516
if (value !== undefined &&
517517
(typeof value !== 'number' || value < min || value > max)) {
518-
const err = new ERR_HTTP2_INVALID_SETTING_VALUE.RangeError(name, value);
519-
err.min = min;
520-
err.max = max;
521-
err.actual = value;
518+
const err = new ERR_HTTP2_INVALID_SETTING_VALUE.RangeError(
519+
name, value, min, max);
522520
Error.captureStackTrace(err, assertWithinRange);
523521
throw err;
524522
}

lib/internal/url.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,7 @@ function onParseComplete(flags, protocol, username, password,
238238
}
239239

240240
function onParseError(flags, input) {
241-
const error = new ERR_INVALID_URL(input);
242-
error.input = input;
243-
throw error;
241+
throw new ERR_INVALID_URL(input);
244242
}
245243

246244
function onParseProtocolComplete(flags, protocol, username, password,

lib/net.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -968,19 +968,20 @@ function lookupAndConnect(self, options) {
968968
if (!self.connecting) return;
969969

970970
if (err) {
971-
// net.createConnection() creates a net.Socket object and
972-
// immediately calls net.Socket.connect() on it (that's us).
973-
// There are no event listeners registered yet so defer the
974-
// error event to the next tick.
971+
// net.createConnection() creates a net.Socket object and immediately
972+
// calls net.Socket.connect() on it (that's us). There are no event
973+
// listeners registered yet so defer the error event to the next tick.
974+
// TODO(BridgeAR): The error could either originate from user code or
975+
// by the C++ layer. The port is never the cause for the error as it is
976+
// not used in the lookup. We should probably just remove this.
975977
err.host = options.host;
976978
err.port = options.port;
977979
err.message = err.message + ' ' + options.host + ':' + options.port;
978980
process.nextTick(connectErrorNT, self, err);
979981
} else if (addressType !== 4 && addressType !== 6) {
980-
err = new ERR_INVALID_ADDRESS_FAMILY(addressType);
981-
err.host = options.host;
982-
err.port = options.port;
983-
err.message = err.message + ' ' + options.host + ':' + options.port;
982+
err = new ERR_INVALID_ADDRESS_FAMILY(addressType,
983+
options.host,
984+
options.port);
984985
process.nextTick(connectErrorNT, self, err);
985986
} else {
986987
self._unrefTimer();

lib/tls.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,7 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
258258
}
259259

260260
if (!valid) {
261-
const err = new ERR_TLS_CERT_ALTNAME_INVALID(reason);
262-
err.reason = reason;
263-
err.host = hostname;
264-
err.cert = cert;
265-
return err;
261+
return new ERR_TLS_CERT_ALTNAME_INVALID(reason, hostname, cert);
266262
}
267263
};
268264

test/parallel/test-net-options-lookup.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,10 @@ function connectDoesNotThrow(input) {
3838
cb(null, '127.0.0.1', 100);
3939
});
4040

41-
s.on('error', common.expectsError({ code: 'ERR_INVALID_ADDRESS_FAMILY' }));
41+
s.on('error', common.expectsError({
42+
code: 'ERR_INVALID_ADDRESS_FAMILY',
43+
host: 'localhost',
44+
port: 0,
45+
message: 'Invalid address family: 100 localhost:0'
46+
}));
4247
}

0 commit comments

Comments
 (0)