util: unify constructor inspection in util.inspect

This makes sure that an objects constructor name is always returned
in a similar fashion instead of having different outputs depending
on the object shape and the code path taken.

PR-URL: https://github.com/nodejs/node/pull/27733
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Ruben Bridgewater 2019-05-16 13:25:59 +02:00
parent 79e55f0774
commit 370ddefc14
No known key found for this signature in database
GPG Key ID: F07496B3EB3C1762
2 changed files with 60 additions and 15 deletions

View File

@ -418,8 +418,15 @@ function getKeys(value, showHidden) {
return keys; return keys;
} }
function getCtxStyle(constructor, tag) { function getCtxStyle(value, constructor, tag) {
return constructor || tag || 'Object'; let fallback = '';
if (constructor === null) {
fallback = internalGetConstructorName(value);
if (fallback === tag) {
fallback = 'Object';
}
}
return getPrefix(constructor, tag, fallback);
} }
function formatProxy(ctx, proxy, recurseTimes) { function formatProxy(ctx, proxy, recurseTimes) {
@ -723,25 +730,21 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
formatter = formatIterator; formatter = formatIterator;
// Handle other regular objects again. // Handle other regular objects again.
} else { } else {
let fallback = '';
if (constructor === null) {
fallback = internalGetConstructorName(value);
if (fallback === tag) {
fallback = 'Object';
}
}
if (keys.length === 0) { if (keys.length === 0) {
if (isExternal(value)) if (isExternal(value))
return ctx.stylize('[External]', 'special'); return ctx.stylize('[External]', 'special');
return `${getPrefix(constructor, tag, fallback)}{}`; return `${getCtxStyle(value, constructor, tag)}{}`;
} }
braces[0] = `${getPrefix(constructor, tag, fallback)}{`; braces[0] = `${getCtxStyle(value, constructor, tag)}{`;
} }
} }
} }
if (recurseTimes > ctx.depth && ctx.depth !== null) { if (recurseTimes > ctx.depth && ctx.depth !== null) {
return ctx.stylize(`[${getCtxStyle(constructor, tag)}]`, 'special'); let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1);
if (constructor !== null)
constructorName = `[${constructorName}]`;
return ctx.stylize(constructorName, 'special');
} }
recurseTimes += 1; recurseTimes += 1;
@ -756,7 +759,8 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
formatProperty(ctx, value, recurseTimes, keys[i], extrasType)); formatProperty(ctx, value, recurseTimes, keys[i], extrasType));
} }
} catch (err) { } catch (err) {
return handleMaxCallStackSize(ctx, err, constructor, tag, indentationLvl); const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1);
return handleMaxCallStackSize(ctx, err, constructorName, indentationLvl);
} }
ctx.seen.pop(); ctx.seen.pop();
@ -1016,12 +1020,12 @@ function groupArrayElements(ctx, output) {
return output; return output;
} }
function handleMaxCallStackSize(ctx, err, constructor, tag, indentationLvl) { function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) {
if (isStackOverflowError(err)) { if (isStackOverflowError(err)) {
ctx.seen.pop(); ctx.seen.pop();
ctx.indentationLvl = indentationLvl; ctx.indentationLvl = indentationLvl;
return ctx.stylize( return ctx.stylize(
`[${getCtxStyle(constructor, tag)}: Inspection interrupted ` + `[${constructorName}: Inspection interrupted ` +
'prematurely. Maximum call stack size exceeded.]', 'prematurely. Maximum call stack size exceeded.]',
'special' 'special'
); );

View File

@ -1094,6 +1094,47 @@ if (typeof Symbol !== 'undefined') {
'[Set Iterator] { 1, ... 1 more item, extra: true }'); '[Set Iterator] { 1, ... 1 more item, extra: true }');
} }
// Minimal inspection should still return as much information as possible about
// the constructor and Symbol.toStringTag.
{
class Foo {
get [Symbol.toStringTag]() {
return 'ABC';
}
}
const a = new Foo();
assert.strictEqual(inspect(a, { depth: -1 }), 'Foo [ABC] {}');
a.foo = true;
assert.strictEqual(inspect(a, { depth: -1 }), '[Foo [ABC]]');
Object.defineProperty(a, Symbol.toStringTag, {
value: 'Foo',
configurable: true,
writable: true
});
assert.strictEqual(inspect(a, { depth: -1 }), '[Foo]');
delete a[Symbol.toStringTag];
Object.setPrototypeOf(a, null);
assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype]');
delete a.foo;
assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype] {}');
Object.defineProperty(a, Symbol.toStringTag, {
value: 'ABC',
configurable: true
});
assert.strictEqual(
inspect(a, { depth: -1 }),
'[Foo: null prototype] [ABC] {}'
);
Object.defineProperty(a, Symbol.toStringTag, {
value: 'Foo',
configurable: true
});
assert.strictEqual(
inspect(a, { depth: -1 }),
'[Object: null prototype] [Foo] {}'
);
}
// Test alignment of items in container. // Test alignment of items in container.
// Assumes that the first numeric character is the start of an item. // Assumes that the first numeric character is the start of an item.
{ {