Skip to content

Commit 6e172be

Browse files
committed
errors: make properties mutable
Userland code can break if it depends on a mutable `code` property for errors. Allow users to change the `code` property but do not propagate changes to the error `name`. Additionally, make `message` and `name` consistent with `Error` object (non-enumerable). Test that `console.log()` and `.toString()` calls on internal `Error` objects with mutated properties have analogous results with the standard ECMAScript `Error` objects. PR-URL: #15694 Fixes: #15658 Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 8068577 commit 6e172be

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

lib/internal/errors.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,13 @@ function makeNodeError(Base) {
2222
return class NodeError extends Base {
2323
constructor(key, ...args) {
2424
super(message(key, args));
25-
this[kCode] = key;
26-
}
27-
28-
get name() {
29-
return `${super.name} [${this[kCode]}]`;
30-
}
31-
32-
get code() {
33-
return this[kCode];
25+
this[kCode] = this.code = key;
26+
Object.defineProperty(this, 'name', {
27+
configurable: true,
28+
enumerable: false,
29+
value: `${super.name} [${this[kCode]}]`,
30+
writable: true
31+
});
3432
}
3533
};
3634
}

test/parallel/test-internal-errors.js

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Flags: --expose-internals
22
'use strict';
3-
43
const common = require('../common');
5-
const errors = require('internal/errors');
4+
65
const assert = require('assert');
6+
const errors = require('internal/errors');
77

88
function invalidKey(key) {
99
return new RegExp(`^An invalid error message key was used: ${key}\\.$`);
@@ -301,3 +301,44 @@ assert.strictEqual(
301301
'The value "bar" is invalid for argument "foo"'
302302
);
303303
}
304+
305+
// Test that `code` property is mutable and that changing it does not change the
306+
// name.
307+
{
308+
const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
309+
assert.strictEqual(myError.code, 'ERR_TLS_HANDSHAKE_TIMEOUT');
310+
const initialName = myError.name;
311+
myError.code = 'FHQWHGADS';
312+
assert.strictEqual(myError.code, 'FHQWHGADS');
313+
assert.strictEqual(myError.name, initialName);
314+
assert.ok(myError.name.includes('ERR_TLS_HANDSHAKE_TIMEOUT'));
315+
assert.ok(!myError.name.includes('FHQWHGADS'));
316+
}
317+
318+
// Test that `name` and `message` are mutable and that changing them alters
319+
// `toString()` but not `console.log()` results, which is the behavior of
320+
// `Error` objects in the browser.
321+
{
322+
function test(prop) {
323+
let initialConsoleLog = '';
324+
common.hijackStdout((data) => { initialConsoleLog += data; });
325+
const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
326+
const initialToString = myError.toString();
327+
console.log(myError);
328+
assert.notStrictEqual(initialConsoleLog, '');
329+
330+
common.restoreStdout();
331+
332+
let subsequentConsoleLog = '';
333+
common.hijackStdout((data) => { subsequentConsoleLog += data; });
334+
myError[prop] = 'Fhqwhgads';
335+
assert.notStrictEqual(myError.toString(), initialToString);
336+
console.log(myError);
337+
assert.strictEqual(subsequentConsoleLog, initialConsoleLog);
338+
339+
common.restoreStdout();
340+
}
341+
342+
test('name');
343+
test('message');
344+
}

0 commit comments

Comments
 (0)