Skip to content

Commit 591923b

Browse files
committed
util: improve display of iterators and weak entries
This adds the number of not visible elements when inspecting iterators while exceeding `maxArrayLength`. It also fixes a edge case with `maxArrayLength` and the map.entries() iterator. Now the whole entry will be visible instead of only the key but not the value of the first entry. Besides that it uses a slighly better algorithm that improves the performance by skipping unnecessary steps.
1 parent 9c50199 commit 591923b

File tree

4 files changed

+89
-70
lines changed

4 files changed

+89
-70
lines changed

lib/console.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,11 @@ Console.prototype.table = function(tabularData, properties) {
367367
const mapIter = isMapIterator(tabularData);
368368
let isKeyValue = false;
369369
let i = 0;
370-
if (mapIter)
371-
[ tabularData, isKeyValue ] = previewEntries(tabularData);
370+
if (mapIter) {
371+
const res = previewEntries(tabularData, true);
372+
tabularData = res[0];
373+
isKeyValue = res[1];
374+
}
372375

373376
if (isKeyValue || isMap(tabularData)) {
374377
const keys = [];
@@ -398,7 +401,7 @@ Console.prototype.table = function(tabularData, properties) {
398401

399402
const setIter = isSetIterator(tabularData);
400403
if (setIter)
401-
[ tabularData ] = previewEntries(tabularData);
404+
tabularData = previewEntries(tabularData);
402405

403406
const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData);
404407
if (setlike) {

lib/util.js

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ const meta = [
118118
'', '', '', '', '', '', '', '', '', '',
119119
'', '', '', '', '', '', '', '\\\\'
120120
];
121+
// Constants to map the iterator state.
122+
const kWeak = 0;
123+
const kIterator = 1;
124+
const kMapEntries = 2;
121125

122126
function addQuotes(str, quotes) {
123127
if (quotes === -1) {
@@ -1017,77 +1021,83 @@ function formatMap(ctx, value, recurseTimes, keys) {
10171021
return output;
10181022
}
10191023

1020-
function formatWeakSet(ctx, value, recurseTimes, keys) {
1024+
function formatSetIterInner(ctx, value, recurseTimes, keys, entries, state) {
10211025
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
1022-
const [ entries ] = previewEntries(value).slice(0, maxArrayLength + 1);
10231026
const maxLength = Math.min(maxArrayLength, entries.length);
10241027
let output = new Array(maxLength);
10251028
for (var i = 0; i < maxLength; ++i)
10261029
output[i] = formatValue(ctx, entries[i], recurseTimes);
1027-
// Sort all entries to have a halfway reliable output (if more entries than
1028-
// retrieved ones exist, we can not reliably return the same output).
1029-
output = output.sort();
1030-
if (entries.length > maxArrayLength)
1031-
output.push('... more items');
1030+
if (state === kWeak) {
1031+
// Sort all entries to have a halfway reliable output (if more entries than
1032+
// retrieved ones exist, we can not reliably return the same output).
1033+
output = output.sort();
1034+
}
1035+
const remaining = entries.length - maxLength;
1036+
if (remaining > 0) {
1037+
output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
1038+
}
10321039
for (i = 0; i < keys.length; i++)
10331040
output.push(formatProperty(ctx, value, recurseTimes, keys[i], 0));
10341041
return output;
10351042
}
10361043

1037-
function formatWeakMap(ctx, value, recurseTimes, keys) {
1044+
function formatMapIterInner(ctx, value, recurseTimes, keys, entries, state) {
10381045
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
1039-
const [ entries ] = previewEntries(value).slice(0, (maxArrayLength + 1) * 2);
10401046
// Entries exist as [key1, val1, key2, val2, ...]
1041-
const remainder = entries.length / 2 > maxArrayLength;
1042-
const len = entries.length / 2 - (remainder ? 1 : 0);
1047+
const len = entries.length / 2;
1048+
const remaining = len - maxArrayLength;
10431049
const maxLength = Math.min(maxArrayLength, len);
10441050
let output = new Array(maxLength);
1045-
for (var i = 0; i < maxLength; i++) {
1051+
let start = '';
1052+
let end = '';
1053+
let middle = ' => ';
1054+
let i = 0;
1055+
if (state === kMapEntries) {
1056+
start = '[ ';
1057+
end = ' ]';
1058+
middle = ', ';
1059+
}
1060+
for (; i < maxLength; i++) {
10461061
const pos = i * 2;
1047-
output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` +
1048-
formatValue(ctx, entries[pos + 1], recurseTimes);
1062+
output[i] = `${start}${formatValue(ctx, entries[pos], recurseTimes)}` +
1063+
`${middle}${formatValue(ctx, entries[pos + 1], recurseTimes)}${end}`;
1064+
}
1065+
if (state === kWeak) {
1066+
// Sort all entries to have a halfway reliable output (if more entries
1067+
// than retrieved ones exist, we can not reliably return the same output).
1068+
output = output.sort();
1069+
}
1070+
if (remaining > 0) {
1071+
output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
10491072
}
1050-
// Sort all entries to have a halfway reliable output (if more entries than
1051-
// retrieved ones exist, we can not reliably return the same output).
1052-
output = output.sort();
1053-
if (remainder > 0)
1054-
output.push('... more items');
10551073
for (i = 0; i < keys.length; i++)
10561074
output.push(formatProperty(ctx, value, recurseTimes, keys[i], 0));
10571075
return output;
10581076
}
10591077

1060-
function zip2(list) {
1061-
const ret = Array(list.length / 2);
1062-
for (var i = 0; i < ret.length; ++i)
1063-
ret[i] = [list[2 * i], list[2 * i + 1]];
1064-
return ret;
1078+
function formatWeakSet(ctx, value, recurseTimes, keys) {
1079+
const entries = previewEntries(value);
1080+
return formatSetIterInner(ctx, value, recurseTimes, keys, entries, kWeak);
10651081
}
10661082

1067-
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
1068-
const output = [];
1069-
var [ entries, isKeyValue ] = previewEntries(value);
1070-
if (isKeyValue)
1071-
entries = zip2(entries);
1072-
for (const entry of entries) {
1073-
if (ctx.maxArrayLength === output.length) {
1074-
output.push('... more items');
1075-
break;
1076-
}
1077-
output.push(formatValue(ctx, entry, recurseTimes));
1078-
}
1079-
for (var n = 0; n < keys.length; n++) {
1080-
output.push(formatProperty(ctx, value, recurseTimes, keys[n], 0));
1081-
}
1082-
return output;
1083+
function formatWeakMap(ctx, value, recurseTimes, keys) {
1084+
const entries = previewEntries(value);
1085+
return formatMapIterInner(ctx, value, recurseTimes, keys, entries, kWeak);
10831086
}
10841087

1085-
function formatMapIterator(ctx, value, recurseTimes, keys) {
1086-
return formatCollectionIterator(ctx, value, recurseTimes, keys);
1088+
function formatSetIterator(ctx, value, recurseTimes, keys) {
1089+
const entries = previewEntries(value);
1090+
return formatSetIterInner(ctx, value, recurseTimes, keys, entries, kIterator);
10871091
}
10881092

1089-
function formatSetIterator(ctx, value, recurseTimes, keys) {
1090-
return formatCollectionIterator(ctx, value, recurseTimes, keys);
1093+
function formatMapIterator(ctx, value, recurseTimes, keys) {
1094+
const [entries, isKeyValue] = previewEntries(value, true);
1095+
if (isKeyValue) {
1096+
return formatMapIterInner(
1097+
ctx, value, recurseTimes, keys, entries, kMapEntries);
1098+
}
1099+
1100+
return formatSetIterInner(ctx, value, recurseTimes, keys, entries, kIterator);
10911101
}
10921102

10931103
function formatPromise(ctx, value, recurseTimes, keys) {

src/node_util.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
5858
Local<Array> entries;
5959
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
6060
return;
61+
// Fast path for WeakMap, WeakSet and Set iterators.
62+
if (args.Length() == 1)
63+
return args.GetReturnValue().Set(entries);
6164
Local<Array> ret = Array::New(env->isolate(), 2);
6265
ret->Set(env->context(), 0, entries).FromJust();
6366
ret->Set(env->context(), 1, v8::Boolean::New(env->isolate(), is_key_value))

test/parallel/test-util-inspect.js

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -447,13 +447,15 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324');
447447
{
448448
const map = new Map();
449449
map.set(1, 2);
450-
const [ vals ] = previewEntries(map.entries());
451-
const valsOutput = [];
452-
for (const o of vals) {
453-
valsOutput.push(o);
454-
}
455-
456-
assert.strictEqual(util.inspect(valsOutput), '[ 1, 2 ]');
450+
// Passing only a single argument to indicate a set iterator.
451+
const valsSetIterator = previewEntries(map.entries());
452+
// Passing through true to indicate a map iterator.
453+
const valsMapIterEntries = previewEntries(map.entries(), true);
454+
const valsMapIterKeys = previewEntries(map.keys(), true);
455+
456+
assert.strictEqual(util.inspect(valsSetIterator), '[ 1, 2 ]');
457+
assert.strictEqual(util.inspect(valsMapIterEntries), '[ [ 1, 2 ], true ]');
458+
assert.strictEqual(util.inspect(valsMapIterKeys), '[ [ 1 ], false ]');
457459
}
458460

459461
// Test for other constructors in different context.
@@ -913,18 +915,19 @@ if (typeof Symbol !== 'undefined') {
913915
// Test Map iterators.
914916
{
915917
const map = new Map([['foo', 'bar']]);
916-
assert.strictEqual(util.inspect(map.keys()), "[Map Iterator] { 'foo' }");
917-
assert.strictEqual(util.inspect(map.values()), "[Map Iterator] { 'bar' }");
918-
assert.strictEqual(util.inspect(map.entries()),
919-
"[Map Iterator] { [ 'foo', 'bar' ] }");
918+
assert.strictEqual(util.inspect(map.keys()), '[Map Iterator] { \'foo\' }');
919+
assert.strictEqual(util.inspect(map.values()), '[Map Iterator] { \'bar\' }');
920+
map.set('A', 'B!');
921+
assert.strictEqual(util.inspect(map.entries(), { maxArrayLength: 1 }),
922+
"[Map Iterator] { [ 'foo', 'bar' ], ... 1 more item }");
920923
// Make sure the iterator doesn't get consumed.
921924
const keys = map.keys();
922-
assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo' }");
923-
assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo' }");
925+
assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }");
926+
assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }");
924927
keys.extra = true;
925928
assert.strictEqual(
926929
util.inspect(keys, { maxArrayLength: 0 }),
927-
'[Map Iterator] { ... more items, extra: true }');
930+
'[Map Iterator] { ... 2 more items, extra: true }');
928931
}
929932

930933
// Test Set iterators.
@@ -940,7 +943,7 @@ if (typeof Symbol !== 'undefined') {
940943
keys.extra = true;
941944
assert.strictEqual(
942945
util.inspect(keys, { maxArrayLength: 1 }),
943-
'[Set Iterator] { 1, ... more items, extra: true }');
946+
'[Set Iterator] { 1, ... 1 more item, extra: true }');
944947
}
945948

946949
// Test alignment of items in container.
@@ -1364,17 +1367,17 @@ util.inspect(process);
13641367
assert.strictEqual(out, expect);
13651368

13661369
out = util.inspect(weakMap, { maxArrayLength: 0, showHidden: true });
1367-
expect = 'WeakMap { ... more items }';
1370+
expect = 'WeakMap { ... 2 more items }';
13681371
assert.strictEqual(out, expect);
13691372

13701373
weakMap.extra = true;
13711374
out = util.inspect(weakMap, { maxArrayLength: 1, showHidden: true });
13721375
// It is not possible to determine the output reliable.
1373-
expect = 'WeakMap { [ [length]: 0 ] => {}, ... more items, extra: true }';
1374-
const expectAlt = 'WeakMap { {} => [ [length]: 0 ], ... more items, ' +
1376+
expect = 'WeakMap { [ [length]: 0 ] => {}, ... 1 more item, extra: true }';
1377+
const expectAlt = 'WeakMap { {} => [ [length]: 0 ], ... 1 more item, ' +
13751378
'extra: true }';
13761379
assert(out === expect || out === expectAlt,
1377-
`Found "${out}" rather than "${expect}" or "${expectAlt}"`);
1380+
`Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`);
13781381
}
13791382

13801383
{ // Test WeakSet
@@ -1388,17 +1391,17 @@ util.inspect(process);
13881391
assert.strictEqual(out, expect);
13891392

13901393
out = util.inspect(weakSet, { maxArrayLength: -2, showHidden: true });
1391-
expect = 'WeakSet { ... more items }';
1394+
expect = 'WeakSet { ... 2 more items }';
13921395
assert.strictEqual(out, expect);
13931396

13941397
weakSet.extra = true;
13951398
out = util.inspect(weakSet, { maxArrayLength: 1, showHidden: true });
13961399
// It is not possible to determine the output reliable.
1397-
expect = 'WeakSet { {}, ... more items, extra: true }';
1398-
const expectAlt = 'WeakSet { [ 1, [length]: 1 ], ... more items, ' +
1400+
expect = 'WeakSet { {}, ... 1 more item, extra: true }';
1401+
const expectAlt = 'WeakSet { [ 1, [length]: 1 ], ... 1 more item, ' +
13991402
'extra: true }';
14001403
assert(out === expect || out === expectAlt,
1401-
`Found "${out}" rather than "${expect}" or "${expectAlt}"`);
1404+
`Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`);
14021405
}
14031406

14041407
{ // Test argument objects.

0 commit comments

Comments
 (0)