Skip to content

Commit 73cafd8

Browse files
addaleaxtargos
authored andcommitted
console,util: avoid pair array generation in C++
Use a plain `[key, value, key, value]`-style list instead of an array of pairs for inspecting collections. This also fixes a bug with `console.table()` where inspecting a non-key-value `MapIterator` would have led to odd results. PR-URL: #20831 Refs: #20719 (comment) Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Minwoo Jung <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent 4e60ce8 commit 73cafd8

File tree

5 files changed

+67
-38
lines changed

5 files changed

+67
-38
lines changed

β€Žlib/console.js

+19-9
Original file line numberDiff line numberDiff line change
@@ -356,17 +356,27 @@ Console.prototype.table = function(tabularData, properties) {
356356
const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i));
357357

358358
const mapIter = isMapIterator(tabularData);
359+
let isKeyValue = false;
360+
let i = 0;
359361
if (mapIter)
360-
tabularData = previewEntries(tabularData);
362+
[ tabularData, isKeyValue ] = previewEntries(tabularData);
361363

362-
if (mapIter || isMap(tabularData)) {
364+
if (isKeyValue || isMap(tabularData)) {
363365
const keys = [];
364366
const values = [];
365367
let length = 0;
366-
for (const [k, v] of tabularData) {
367-
keys.push(inspect(k));
368-
values.push(inspect(v));
369-
length++;
368+
if (mapIter) {
369+
for (; i < tabularData.length / 2; ++i) {
370+
keys.push(inspect(tabularData[i * 2]));
371+
values.push(inspect(tabularData[i * 2 + 1]));
372+
length++;
373+
}
374+
} else {
375+
for (const [k, v] of tabularData) {
376+
keys.push(inspect(k));
377+
values.push(inspect(v));
378+
length++;
379+
}
370380
}
371381
return final([
372382
iterKey, keyKey, valuesKey
@@ -379,9 +389,9 @@ Console.prototype.table = function(tabularData, properties) {
379389

380390
const setIter = isSetIterator(tabularData);
381391
if (setIter)
382-
tabularData = previewEntries(tabularData);
392+
[ tabularData ] = previewEntries(tabularData);
383393

384-
const setlike = setIter || isSet(tabularData);
394+
const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData);
385395
if (setlike) {
386396
const values = [];
387397
let length = 0;
@@ -400,7 +410,7 @@ Console.prototype.table = function(tabularData, properties) {
400410
const valuesKeyArray = [];
401411
const indexKeyArray = ObjectKeys(tabularData);
402412

403-
for (var i = 0; i < indexKeyArray.length; i++) {
413+
for (; i < indexKeyArray.length; i++) {
404414
const item = tabularData[indexKeyArray[i]];
405415
const primitive = item === null ||
406416
(typeof item !== 'function' && typeof item !== 'object');

β€Žlib/util.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ function formatMap(ctx, value, recurseTimes, keys) {
984984

985985
function formatWeakSet(ctx, value, recurseTimes, keys) {
986986
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
987-
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
987+
const [ entries ] = previewEntries(value).slice(0, maxArrayLength + 1);
988988
const maxLength = Math.min(maxArrayLength, entries.length);
989989
let output = new Array(maxLength);
990990
for (var i = 0; i < maxLength; ++i)
@@ -1001,14 +1001,16 @@ function formatWeakSet(ctx, value, recurseTimes, keys) {
10011001

10021002
function formatWeakMap(ctx, value, recurseTimes, keys) {
10031003
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
1004-
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
1005-
const remainder = entries.length > maxArrayLength;
1006-
const len = entries.length - (remainder ? 1 : 0);
1004+
const [ entries ] = previewEntries(value).slice(0, (maxArrayLength + 1) * 2);
1005+
// Entries exist as [key1, val1, key2, val2, ...]
1006+
const remainder = entries.length / 2 > maxArrayLength;
1007+
const len = entries.length / 2 - (remainder ? 1 : 0);
10071008
const maxLength = Math.min(maxArrayLength, len);
10081009
let output = new Array(maxLength);
1009-
for (var i = 0; i < len; i++) {
1010-
output[i] = `${formatValue(ctx, entries[i][0], recurseTimes)} => ` +
1011-
formatValue(ctx, entries[i][1], recurseTimes);
1010+
for (var i = 0; i < maxLength; i++) {
1011+
const pos = i * 2;
1012+
output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` +
1013+
formatValue(ctx, entries[pos + 1], recurseTimes);
10121014
}
10131015
// Sort all entries to have a halfway reliable output (if more entries than
10141016
// retrieved ones exist, we can not reliably return the same output).
@@ -1020,9 +1022,19 @@ function formatWeakMap(ctx, value, recurseTimes, keys) {
10201022
return output;
10211023
}
10221024

1025+
function zip2(list) {
1026+
const ret = Array(list.length / 2);
1027+
for (var i = 0; i < ret.length; ++i)
1028+
ret[i] = [list[2 * i], list[2 * i + 1]];
1029+
return ret;
1030+
}
1031+
10231032
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
10241033
const output = [];
1025-
for (const entry of previewEntries(value)) {
1034+
var [ entries, isKeyValue ] = previewEntries(value);
1035+
if (isKeyValue)
1036+
entries = zip2(entries);
1037+
for (const entry of entries) {
10261038
if (ctx.maxArrayLength === output.length) {
10271039
output.push('... more items');
10281040
break;

β€Žsrc/node_util.cc

+6-19
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,16 @@ static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
5353
if (!args[0]->IsObject())
5454
return;
5555

56+
Environment* env = Environment::GetCurrent(args);
5657
bool is_key_value;
5758
Local<Array> entries;
5859
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
5960
return;
60-
if (!is_key_value)
61-
return args.GetReturnValue().Set(entries);
62-
63-
uint32_t length = entries->Length();
64-
CHECK_EQ(length % 2, 0);
65-
66-
Environment* env = Environment::GetCurrent(args);
67-
Local<Context> context = env->context();
68-
69-
Local<Array> pairs = Array::New(env->isolate(), length / 2);
70-
for (uint32_t i = 0; i < length / 2; i++) {
71-
Local<Array> pair = Array::New(env->isolate(), 2);
72-
pair->Set(context, 0, entries->Get(context, i * 2).ToLocalChecked())
73-
.FromJust();
74-
pair->Set(context, 1, entries->Get(context, i * 2 + 1).ToLocalChecked())
75-
.FromJust();
76-
pairs->Set(context, i, pair).FromJust();
77-
}
78-
args.GetReturnValue().Set(pairs);
61+
Local<Array> ret = Array::New(env->isolate(), 2);
62+
ret->Set(env->context(), 0, entries).FromJust();
63+
ret->Set(env->context(), 1, v8::Boolean::New(env->isolate(), is_key_value))
64+
.FromJust();
65+
return args.GetReturnValue().Set(ret);
7966
}
8067

8168
// Side effect-free stringification that will never throw exceptions.

β€Žtest/parallel/test-console-table.js

+20
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,26 @@ test(new Map([[1, 1], [2, 2], [3, 3]]).entries(), `
120120
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜
121121
`);
122122

123+
test(new Map([[1, 1], [2, 2], [3, 3]]).values(), `
124+
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
125+
β”‚ (iteration index) β”‚ Values β”‚
126+
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
127+
β”‚ 0 β”‚ 1 β”‚
128+
β”‚ 1 β”‚ 2 β”‚
129+
β”‚ 2 β”‚ 3 β”‚
130+
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜
131+
`);
132+
133+
test(new Map([[1, 1], [2, 2], [3, 3]]).keys(), `
134+
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
135+
β”‚ (iteration index) β”‚ Values β”‚
136+
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
137+
β”‚ 0 β”‚ 1 β”‚
138+
β”‚ 1 β”‚ 2 β”‚
139+
β”‚ 2 β”‚ 3 β”‚
140+
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜
141+
`);
142+
123143
test(new Set([1, 2, 3]).values(), `
124144
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
125145
β”‚ (iteration index) β”‚ Values β”‚

β€Žtest/parallel/test-util-inspect.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -447,13 +447,13 @@ 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());
450+
const [ vals ] = previewEntries(map.entries());
451451
const valsOutput = [];
452452
for (const o of vals) {
453453
valsOutput.push(o);
454454
}
455455

456-
assert.strictEqual(util.inspect(valsOutput), '[ [ 1, 2 ] ]');
456+
assert.strictEqual(util.inspect(valsOutput), '[ 1, 2 ]');
457457
}
458458

459459
// Test for other constructors in different context.

0 commit comments

Comments
Β (0)