diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index db54e232aa8a5b..4def103f42adeb 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -560,22 +560,20 @@ function formatValue(ctx, value, recurseTimes, typedArray) { return formatRaw(ctx, value, recurseTimes, typedArray); } -function setIteratorBraces(type, tag) { - if (tag !== `${type} Iterator`) { - if (tag !== '') - tag += '] ['; - tag += `${type} Iterator`; - } - return [`[${tag}] {`, '}']; -} - function formatRaw(ctx, value, recurseTimes, typedArray) { let keys; const constructor = getConstructorName(value, ctx); let tag = value[Symbol.toStringTag]; - if (typeof tag !== 'string') + // Only list the tag in case it's non-enumerable / not an own property. + // Otherwise we'd print this twice. + if (typeof tag !== 'string' || + tag !== '' && + (ctx.showHidden ? hasOwnProperty : propertyIsEnumerable)( + value, Symbol.toStringTag + )) { tag = ''; + } let base = ''; let formatter = getEmptyFormatArray; let braces; @@ -623,11 +621,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { extrasType = kArrayExtrasType; } else if (isMapIterator(value)) { keys = getKeys(value, ctx.showHidden); - braces = setIteratorBraces('Map', tag); + braces = getIteratorBraces('Map', tag); formatter = formatIterator; } else if (isSetIterator(value)) { keys = getKeys(value, ctx.showHidden); - braces = setIteratorBraces('Set', tag); + braces = getIteratorBraces('Set', tag); formatter = formatIterator; } else { noIterator = true; @@ -747,10 +745,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } } if (isMapIterator(value)) { - braces = setIteratorBraces('Map', tag); + braces = getIteratorBraces('Map', tag); formatter = formatIterator; } else if (isSetIterator(value)) { - braces = setIteratorBraces('Set', tag); + braces = getIteratorBraces('Set', tag); formatter = formatIterator; // Handle other regular objects again. } else if (keys.length === 0) { @@ -811,6 +809,15 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return res; } +function getIteratorBraces(type, tag) { + if (tag !== `${type} Iterator`) { + if (tag !== '') + tag += '] ['; + tag += `${type} Iterator`; + } + return [`[${tag}] {`, '}']; +} + function formatError(err, constructor, tag, ctx) { // TODO(BridgeAR): Always show the error code if present. let stack = err.stack || ErrorPrototype.toString(err); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index c1b6e3f98d4480..737f86b30ff8e0 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1264,8 +1264,20 @@ util.inspect(process); { // @@toStringTag - assert.strictEqual(util.inspect({ [Symbol.toStringTag]: 'a' }), - "Object [a] { [Symbol(Symbol.toStringTag)]: 'a' }"); + const obj = { [Symbol.toStringTag]: 'a' }; + assert.strictEqual( + util.inspect(obj), + "{ [Symbol(Symbol.toStringTag)]: 'a' }" + ); + Object.defineProperty(obj, Symbol.toStringTag, { + value: 'a', + enumerable: false + }); + assert.strictEqual(util.inspect(obj), 'Object [a] {}'); + assert.strictEqual( + util.inspect(obj, { showHidden: true }), + "{ [Symbol(Symbol.toStringTag)]: 'a' }" + ); class Foo { constructor() {