Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: add (typed) array length to the default output #31027

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 27 additions & 34 deletions lib/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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]}]`;
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-util-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
67 changes: 35 additions & 32 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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` +
Expand All @@ -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 ]`
);
});

Expand All @@ -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` +
Expand All @@ -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 ]`
);
});

Expand Down Expand Up @@ -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' +
Expand Down Expand Up @@ -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]])),
Expand Down Expand Up @@ -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]'));
}
Expand Down Expand Up @@ -1681,7 +1681,7 @@ util.inspect(process);
' ],',
' [length]: 1',
' ]',
' } => Uint8Array [',
' } => Uint8Array(0) [',
' [BYTES_PER_ELEMENT]: 1,',
' [length]: 0,',
' [byteLength]: 0,',
Expand All @@ -1698,7 +1698,7 @@ util.inspect(process);
' [length]: 2',
' ]',
' } => <ref *1> [Map Iterator] {',
' Uint8Array [',
' Uint8Array(0) [',
' [BYTES_PER_ELEMENT]: 1,',
' [length]: 0,',
' [byteLength]: 0,',
Expand Down Expand Up @@ -1728,15 +1728,15 @@ util.inspect(process);
' ],',
' [length]: 1',
' ]',
' } => Uint8Array [',
' } => Uint8Array(0) [',
' [BYTES_PER_ELEMENT]: 1,',
' [length]: 0,',
' [byteLength]: 0,',
' [byteOffset]: 0,',
' [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
' ],',
' [Set Iterator] { [ 1, 2, [length]: 2 ] } => <ref *1> [Map Iterator] {',
' Uint8Array [',
' Uint8Array(0) [',
' [BYTES_PER_ELEMENT]: 1,',
' [length]: 0,',
' [byteLength]: 0,',
Expand All @@ -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,',
Expand All @@ -1774,7 +1774,7 @@ util.inspect(process);
' [ 1,',
' 2,',
' [length]: 2 ] } => <ref *1> [Map Iterator] {',
' Uint8Array [',
' Uint8Array(0) [',
' [BYTES_PER_ELEMENT]: 1,',
' [length]: 0,',
' [byteLength]: 0,',
Expand Down Expand Up @@ -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 { <pending> }'],
[new WeakSet(), 'WeakSet { <items unknown> }'],
[new WeakMap(), 'WeakMap { <items unknown> }'],
Expand All @@ -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] { <pending> }'],
[new WeakSet(), '[WeakSet: null prototype] { <items unknown> }'],
[new WeakMap(), '[WeakMap: null prototype] { <items unknown> }'],
[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}'],
Expand Down Expand Up @@ -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);
Expand All @@ -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.
{
Expand Down Expand Up @@ -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() {}
Expand Down