From f885f509ac6a6ca77e28ee908656bae79d172123 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 19 Dec 2019 16:12:30 +0100 Subject: [PATCH 1/2] util: add (typed) array length to the default output Align the inspect output with the one used in the Chrome dev tools. A recent survey outlined that most users prefer to see the number of set and map entries. This should count as well for array sizes. The size is only added to regular arrays in case the constructor is not the default constructor. Typed arrays always indicate their size. --- lib/internal/util/inspect.js | 61 ++++++++++++--------------- test/parallel/test-util-format.js | 2 +- test/parallel/test-util-inspect.js | 67 ++++++++++++++++-------------- 3 files changed, 63 insertions(+), 67 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 701cc34cebff15..7054e35477e172 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -117,6 +117,9 @@ const setSizeGetter = uncurryThis( ObjectGetOwnPropertyDescriptor(SetPrototype, 'size').get); const mapSizeGetter = uncurryThis( ObjectGetOwnPropertyDescriptor(MapPrototype, 'size').get); +const typedArraySizeGetter = uncurryThis( + ObjectGetOwnPropertyDescriptor( + ObjectGetPrototypeOf(Uint8Array.prototype), 'length').get); let hexSlice; @@ -567,18 +570,18 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, isProto, output) { } while (++depth !== 3); } -function getPrefix(constructor, tag, fallback) { +function getPrefix(constructor, tag, fallback, size = '') { if (constructor === null) { if (tag !== '') { - return `[${fallback}: null prototype] [${tag}] `; + return `[${fallback}${size}: null prototype] [${tag}] `; } - return `[${fallback}: null prototype] `; + return `[${fallback}${size}: null prototype] `; } if (tag !== '' && constructor !== tag) { - return `${constructor} [${tag}] `; + return `${constructor}${size} [${tag}] `; } - return `${constructor} `; + return `${constructor}${size} `; } // Look up the keys of the object. @@ -763,58 +766,48 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { if (value[SymbolIterator] || constructor === null) { noIterator = false; if (ArrayIsArray(value)) { - keys = getOwnNonIndexProperties(value, filter); // Only set the constructor for non ordinary ("Array [...]") arrays. - const prefix = getPrefix(constructor, tag, 'Array'); - braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']']; + const prefix = (constructor !== 'Array' || tag !== '') ? + getPrefix(constructor, tag, 'Array', `(${value.length})`) : + ''; + keys = getOwnNonIndexProperties(value, filter); + braces = [`${prefix}[`, ']']; if (value.length === 0 && keys.length === 0 && protoProps === undefined) return `${braces[0]}]`; extrasType = kArrayExtrasType; formatter = formatArray; } else if (isSet(value)) { const size = setSizeGetter(value); + const prefix = getPrefix(constructor, tag, 'Set', `(${size})`); keys = getKeys(value, ctx.showHidden); - let prefix = ''; - if (constructor !== null) { - if (constructor === tag) - tag = ''; - prefix = getPrefix(`${constructor}(${size})`, tag, ''); - formatter = formatSet.bind(null, value, size); - } else { - prefix = getPrefix(constructor, tag, `Set(${size})`); - formatter = formatSet.bind(null, SetPrototypeValues(value), size); - } + formatter = constructor !== null ? + formatSet.bind(null, value) : + formatSet.bind(null, SetPrototypeValues(value)); if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; } else if (isMap(value)) { const size = mapSizeGetter(value); + const prefix = getPrefix(constructor, tag, 'Map', `(${size})`); keys = getKeys(value, ctx.showHidden); - let prefix = ''; - if (constructor !== null) { - if (constructor === tag) - tag = ''; - prefix = getPrefix(`${constructor}(${size})`, tag, ''); - formatter = formatMap.bind(null, value, size); - } else { - prefix = getPrefix(constructor, tag, `Map(${size})`); - formatter = formatMap.bind(null, MapPrototypeEntries(value), size); - } + formatter = constructor !== null ? + formatMap.bind(null, value) : + formatMap.bind(null, MapPrototypeEntries(value)); if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; } else if (isTypedArray(value)) { keys = getOwnNonIndexProperties(value, filter); let bound = value; - let prefix = ''; + let fallback = ''; if (constructor === null) { const constr = findTypedConstructor(value); - prefix = getPrefix(constructor, tag, constr.name); + fallback = constr.name; // Reconstruct the array information. bound = new constr(value); - } else { - prefix = getPrefix(constructor, tag); } + const size = typedArraySizeGetter(value); + const prefix = getPrefix(constructor, tag, fallback, `(${size})`); braces = [`${prefix}[`, ']']; if (value.length === 0 && keys.length === 0 && !ctx.showHidden) return `${braces[0]}]`; @@ -1430,7 +1423,7 @@ function formatTypedArray(value, ctx, ignored, recurseTimes) { return output; } -function formatSet(value, size, ctx, ignored, recurseTimes) { +function formatSet(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; for (const v of value) { @@ -1440,7 +1433,7 @@ function formatSet(value, size, ctx, ignored, recurseTimes) { return output; } -function formatMap(value, size, ctx, ignored, recurseTimes) { +function formatMap(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; for (const [k, v] of value) { diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index e07ec6d6a34c2f..0599fda1d63456 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -158,7 +158,7 @@ assert.strictEqual(util.format('%s', () => 5), '() => 5'); class Foobar extends Array { aaa = true; } assert.strictEqual( util.format('%s', new Foobar(5)), - 'Foobar [ <5 empty items>, aaa: true ]' + 'Foobar(5) [ <5 empty items>, aaa: true ]' ); // Subclassing: diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 3b09e0c0fa20c2..5b08d34b9af643 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -127,7 +127,7 @@ assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 1), '{ a: { b: [Object] } }'); assert.strictEqual(util.inspect({ 'a': { 'b': ['c'] } }, false, 1), '{ a: { b: [Array] } }'); -assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array []'); +assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array(0) []'); assert(inspect(new Uint8Array(0), { showHidden: true }).includes('[buffer]')); assert.strictEqual( util.inspect( @@ -264,7 +264,7 @@ assert(!/Object/.test( array[1] = 97; assert.strictEqual( util.inspect(array, { showHidden: true }), - `${constructor.name} [\n` + + `${constructor.name}(${length}) [\n` + ' 65,\n' + ' 97,\n' + ` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` + @@ -274,7 +274,7 @@ assert(!/Object/.test( ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), - `${constructor.name} [ 65, 97 ]` + `${constructor.name}(${length}) [ 65, 97 ]` ); }); @@ -298,7 +298,7 @@ assert(!/Object/.test( array[1] = 97; assert.strictEqual( util.inspect(array, true), - `${constructor.name} [\n` + + `${constructor.name}(${length}) [\n` + ' 65,\n' + ' 97,\n' + ` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` + @@ -308,7 +308,7 @@ assert(!/Object/.test( ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), - `${constructor.name} [ 65, 97 ]` + `${constructor.name}(${length}) [ 65, 97 ]` ); }); @@ -398,11 +398,11 @@ assert.strictEqual( arr[49] = 'I win'; assert.strictEqual( util.inspect(arr), - "CustomArray [ <49 empty items>, 'I win' ]" + "CustomArray(50) [ <49 empty items>, 'I win' ]" ); assert.strictEqual( util.inspect(arr, { showHidden: true }), - 'CustomArray [\n' + + 'CustomArray(50) [\n' + ' <49 empty items>,\n' + " 'I win',\n" + ' [length]: 50,\n' + @@ -1301,7 +1301,7 @@ if (typeof Symbol !== 'undefined') { assert.strictEqual(util.inspect(x), 'ObjectSubclass { foo: 42 }'); assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)), - 'ArraySubclass [ 1, 2, 3 ]'); + 'ArraySubclass(3) [ 1, 2, 3 ]'); assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])), 'SetSubclass(3) [Set] { 1, 2, 3 }'); assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])), @@ -1397,7 +1397,7 @@ if (typeof Symbol !== 'undefined') { assert(util.inspect(x).endsWith('1 more item\n]')); assert(!util.inspect(x, { maxArrayLength: 101 }).includes('1 more item')); assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), - 'Uint8Array [ ... 101 more items ]'); + 'Uint8Array(101) [ ... 101 more items ]'); assert(!util.inspect(x, { maxArrayLength: null }).includes('1 more item')); assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]')); } @@ -1681,7 +1681,7 @@ util.inspect(process); ' ],', ' [length]: 1', ' ]', - ' } => Uint8Array [', + ' } => Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1698,7 +1698,7 @@ util.inspect(process); ' [length]: 2', ' ]', ' } => [Map Iterator] {', - ' Uint8Array [', + ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1728,7 +1728,7 @@ util.inspect(process); ' ],', ' [length]: 1', ' ]', - ' } => Uint8Array [', + ' } => Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1736,7 +1736,7 @@ util.inspect(process); ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', ' ],', ' [Set Iterator] { [ 1, 2, [length]: 2 ] } => [Map Iterator] {', - ' Uint8Array [', + ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1762,7 +1762,7 @@ util.inspect(process); ' 2,', ' [length]: 2 ] },', ' [length]: 2 ],', - ' [length]: 1 ] } => Uint8Array [', + ' [length]: 1 ] } => Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1774,7 +1774,7 @@ util.inspect(process); ' [ 1,', ' 2,', ' [length]: 2 ] } => [Map Iterator] {', - ' Uint8Array [', + ' Uint8Array(0) [', ' [BYTES_PER_ELEMENT]: 1,', ' [length]: 0,', ' [byteLength]: 0,', @@ -1951,7 +1951,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [new Set([1, 2]).entries(), '[Set Entries] { [ 1, 1 ], [ 2, 2 ] }'], [new Map([[1, 2]]).keys(), '[Map Iterator] { 1 }'], [new Date(2000), '1970-01-01T00:00:02.000Z'], - [new Uint8Array(2), 'Uint8Array [ 0, 0 ]'], + [new Uint8Array(2), 'Uint8Array(2) [ 0, 0 ]'], [new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { }'], [new WeakSet(), 'WeakSet { }'], [new WeakMap(), 'WeakMap { }'], @@ -1977,23 +1977,23 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); // Verify that having no prototype still produces nice results. [ - [[1, 3, 4], '[Array: null prototype] [ 1, 3, 4 ]'], + [[1, 3, 4], '[Array(3): null prototype] [ 1, 3, 4 ]'], [new Set([1, 2]), '[Set(2): null prototype] { 1, 2 }'], [new Map([[1, 2]]), '[Map(1): null prototype] { 1 => 2 }'], [new Promise((resolve) => setTimeout(resolve, 10)), '[Promise: null prototype] { }'], [new WeakSet(), '[WeakSet: null prototype] { }'], [new WeakMap(), '[WeakMap: null prototype] { }'], - [new Uint8Array(2), '[Uint8Array: null prototype] [ 0, 0 ]'], - [new Uint16Array(2), '[Uint16Array: null prototype] [ 0, 0 ]'], - [new Uint32Array(2), '[Uint32Array: null prototype] [ 0, 0 ]'], - [new Int8Array(2), '[Int8Array: null prototype] [ 0, 0 ]'], - [new Int16Array(2), '[Int16Array: null prototype] [ 0, 0 ]'], - [new Int32Array(2), '[Int32Array: null prototype] [ 0, 0 ]'], - [new Float32Array(2), '[Float32Array: null prototype] [ 0, 0 ]'], - [new Float64Array(2), '[Float64Array: null prototype] [ 0, 0 ]'], - [new BigInt64Array(2), '[BigInt64Array: null prototype] [ 0n, 0n ]'], - [new BigUint64Array(2), '[BigUint64Array: null prototype] [ 0n, 0n ]'], + [new Uint8Array(2), '[Uint8Array(2): null prototype] [ 0, 0 ]'], + [new Uint16Array(2), '[Uint16Array(2): null prototype] [ 0, 0 ]'], + [new Uint32Array(2), '[Uint32Array(2): null prototype] [ 0, 0 ]'], + [new Int8Array(2), '[Int8Array(2): null prototype] [ 0, 0 ]'], + [new Int16Array(2), '[Int16Array(2): null prototype] [ 0, 0 ]'], + [new Int32Array(2), '[Int32Array(2): null prototype] [ 0, 0 ]'], + [new Float32Array(2), '[Float32Array(2): null prototype] [ 0, 0 ]'], + [new Float64Array(2), '[Float64Array(2): null prototype] [ 0, 0 ]'], + [new BigInt64Array(2), '[BigInt64Array(2): null prototype] [ 0n, 0n ]'], + [new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'], [new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' + ' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' + ' byteLength: undefined\n}'], @@ -2031,8 +2031,10 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); class Foo extends base {} const value = new Foo(...input); const symbol = value[Symbol.toStringTag]; - const expected = `Foo ${symbol ? `[${symbol}] ` : ''}${rawExpected}`; - const expectedWithoutProto = `[${base.name}: null prototype] ${rawExpected}`; + const size = base.name.includes('Array') ? `(${input[0]})` : ''; + const expected = `Foo${size} ${symbol ? `[${symbol}] ` : ''}${rawExpected}`; + const expectedWithoutProto = + `[${base.name}${size}: null prototype] ${rawExpected}`; assert.strictEqual(util.inspect(value), expected); value.foo = 'bar'; assert.notStrictEqual(util.inspect(value), expected); @@ -2055,8 +2057,9 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); assert.strictEqual(inspect(1n), '1n'); assert.strictEqual(inspect(Object(-1n)), '[BigInt: -1n]'); assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]'); -assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array [ 0n ]'); -assert.strictEqual(inspect(new BigUint64Array([0n])), 'BigUint64Array [ 0n ]'); +assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array(1) [ 0n ]'); +assert.strictEqual( + inspect(new BigUint64Array([0n])), 'BigUint64Array(1) [ 0n ]'); // Verify non-enumerable keys get escaped. { @@ -2175,7 +2178,7 @@ assert.strictEqual( Object.setPrototypeOf(obj, value); assert.strictEqual( util.inspect(obj), - 'Object <[Array: null prototype] []> { a: true }' + 'Object <[Array(0): null prototype] []> { a: true }' ); function StorageObject() {} From 7c3dda8ef11a3c7fec8ff957a954201e2c7af002 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 19 Dec 2019 19:08:04 +0100 Subject: [PATCH 2/2] fixup: failing tests --- test/parallel/test-assert-deep.js | 8 ++++---- test/parallel/test-buffer-inspect.js | 2 +- test/parallel/test-fs-read-empty-buffer.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index fc2f96741ca7ef..454b1920f00aee 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -51,8 +51,8 @@ assert.throws( { code: 'ERR_ASSERTION', message: `${defaultMsgStartFull} ... Lines skipped\n\n` + - '+ Uint8Array [\n' + - '- Buffer [Uint8Array] [\n 120,\n...\n 122,\n 10\n ]' + '+ Uint8Array(4) [\n' + + '- Buffer(4) [Uint8Array] [\n 120,\n...\n 122,\n 10\n ]' } ); assert.deepEqual(arr, buf); @@ -66,7 +66,7 @@ assert.deepEqual(arr, buf); { code: 'ERR_ASSERTION', message: `${defaultMsgStartFull}\n\n` + - ' Buffer [Uint8Array] [\n' + + ' Buffer(4) [Uint8Array] [\n' + ' 120,\n' + ' 121,\n' + ' 122,\n' + @@ -86,7 +86,7 @@ assert.deepEqual(arr, buf); { code: 'ERR_ASSERTION', message: `${defaultMsgStartFull}\n\n` + - ' Uint8Array [\n' + + ' Uint8Array(4) [\n' + ' 120,\n' + ' 121,\n' + ' 122,\n' + diff --git a/test/parallel/test-buffer-inspect.js b/test/parallel/test-buffer-inspect.js index d6ecf6b7fc5c38..1e8212e876344a 100644 --- a/test/parallel/test-buffer-inspect.js +++ b/test/parallel/test-buffer-inspect.js @@ -58,7 +58,7 @@ b.inspect = undefined; b.prop = new Uint8Array(0); assert.strictEqual( util.inspect(b), - '' + '' ); b = Buffer.alloc(0); diff --git a/test/parallel/test-fs-read-empty-buffer.js b/test/parallel/test-fs-read-empty-buffer.js index 2f44b9a1843a6b..8ca08448182d4a 100644 --- a/test/parallel/test-fs-read-empty-buffer.js +++ b/test/parallel/test-fs-read-empty-buffer.js @@ -15,7 +15,7 @@ assert.throws( { code: 'ERR_INVALID_ARG_VALUE', message: 'The argument \'buffer\' is empty and cannot be written. ' + - 'Received Uint8Array []' + 'Received Uint8Array(0) []' } ); @@ -24,7 +24,7 @@ assert.throws( { code: 'ERR_INVALID_ARG_VALUE', message: 'The argument \'buffer\' is empty and cannot be written. ' + - 'Received Uint8Array []' + 'Received Uint8Array(0) []' } ); @@ -35,7 +35,7 @@ assert.throws( { code: 'ERR_INVALID_ARG_VALUE', message: 'The argument \'buffer\' is empty and cannot be written. ' + - 'Received Uint8Array []' + 'Received Uint8Array(0) []' } ); })();