Skip to content

Commit db49589

Browse files
addaleaxBridgeAR
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: nodejs#20831 Refs: nodejs#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 df97126 commit db49589

File tree

5 files changed

+67
-38
lines changed

5 files changed

+67
-38
lines changed

lib/console.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -363,17 +363,27 @@ Console.prototype.table = function(tabularData, properties) {
363363
const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i));
364364

365365
const mapIter = isMapIterator(tabularData);
366+
let isKeyValue = false;
367+
let i = 0;
366368
if (mapIter)
367-
tabularData = previewEntries(tabularData);
369+
[ tabularData, isKeyValue ] = previewEntries(tabularData);
368370

369-
if (mapIter || isMap(tabularData)) {
371+
if (isKeyValue || isMap(tabularData)) {
370372
const keys = [];
371373
const values = [];
372374
let length = 0;
373-
for (const [k, v] of tabularData) {
374-
keys.push(inspect(k));
375-
values.push(inspect(v));
376-
length++;
375+
if (mapIter) {
376+
for (; i < tabularData.length / 2; ++i) {
377+
keys.push(inspect(tabularData[i * 2]));
378+
values.push(inspect(tabularData[i * 2 + 1]));
379+
length++;
380+
}
381+
} else {
382+
for (const [k, v] of tabularData) {
383+
keys.push(inspect(k));
384+
values.push(inspect(v));
385+
length++;
386+
}
377387
}
378388
return final([
379389
iterKey, keyKey, valuesKey
@@ -386,9 +396,9 @@ Console.prototype.table = function(tabularData, properties) {
386396

387397
const setIter = isSetIterator(tabularData);
388398
if (setIter)
389-
tabularData = previewEntries(tabularData);
399+
[ tabularData ] = previewEntries(tabularData);
390400

391-
const setlike = setIter || isSet(tabularData);
401+
const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData);
392402
if (setlike) {
393403
const values = [];
394404
let length = 0;
@@ -407,7 +417,7 @@ Console.prototype.table = function(tabularData, properties) {
407417
const valuesKeyArray = [];
408418
const indexKeyArray = ObjectKeys(tabularData);
409419

410-
for (var i = 0; i < indexKeyArray.length; i++) {
420+
for (; i < indexKeyArray.length; i++) {
411421
const item = tabularData[indexKeyArray[i]];
412422
const primitive = item === null ||
413423
(typeof item !== 'function' && typeof item !== 'object');

lib/util.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ function formatMap(ctx, value, recurseTimes, keys) {
981981

982982
function formatWeakSet(ctx, value, recurseTimes, keys) {
983983
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
984-
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
984+
const [ entries ] = previewEntries(value).slice(0, maxArrayLength + 1);
985985
const maxLength = Math.min(maxArrayLength, entries.length);
986986
let output = new Array(maxLength);
987987
for (var i = 0; i < maxLength; ++i)
@@ -998,14 +998,16 @@ function formatWeakSet(ctx, value, recurseTimes, keys) {
998998

999999
function formatWeakMap(ctx, value, recurseTimes, keys) {
10001000
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
1001-
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
1002-
const remainder = entries.length > maxArrayLength;
1003-
const len = entries.length - (remainder ? 1 : 0);
1001+
const [ entries ] = previewEntries(value).slice(0, (maxArrayLength + 1) * 2);
1002+
// Entries exist as [key1, val1, key2, val2, ...]
1003+
const remainder = entries.length / 2 > maxArrayLength;
1004+
const len = entries.length / 2 - (remainder ? 1 : 0);
10041005
const maxLength = Math.min(maxArrayLength, len);
10051006
let output = new Array(maxLength);
1006-
for (var i = 0; i < len; i++) {
1007-
output[i] = `${formatValue(ctx, entries[i][0], recurseTimes)} => ` +
1008-
formatValue(ctx, entries[i][1], recurseTimes);
1007+
for (var i = 0; i < maxLength; i++) {
1008+
const pos = i * 2;
1009+
output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` +
1010+
formatValue(ctx, entries[pos + 1], recurseTimes);
10091011
}
10101012
// Sort all entries to have a halfway reliable output (if more entries than
10111013
// retrieved ones exist, we can not reliably return the same output).
@@ -1017,9 +1019,19 @@ function formatWeakMap(ctx, value, recurseTimes, keys) {
10171019
return output;
10181020
}
10191021

1022+
function zip2(list) {
1023+
const ret = Array(list.length / 2);
1024+
for (var i = 0; i < ret.length; ++i)
1025+
ret[i] = [list[2 * i], list[2 * i + 1]];
1026+
return ret;
1027+
}
1028+
10201029
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
10211030
const output = [];
1022-
for (const entry of previewEntries(value)) {
1031+
var [ entries, isKeyValue ] = previewEntries(value);
1032+
if (isKeyValue)
1033+
entries = zip2(entries);
1034+
for (const entry of entries) {
10231035
if (ctx.maxArrayLength === output.length) {
10241036
output.push('... more items');
10251037
break;

src/node_util.cc

Lines changed: 6 additions & 19 deletions
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

Lines changed: 20 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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)