From 5887e9f331775710b111821bcf7b623368e28af8 Mon Sep 17 00:00:00 2001 From: Anto Aravinth Date: Wed, 24 Oct 2018 19:31:19 +0530 Subject: [PATCH 1/2] util: add support for error subclassing and null handling --- lib/internal/util/inspect.js | 33 +++++++++++++++++++++++++---- test/parallel/test-util-inspect.js | 34 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 4cff372b330ec1..9a27d3dd42e988 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -643,7 +643,7 @@ function formatRaw(ctx, value, recurseTimes) { base = dateToISOString(value); } else if (isError(value)) { // Make error with message first say the error. - base = formatError(value); + base = formatError(value, getPrefix(constructor, '', 'Error')); // Wrap the error in brackets in case it has no stack trace. const stackStart = base.indexOf('\n at'); if (stackStart === -1) { @@ -652,7 +652,8 @@ function formatRaw(ctx, value, recurseTimes) { // The message and the stack have to be indented as well! if (ctx.indentationLvl !== 0) { const indentation = ' '.repeat(ctx.indentationLvl); - base = formatError(value).replace(/\n/g, `\n${indentation}`); + base = formatError(value, getPrefix(constructor, '', 'Error')) + .replace(/\n/g, `\n${indentation}`); } if (keys.length === 0) return base; @@ -861,8 +862,32 @@ function formatPrimitive(fn, value, ctx) { return fn(value.toString(), 'symbol'); } -function formatError(value) { - return value.stack || errorToString(value); +function formatError(value, constructorName) { + + if (value.stack) { + let stack = value.stack; + if (!stack.startsWith(constructorName) && + !(stack.startsWith('file:') && !constructorName.startsWith('file'))) { + const stackStart = value.stack.indexOf('\n at'); + const errorAndMessage = value.stack.slice(0, stackStart); + const messageStart = errorAndMessage.indexOf(':'); + if (stack && stackStart !== -1) { + stack = stack.slice(stackStart, stack.length); + if (messageStart !== -1) { + const message = errorAndMessage.slice(messageStart + 2, + stackStart.length); + // Reconstruct the stack + stack = constructorName.trim() + ': ' + message.trim() + stack; + } else { + stack = constructorName.trim() + stack; + } + } + + return stack; + } + return stack; + } + return errorToString(value); } function formatNamespaceObject(ctx, value, recurseTimes, keys) { diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 713bf047ddd345..af89a740cba00a 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1772,3 +1772,37 @@ assert.strictEqual( }); assert.strictEqual(util.inspect(obj), '[Set: null prototype] { 1, 2 }'); } + +// error subclassing and null prototype checks +{ + const error = new Error('My Message'); + assert.ok(util.inspect(error).includes('Error: My Message')); + + Object.setPrototypeOf(error, null); + assert.ok(util.inspect(error) + .includes('[Error: null prototype]: My Message')); +} + +{ + class MyCustomError extends Error {} + const customErr = new MyCustomError('custom message'); + assert.ok(util.inspect(customErr) + .includes('MyCustomError: custom message')); + + Object.setPrototypeOf(customErr, null); + assert.ok(util.inspect(customErr) + .includes('[Error: null prototype]: custom message')); +} + +// Check custom errors with name `File` +// works +{ + class FileCustomError extends Error {} + const customErr = new FileCustomError(); + assert.ok(util.inspect(customErr) + .includes('FileCustomError')); + + Object.setPrototypeOf(customErr, null); + assert.ok(util.inspect(customErr) + .includes('[Error: null prototype]')); +} From d18e25ecb8bb141bc21020c518c59bbb86867550 Mon Sep 17 00:00:00 2001 From: abelginrayen Date: Sat, 10 Nov 2018 18:51:08 +0530 Subject: [PATCH 2/2] addressing review comments --- lib/internal/util/inspect.js | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 9a27d3dd42e988..31372eae06a532 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -642,8 +642,9 @@ function formatRaw(ctx, value, recurseTimes) { } base = dateToISOString(value); } else if (isError(value)) { + const prefix = getPrefix(constructor, '', 'Error'); // Make error with message first say the error. - base = formatError(value, getPrefix(constructor, '', 'Error')); + base = formatError(value, prefix); // Wrap the error in brackets in case it has no stack trace. const stackStart = base.indexOf('\n at'); if (stackStart === -1) { @@ -652,8 +653,7 @@ function formatRaw(ctx, value, recurseTimes) { // The message and the stack have to be indented as well! if (ctx.indentationLvl !== 0) { const indentation = ' '.repeat(ctx.indentationLvl); - base = formatError(value, getPrefix(constructor, '', 'Error')) - .replace(/\n/g, `\n${indentation}`); + base = formatError(value, prefix).replace(/\n/g, `\n${indentation}`); } if (keys.length === 0) return base; @@ -863,27 +863,13 @@ function formatPrimitive(fn, value, ctx) { } function formatError(value, constructorName) { - if (value.stack) { let stack = value.stack; - if (!stack.startsWith(constructorName) && - !(stack.startsWith('file:') && !constructorName.startsWith('file'))) { - const stackStart = value.stack.indexOf('\n at'); - const errorAndMessage = value.stack.slice(0, stackStart); - const messageStart = errorAndMessage.indexOf(':'); - if (stack && stackStart !== -1) { - stack = stack.slice(stackStart, stack.length); - if (messageStart !== -1) { - const message = errorAndMessage.slice(messageStart + 2, - stackStart.length); - // Reconstruct the stack - stack = constructorName.trim() + ': ' + message.trim() + stack; - } else { - stack = constructorName.trim() + stack; - } - } - - return stack; + const parent = Object.getPrototypeOf(value); + if (!stack.startsWith(constructorName)) { + stack = stack.replace(parent, constructorName.trim()); + } else if (parent === null) { + stack = stack.replace('Error', constructorName.trim()); } return stack; }