errors: make code and name properties settable

For internal errors, make `code` and `name` settable while keeping them
non-own properties by default.

PR-URL: https://github.com/nodejs/node/pull/15694
Fixes: https://github.com/nodejs/node/issues/15658
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
This commit is contained in:
John-David Dalton 2017-10-01 16:14:45 -07:00 committed by Rich Trott
parent 6e172beaf0
commit 42a2a9badb
2 changed files with 68 additions and 30 deletions

View File

@ -11,9 +11,8 @@
const kCode = Symbol('code'); const kCode = Symbol('code');
const messages = new Map(); const messages = new Map();
const { const { kMaxLength } = process.binding('buffer');
kMaxLength const { defineProperty } = Object;
} = process.binding('buffer');
// Lazily loaded // Lazily loaded
var util = null; var util = null;
@ -22,11 +21,36 @@ function makeNodeError(Base) {
return class NodeError extends Base { return class NodeError extends Base {
constructor(key, ...args) { constructor(key, ...args) {
super(message(key, args)); super(message(key, args));
this[kCode] = this.code = key; defineProperty(this, kCode, {
Object.defineProperty(this, 'name', {
configurable: true, configurable: true,
enumerable: false, enumerable: false,
value: `${super.name} [${this[kCode]}]`, value: key,
writable: true
});
}
get name() {
return `${super.name} [${this[kCode]}]`;
}
set name(value) {
defineProperty(this, 'name', {
configurable: true,
enumerable: true,
value,
writable: true
});
}
get code() {
return this[kCode];
}
set code(value) {
defineProperty(this, 'code', {
configurable: true,
enumerable: true,
value,
writable: true writable: true
}); });
} }

View File

@ -307,38 +307,52 @@ assert.strictEqual(
{ {
const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT'); const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
assert.strictEqual(myError.code, 'ERR_TLS_HANDSHAKE_TIMEOUT'); assert.strictEqual(myError.code, 'ERR_TLS_HANDSHAKE_TIMEOUT');
assert.strictEqual(myError.hasOwnProperty('code'), false);
assert.strictEqual(myError.hasOwnProperty('name'), false);
assert.deepStrictEqual(Object.keys(myError), []);
const initialName = myError.name; const initialName = myError.name;
myError.code = 'FHQWHGADS'; myError.code = 'FHQWHGADS';
assert.strictEqual(myError.code, 'FHQWHGADS'); assert.strictEqual(myError.code, 'FHQWHGADS');
assert.strictEqual(myError.name, initialName); assert.strictEqual(myError.name, initialName);
assert.deepStrictEqual(Object.keys(myError), ['code']);
assert.ok(myError.name.includes('ERR_TLS_HANDSHAKE_TIMEOUT')); assert.ok(myError.name.includes('ERR_TLS_HANDSHAKE_TIMEOUT'));
assert.ok(!myError.name.includes('FHQWHGADS')); assert.ok(!myError.name.includes('FHQWHGADS'));
} }
// Test that `name` and `message` are mutable and that changing them alters // Test that `name` is mutable and that changing it alters `toString()` but not
// `toString()` but not `console.log()` results, which is the behavior of // `console.log()` results, which is the behavior of `Error` objects in the
// `Error` objects in the browser. // browser. Note that `name` becomes enumerable after being assigned.
{ {
function test(prop) { const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
let initialConsoleLog = ''; assert.deepStrictEqual(Object.keys(myError), []);
common.hijackStdout((data) => { initialConsoleLog += data; }); const initialToString = myError.toString();
const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
const initialToString = myError.toString();
console.log(myError);
assert.notStrictEqual(initialConsoleLog, '');
common.restoreStdout(); myError.name = 'Fhqwhgads';
assert.deepStrictEqual(Object.keys(myError), ['name']);
let subsequentConsoleLog = ''; assert.notStrictEqual(myError.toString(), initialToString);
common.hijackStdout((data) => { subsequentConsoleLog += data; }); }
myError[prop] = 'Fhqwhgads';
assert.notStrictEqual(myError.toString(), initialToString); // Test that `message` is mutable and that changing it alters `toString()` but
console.log(myError); // not `console.log()` results, which is the behavior of `Error` objects in the
assert.strictEqual(subsequentConsoleLog, initialConsoleLog); // browser. Note that `message` remains non-enumerable after being assigned.
{
common.restoreStdout(); let initialConsoleLog = '';
} common.hijackStdout((data) => { initialConsoleLog += data; });
const myError = new errors.Error('ERR_TLS_HANDSHAKE_TIMEOUT');
test('name'); assert.deepStrictEqual(Object.keys(myError), []);
test('message'); const initialToString = myError.toString();
console.log(myError);
assert.notStrictEqual(initialConsoleLog, '');
common.restoreStdout();
let subsequentConsoleLog = '';
common.hijackStdout((data) => { subsequentConsoleLog += data; });
myError.message = 'Fhqwhgads';
assert.deepStrictEqual(Object.keys(myError), []);
assert.notStrictEqual(myError.toString(), initialToString);
console.log(myError);
assert.strictEqual(subsequentConsoleLog, initialConsoleLog);
common.restoreStdout();
} }