From 7babffbe6054754b733098c52786a23df4049a74 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Dec 2019 20:57:53 +0100 Subject: [PATCH] util: improve prototype inspection using `inspect()` and `showHidden` The fast path for the prototype inspection had a bug that caused some prototype properties to be skipped that should in fact be inspected. PR-URL: https://github.com/nodejs/node/pull/31113 Reviewed-By: James M Snell Reviewed-By: Rich Trott --- lib/internal/util/inspect.js | 12 +++++------- test/parallel/test-util-inspect.js | 29 +++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 7054e35477e172..11a02e9d15a0ab 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -470,10 +470,10 @@ function getConstructorName(obj, ctx, recurseTimes, protoProps) { typeof descriptor.value === 'function' && descriptor.value.name !== '') { if (protoProps !== undefined && - !builtInObjects.has(descriptor.value.name)) { - const isProto = firstProto !== undefined; + (firstProto !== obj || + !builtInObjects.has(descriptor.value.name))) { addPrototypeProperties( - ctx, tmp, obj, recurseTimes, isProto, protoProps); + ctx, tmp, firstProto || tmp, recurseTimes, protoProps); } return descriptor.value.name; } @@ -511,12 +511,12 @@ function getConstructorName(obj, ctx, recurseTimes, protoProps) { // This function has the side effect of adding prototype properties to the // `output` argument (which is an array). This is intended to highlight user // defined prototype properties. -function addPrototypeProperties(ctx, main, obj, recurseTimes, isProto, output) { +function addPrototypeProperties(ctx, main, obj, recurseTimes, output) { let depth = 0; let keys; let keySet; do { - if (!isProto) { + if (depth !== 0 || main === obj) { obj = ObjectGetPrototypeOf(obj); // Stop as soon as a null prototype is encountered. if (obj === null) { @@ -529,8 +529,6 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, isProto, output) { builtInObjects.has(descriptor.value.name)) { return; } - } else { - isProto = false; } if (depth === 0) { diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index b7c7623410c216..24503304f24101 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1696,7 +1696,8 @@ util.inspect(process); ' 1,', ' 2,', ' [length]: 2', - ' ]', + ' ],', + " [Symbol(Symbol.toStringTag)]: 'Set Iterator'", ' } => [Map Iterator] {', ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', @@ -1708,7 +1709,8 @@ util.inspect(process); ' foo: true', ' }', ' ],', - ' [Circular *1]', + ' [Circular *1],', + " [Symbol(Symbol.toStringTag)]: 'Map Iterator'", ' }', '}' ].join('\n'); @@ -1735,7 +1737,10 @@ util.inspect(process); ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', ' ],', - ' [Set Iterator] { [ 1, 2, [length]: 2 ] } => [Map Iterator] {', + ' [Set Iterator] {', + ' [ 1, 2, [length]: 2 ],', + " [Symbol(Symbol.toStringTag)]: 'Set Iterator'", + ' } => [Map Iterator] {', ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', @@ -1743,7 +1748,8 @@ util.inspect(process); ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', ' ],', - ' [Circular *1]', + ' [Circular *1],', + " [Symbol(Symbol.toStringTag)]: 'Map Iterator'", ' }', '}' ].join('\n'); @@ -1773,7 +1779,9 @@ util.inspect(process); ' [Set Iterator] {', ' [ 1,', ' 2,', - ' [length]: 2 ] } => [Map Iterator] {', + ' [length]: 2 ],', + ' [Symbol(Symbol.toStringTag)]:', + " 'Set Iterator' } => [Map Iterator] {", ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', @@ -1782,7 +1790,9 @@ util.inspect(process); ' [buffer]: ArrayBuffer {', ' byteLength: 0,', ' foo: true } ],', - ' [Circular *1] } }' + ' [Circular *1],', + ' [Symbol(Symbol.toStringTag)]:', + " 'Map Iterator' } }" ].join('\n'); assert.strict.equal(out, expected); @@ -2681,4 +2691,11 @@ assert.strictEqual( ' \x1B[2m[def]: \x1B[36m[Getter/Setter]\x1B[39m\x1B[22m\n' + '}' ); + + const obj = Object.create({ abc: true, def: 5, toString() {} }); + assert.strictEqual( + inspect(obj, { showHidden: true, colors: true }), + '{ \x1B[2mabc: \x1B[33mtrue\x1B[39m\x1B[22m, ' + + '\x1B[2mdef: \x1B[33m5\x1B[39m\x1B[22m }' + ); }