Skip to content

Commit cc19d08

Browse files
BridgeARMylesBorins
authored andcommitted
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. Backport-PR-URL: #31431 PR-URL: #31027 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Anto Aravinth <[email protected]>
1 parent 3b9360d commit cc19d08

6 files changed

+69
-73
lines changed

lib/internal/util/inspect.js

+25-32
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ const setSizeGetter = uncurryThis(
119119
ObjectGetOwnPropertyDescriptor(SetPrototype, 'size').get);
120120
const mapSizeGetter = uncurryThis(
121121
ObjectGetOwnPropertyDescriptor(MapPrototype, 'size').get);
122+
const typedArraySizeGetter = uncurryThis(
123+
ObjectGetOwnPropertyDescriptor(
124+
ObjectGetPrototypeOf(Uint8Array.prototype), 'length').get);
122125

123126
let hexSlice;
124127

@@ -564,18 +567,18 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, isProto, output) {
564567
} while (++depth !== 3);
565568
}
566569

567-
function getPrefix(constructor, tag, fallback) {
570+
function getPrefix(constructor, tag, fallback, size = '') {
568571
if (constructor === null) {
569572
if (tag !== '') {
570-
return `[${fallback}: null prototype] [${tag}] `;
573+
return `[${fallback}${size}: null prototype] [${tag}] `;
571574
}
572-
return `[${fallback}: null prototype] `;
575+
return `[${fallback}${size}: null prototype] `;
573576
}
574577

575578
if (tag !== '' && constructor !== tag) {
576-
return `${constructor} [${tag}] `;
579+
return `${constructor}${size} [${tag}] `;
577580
}
578-
return `${constructor} `;
581+
return `${constructor}${size} `;
579582
}
580583

581584
// Look up the keys of the object.
@@ -760,58 +763,48 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
760763
if (value[SymbolIterator] || constructor === null) {
761764
noIterator = false;
762765
if (ArrayIsArray(value)) {
763-
keys = getOwnNonIndexProperties(value, filter);
764766
// Only set the constructor for non ordinary ("Array [...]") arrays.
765-
const prefix = getPrefix(constructor, tag, 'Array');
766-
braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']'];
767+
const prefix = (constructor !== 'Array' || tag !== '') ?
768+
getPrefix(constructor, tag, 'Array', `(${value.length})`) :
769+
'';
770+
keys = getOwnNonIndexProperties(value, filter);
771+
braces = [`${prefix}[`, ']'];
767772
if (value.length === 0 && keys.length === 0 && protoProps === undefined)
768773
return `${braces[0]}]`;
769774
extrasType = kArrayExtrasType;
770775
formatter = formatArray;
771776
} else if (isSet(value)) {
772777
const size = setSizeGetter(value);
778+
const prefix = getPrefix(constructor, tag, 'Set');
773779
keys = getKeys(value, ctx.showHidden);
774-
let prefix = '';
775-
if (constructor !== null) {
776-
if (constructor === tag)
777-
tag = '';
778-
prefix = getPrefix(`${constructor}`, tag, '');
779-
formatter = formatSet.bind(null, value, size);
780-
} else {
781-
prefix = getPrefix(constructor, tag, 'Set');
782-
formatter = formatSet.bind(null, SetPrototypeValues(value), size);
783-
}
780+
formatter = constructor !== null ?
781+
formatSet.bind(null, value, size) :
782+
formatSet.bind(null, SetPrototypeValues(value), size);
784783
if (size === 0 && keys.length === 0 && protoProps === undefined)
785784
return `${prefix}{}`;
786785
braces = [`${prefix}{`, '}'];
787786
} else if (isMap(value)) {
788787
const size = mapSizeGetter(value);
788+
const prefix = getPrefix(constructor, tag, 'Map');
789789
keys = getKeys(value, ctx.showHidden);
790-
let prefix = '';
791-
if (constructor !== null) {
792-
if (constructor === tag)
793-
tag = '';
794-
prefix = getPrefix(`${constructor}`, tag, '');
795-
formatter = formatMap.bind(null, value, size);
796-
} else {
797-
prefix = getPrefix(constructor, tag, 'Map');
798-
formatter = formatMap.bind(null, MapPrototypeEntries(value), size);
799-
}
790+
formatter = constructor !== null ?
791+
formatMap.bind(null, value, size) :
792+
formatMap.bind(null, MapPrototypeEntries(value), size);
800793
if (size === 0 && keys.length === 0 && protoProps === undefined)
801794
return `${prefix}{}`;
802795
braces = [`${prefix}{`, '}'];
803796
} else if (isTypedArray(value)) {
804797
keys = getOwnNonIndexProperties(value, filter);
805798
let bound = value;
806-
let prefix = '';
799+
let fallback = '';
807800
if (constructor === null) {
808801
const constr = findTypedConstructor(value);
809-
prefix = getPrefix(constructor, tag, constr.name);
802+
fallback = constr.name;
810803
// Reconstruct the array information.
811804
bound = new constr(value);
812-
} else {
813-
prefix = getPrefix(constructor, tag);
814805
}
806+
const size = typedArraySizeGetter(value);
807+
const prefix = getPrefix(constructor, tag, fallback, `(${size})`);
815808
braces = [`${prefix}[`, ']'];
816809
if (value.length === 0 && keys.length === 0 && !ctx.showHidden)
817810
return `${braces[0]}]`;

test/parallel/test-assert-deep.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ assert.throws(
5151
{
5252
code: 'ERR_ASSERTION',
5353
message: `${defaultMsgStartFull} ... Lines skipped\n\n` +
54-
'+ Uint8Array [\n' +
55-
'- Buffer [Uint8Array] [\n 120,\n...\n 122,\n 10\n ]'
54+
'+ Uint8Array(4) [\n' +
55+
'- Buffer(4) [Uint8Array] [\n 120,\n...\n 122,\n 10\n ]'
5656
}
5757
);
5858
assert.deepEqual(arr, buf);
@@ -66,7 +66,7 @@ assert.deepEqual(arr, buf);
6666
{
6767
code: 'ERR_ASSERTION',
6868
message: `${defaultMsgStartFull}\n\n` +
69-
' Buffer [Uint8Array] [\n' +
69+
' Buffer(4) [Uint8Array] [\n' +
7070
' 120,\n' +
7171
' 121,\n' +
7272
' 122,\n' +
@@ -86,7 +86,7 @@ assert.deepEqual(arr, buf);
8686
{
8787
code: 'ERR_ASSERTION',
8888
message: `${defaultMsgStartFull}\n\n` +
89-
' Uint8Array [\n' +
89+
' Uint8Array(4) [\n' +
9090
' 120,\n' +
9191
' 121,\n' +
9292
' 122,\n' +

test/parallel/test-buffer-inspect.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ b.inspect = undefined;
5858
b.prop = new Uint8Array(0);
5959
assert.strictEqual(
6060
util.inspect(b),
61-
'<Buffer 31 32, inspect: undefined, prop: Uint8Array []>'
61+
'<Buffer 31 32, inspect: undefined, prop: Uint8Array(0) []>'
6262
);
6363

6464
b = Buffer.alloc(0);

test/parallel/test-fs-read-empty-buffer.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ assert.throws(
1515
{
1616
code: 'ERR_INVALID_ARG_VALUE',
1717
message: 'The argument \'buffer\' is empty and cannot be written. ' +
18-
'Received Uint8Array []'
18+
'Received Uint8Array(0) []'
1919
}
2020
);
2121

@@ -24,7 +24,7 @@ assert.throws(
2424
{
2525
code: 'ERR_INVALID_ARG_VALUE',
2626
message: 'The argument \'buffer\' is empty and cannot be written. ' +
27-
'Received Uint8Array []'
27+
'Received Uint8Array(0) []'
2828
}
2929
);
3030

@@ -35,7 +35,7 @@ assert.throws(
3535
{
3636
code: 'ERR_INVALID_ARG_VALUE',
3737
message: 'The argument \'buffer\' is empty and cannot be written. ' +
38-
'Received Uint8Array []'
38+
'Received Uint8Array(0) []'
3939
}
4040
);
4141
})();

test/parallel/test-util-format.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ assert.strictEqual(util.format('%s', () => 5), '() => 5');
158158
class Foobar extends Array { aaa = true; }
159159
assert.strictEqual(
160160
util.format('%s', new Foobar(5)),
161-
'Foobar [ <5 empty items>, aaa: true ]'
161+
'Foobar(5) [ <5 empty items>, aaa: true ]'
162162
);
163163

164164
// Subclassing:

test/parallel/test-util-inspect.js

+35-32
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 1),
126126
'{ a: { b: [Object] } }');
127127
assert.strictEqual(util.inspect({ 'a': { 'b': ['c'] } }, false, 1),
128128
'{ a: { b: [Array] } }');
129-
assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array []');
129+
assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array(0) []');
130130
assert(inspect(new Uint8Array(0), { showHidden: true }).includes('[buffer]'));
131131
assert.strictEqual(
132132
util.inspect(
@@ -263,7 +263,7 @@ assert(!/Object/.test(
263263
array[1] = 97;
264264
assert.strictEqual(
265265
util.inspect(array, { showHidden: true }),
266-
`${constructor.name} [\n` +
266+
`${constructor.name}(${length}) [\n` +
267267
' 65,\n' +
268268
' 97,\n' +
269269
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
@@ -273,7 +273,7 @@ assert(!/Object/.test(
273273
` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
274274
assert.strictEqual(
275275
util.inspect(array, false),
276-
`${constructor.name} [ 65, 97 ]`
276+
`${constructor.name}(${length}) [ 65, 97 ]`
277277
);
278278
});
279279

@@ -297,7 +297,7 @@ assert(!/Object/.test(
297297
array[1] = 97;
298298
assert.strictEqual(
299299
util.inspect(array, true),
300-
`${constructor.name} [\n` +
300+
`${constructor.name}(${length}) [\n` +
301301
' 65,\n' +
302302
' 97,\n' +
303303
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
@@ -307,7 +307,7 @@ assert(!/Object/.test(
307307
` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
308308
assert.strictEqual(
309309
util.inspect(array, false),
310-
`${constructor.name} [ 65, 97 ]`
310+
`${constructor.name}(${length}) [ 65, 97 ]`
311311
);
312312
});
313313

@@ -397,11 +397,11 @@ assert.strictEqual(
397397
arr[49] = 'I win';
398398
assert.strictEqual(
399399
util.inspect(arr),
400-
"CustomArray [ <49 empty items>, 'I win' ]"
400+
"CustomArray(50) [ <49 empty items>, 'I win' ]"
401401
);
402402
assert.strictEqual(
403403
util.inspect(arr, { showHidden: true }),
404-
'CustomArray [\n' +
404+
'CustomArray(50) [\n' +
405405
' <49 empty items>,\n' +
406406
" 'I win',\n" +
407407
' [length]: 50,\n' +
@@ -1291,7 +1291,7 @@ if (typeof Symbol !== 'undefined') {
12911291
assert.strictEqual(util.inspect(x),
12921292
'ObjectSubclass { foo: 42 }');
12931293
assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)),
1294-
'ArraySubclass [ 1, 2, 3 ]');
1294+
'ArraySubclass(3) [ 1, 2, 3 ]');
12951295
assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])),
12961296
'SetSubclass [Set] { 1, 2, 3 }');
12971297
assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])),
@@ -1387,7 +1387,7 @@ if (typeof Symbol !== 'undefined') {
13871387
assert(util.inspect(x).endsWith('1 more item\n]'));
13881388
assert(!util.inspect(x, { maxArrayLength: 101 }).includes('1 more item'));
13891389
assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
1390-
'Uint8Array [ ... 101 more items ]');
1390+
'Uint8Array(101) [ ... 101 more items ]');
13911391
assert(!util.inspect(x, { maxArrayLength: null }).includes('1 more item'));
13921392
assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]'));
13931393
}
@@ -1672,7 +1672,7 @@ util.inspect(process);
16721672
' ],',
16731673
' [length]: 1',
16741674
' ]',
1675-
' } => Uint8Array [',
1675+
' } => Uint8Array(0) [',
16761676
' [BYTES_PER_ELEMENT]: 1,',
16771677
' [length]: 0,',
16781678
' [byteLength]: 0,',
@@ -1689,7 +1689,7 @@ util.inspect(process);
16891689
' [length]: 2',
16901690
' ]',
16911691
' } => [Map Iterator] {',
1692-
' Uint8Array [',
1692+
' Uint8Array(0) [',
16931693
' [BYTES_PER_ELEMENT]: 1,',
16941694
' [length]: 0,',
16951695
' [byteLength]: 0,',
@@ -1720,15 +1720,15 @@ util.inspect(process);
17201720
' ],',
17211721
' [length]: 1',
17221722
' ]',
1723-
' } => Uint8Array [',
1723+
' } => Uint8Array(0) [',
17241724
' [BYTES_PER_ELEMENT]: 1,',
17251725
' [length]: 0,',
17261726
' [byteLength]: 0,',
17271727
' [byteOffset]: 0,',
17281728
' [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
17291729
' ],',
17301730
' [Set Iterator] { [ 1, 2, [length]: 2 ] } => [Map Iterator] {',
1731-
' Uint8Array [',
1731+
' Uint8Array(0) [',
17321732
' [BYTES_PER_ELEMENT]: 1,',
17331733
' [length]: 0,',
17341734
' [byteLength]: 0,',
@@ -1756,7 +1756,7 @@ util.inspect(process);
17561756
' [length]: 2 ],',
17571757
' [size]: 1 },',
17581758
' [length]: 2 ],',
1759-
' [length]: 1 ] } => Uint8Array [',
1759+
' [length]: 1 ] } => Uint8Array(0) [',
17601760
' [BYTES_PER_ELEMENT]: 1,',
17611761
' [length]: 0,',
17621762
' [byteLength]: 0,',
@@ -1768,7 +1768,7 @@ util.inspect(process);
17681768
' [ 1,',
17691769
' 2,',
17701770
' [length]: 2 ] } => [Map Iterator] {',
1771-
' Uint8Array [',
1771+
' Uint8Array(0) [',
17721772
' [BYTES_PER_ELEMENT]: 1,',
17731773
' [length]: 0,',
17741774
' [byteLength]: 0,',
@@ -1946,7 +1946,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
19461946
[new Set([1, 2]).entries(), '[Set Entries] { [ 1, 1 ], [ 2, 2 ] }'],
19471947
[new Map([[1, 2]]).keys(), '[Map Iterator] { 1 }'],
19481948
[new Date(2000), '1970-01-01T00:00:02.000Z'],
1949-
[new Uint8Array(2), 'Uint8Array [ 0, 0 ]'],
1949+
[new Uint8Array(2), 'Uint8Array(2) [ 0, 0 ]'],
19501950
[new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { <pending> }'],
19511951
[new WeakSet(), 'WeakSet { <items unknown> }'],
19521952
[new WeakMap(), 'WeakMap { <items unknown> }'],
@@ -1972,23 +1972,23 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
19721972

19731973
// Verify that having no prototype still produces nice results.
19741974
[
1975-
[[1, 3, 4], '[Array: null prototype] [ 1, 3, 4 ]'],
1975+
[[1, 3, 4], '[Array(3): null prototype] [ 1, 3, 4 ]'],
19761976
[new Set([1, 2]), '[Set: null prototype] { 1, 2 }'],
19771977
[new Map([[1, 2]]), '[Map: null prototype] { 1 => 2 }'],
19781978
[new Promise((resolve) => setTimeout(resolve, 10)),
19791979
'[Promise: null prototype] { <pending> }'],
19801980
[new WeakSet(), '[WeakSet: null prototype] { <items unknown> }'],
19811981
[new WeakMap(), '[WeakMap: null prototype] { <items unknown> }'],
1982-
[new Uint8Array(2), '[Uint8Array: null prototype] [ 0, 0 ]'],
1983-
[new Uint16Array(2), '[Uint16Array: null prototype] [ 0, 0 ]'],
1984-
[new Uint32Array(2), '[Uint32Array: null prototype] [ 0, 0 ]'],
1985-
[new Int8Array(2), '[Int8Array: null prototype] [ 0, 0 ]'],
1986-
[new Int16Array(2), '[Int16Array: null prototype] [ 0, 0 ]'],
1987-
[new Int32Array(2), '[Int32Array: null prototype] [ 0, 0 ]'],
1988-
[new Float32Array(2), '[Float32Array: null prototype] [ 0, 0 ]'],
1989-
[new Float64Array(2), '[Float64Array: null prototype] [ 0, 0 ]'],
1990-
[new BigInt64Array(2), '[BigInt64Array: null prototype] [ 0n, 0n ]'],
1991-
[new BigUint64Array(2), '[BigUint64Array: null prototype] [ 0n, 0n ]'],
1982+
[new Uint8Array(2), '[Uint8Array(2): null prototype] [ 0, 0 ]'],
1983+
[new Uint16Array(2), '[Uint16Array(2): null prototype] [ 0, 0 ]'],
1984+
[new Uint32Array(2), '[Uint32Array(2): null prototype] [ 0, 0 ]'],
1985+
[new Int8Array(2), '[Int8Array(2): null prototype] [ 0, 0 ]'],
1986+
[new Int16Array(2), '[Int16Array(2): null prototype] [ 0, 0 ]'],
1987+
[new Int32Array(2), '[Int32Array(2): null prototype] [ 0, 0 ]'],
1988+
[new Float32Array(2), '[Float32Array(2): null prototype] [ 0, 0 ]'],
1989+
[new Float64Array(2), '[Float64Array(2): null prototype] [ 0, 0 ]'],
1990+
[new BigInt64Array(2), '[BigInt64Array(2): null prototype] [ 0n, 0n ]'],
1991+
[new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'],
19921992
[new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' +
19931993
' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' +
19941994
' byteLength: undefined\n}'],
@@ -2026,8 +2026,10 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
20262026
class Foo extends base {}
20272027
const value = new Foo(...input);
20282028
const symbol = value[Symbol.toStringTag];
2029-
const expected = `Foo ${symbol ? `[${symbol}] ` : ''}${rawExpected}`;
2030-
const expectedWithoutProto = `[${base.name}: null prototype] ${rawExpected}`;
2029+
const size = base.name.includes('Array') ? `(${input[0]})` : '';
2030+
const expected = `Foo${size} ${symbol ? `[${symbol}] ` : ''}${rawExpected}`;
2031+
const expectedWithoutProto =
2032+
`[${base.name}${size}: null prototype] ${rawExpected}`;
20312033
assert.strictEqual(util.inspect(value), expected);
20322034
value.foo = 'bar';
20332035
assert.notStrictEqual(util.inspect(value), expected);
@@ -2050,8 +2052,9 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
20502052
assert.strictEqual(inspect(1n), '1n');
20512053
assert.strictEqual(inspect(Object(-1n)), '[BigInt: -1n]');
20522054
assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]');
2053-
assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array [ 0n ]');
2054-
assert.strictEqual(inspect(new BigUint64Array([0n])), 'BigUint64Array [ 0n ]');
2055+
assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array(1) [ 0n ]');
2056+
assert.strictEqual(
2057+
inspect(new BigUint64Array([0n])), 'BigUint64Array(1) [ 0n ]');
20552058

20562059
// Verify non-enumerable keys get escaped.
20572060
{
@@ -2170,7 +2173,7 @@ assert.strictEqual(
21702173
Object.setPrototypeOf(obj, value);
21712174
assert.strictEqual(
21722175
util.inspect(obj),
2173-
'Object <[Array: null prototype] []> { a: true }'
2176+
'Object <[Array(0): null prototype] []> { a: true }'
21742177
);
21752178

21762179
function StorageObject() {}

0 commit comments

Comments
 (0)