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: https://github.com/nodejs/node/pull/20831 Refs: https://github.com/nodejs/node/pull/20719#discussion_r189342513 Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Minwoo Jung <minwoo@nodesource.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
df97126173
commit
db49589624
@ -363,17 +363,27 @@ Console.prototype.table = function(tabularData, properties) {
|
|||||||
const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i));
|
const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i));
|
||||||
|
|
||||||
const mapIter = isMapIterator(tabularData);
|
const mapIter = isMapIterator(tabularData);
|
||||||
|
let isKeyValue = false;
|
||||||
|
let i = 0;
|
||||||
if (mapIter)
|
if (mapIter)
|
||||||
tabularData = previewEntries(tabularData);
|
[ tabularData, isKeyValue ] = previewEntries(tabularData);
|
||||||
|
|
||||||
if (mapIter || isMap(tabularData)) {
|
if (isKeyValue || isMap(tabularData)) {
|
||||||
const keys = [];
|
const keys = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
let length = 0;
|
let length = 0;
|
||||||
for (const [k, v] of tabularData) {
|
if (mapIter) {
|
||||||
keys.push(inspect(k));
|
for (; i < tabularData.length / 2; ++i) {
|
||||||
values.push(inspect(v));
|
keys.push(inspect(tabularData[i * 2]));
|
||||||
length++;
|
values.push(inspect(tabularData[i * 2 + 1]));
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const [k, v] of tabularData) {
|
||||||
|
keys.push(inspect(k));
|
||||||
|
values.push(inspect(v));
|
||||||
|
length++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return final([
|
return final([
|
||||||
iterKey, keyKey, valuesKey
|
iterKey, keyKey, valuesKey
|
||||||
@ -386,9 +396,9 @@ Console.prototype.table = function(tabularData, properties) {
|
|||||||
|
|
||||||
const setIter = isSetIterator(tabularData);
|
const setIter = isSetIterator(tabularData);
|
||||||
if (setIter)
|
if (setIter)
|
||||||
tabularData = previewEntries(tabularData);
|
[ tabularData ] = previewEntries(tabularData);
|
||||||
|
|
||||||
const setlike = setIter || isSet(tabularData);
|
const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData);
|
||||||
if (setlike) {
|
if (setlike) {
|
||||||
const values = [];
|
const values = [];
|
||||||
let length = 0;
|
let length = 0;
|
||||||
@ -407,7 +417,7 @@ Console.prototype.table = function(tabularData, properties) {
|
|||||||
const valuesKeyArray = [];
|
const valuesKeyArray = [];
|
||||||
const indexKeyArray = ObjectKeys(tabularData);
|
const indexKeyArray = ObjectKeys(tabularData);
|
||||||
|
|
||||||
for (var i = 0; i < indexKeyArray.length; i++) {
|
for (; i < indexKeyArray.length; i++) {
|
||||||
const item = tabularData[indexKeyArray[i]];
|
const item = tabularData[indexKeyArray[i]];
|
||||||
const primitive = item === null ||
|
const primitive = item === null ||
|
||||||
(typeof item !== 'function' && typeof item !== 'object');
|
(typeof item !== 'function' && typeof item !== 'object');
|
||||||
|
28
lib/util.js
28
lib/util.js
@ -981,7 +981,7 @@ function formatMap(ctx, value, recurseTimes, keys) {
|
|||||||
|
|
||||||
function formatWeakSet(ctx, value, recurseTimes, keys) {
|
function formatWeakSet(ctx, value, recurseTimes, keys) {
|
||||||
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
|
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
|
||||||
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
|
const [ entries ] = previewEntries(value).slice(0, maxArrayLength + 1);
|
||||||
const maxLength = Math.min(maxArrayLength, entries.length);
|
const maxLength = Math.min(maxArrayLength, entries.length);
|
||||||
let output = new Array(maxLength);
|
let output = new Array(maxLength);
|
||||||
for (var i = 0; i < maxLength; ++i)
|
for (var i = 0; i < maxLength; ++i)
|
||||||
@ -998,14 +998,16 @@ function formatWeakSet(ctx, value, recurseTimes, keys) {
|
|||||||
|
|
||||||
function formatWeakMap(ctx, value, recurseTimes, keys) {
|
function formatWeakMap(ctx, value, recurseTimes, keys) {
|
||||||
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
|
const maxArrayLength = Math.max(ctx.maxArrayLength, 0);
|
||||||
const entries = previewEntries(value).slice(0, maxArrayLength + 1);
|
const [ entries ] = previewEntries(value).slice(0, (maxArrayLength + 1) * 2);
|
||||||
const remainder = entries.length > maxArrayLength;
|
// Entries exist as [key1, val1, key2, val2, ...]
|
||||||
const len = entries.length - (remainder ? 1 : 0);
|
const remainder = entries.length / 2 > maxArrayLength;
|
||||||
|
const len = entries.length / 2 - (remainder ? 1 : 0);
|
||||||
const maxLength = Math.min(maxArrayLength, len);
|
const maxLength = Math.min(maxArrayLength, len);
|
||||||
let output = new Array(maxLength);
|
let output = new Array(maxLength);
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < maxLength; i++) {
|
||||||
output[i] = `${formatValue(ctx, entries[i][0], recurseTimes)} => ` +
|
const pos = i * 2;
|
||||||
formatValue(ctx, entries[i][1], recurseTimes);
|
output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` +
|
||||||
|
formatValue(ctx, entries[pos + 1], recurseTimes);
|
||||||
}
|
}
|
||||||
// Sort all entries to have a halfway reliable output (if more entries than
|
// Sort all entries to have a halfway reliable output (if more entries than
|
||||||
// retrieved ones exist, we can not reliably return the same output).
|
// retrieved ones exist, we can not reliably return the same output).
|
||||||
@ -1017,9 +1019,19 @@ function formatWeakMap(ctx, value, recurseTimes, keys) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zip2(list) {
|
||||||
|
const ret = Array(list.length / 2);
|
||||||
|
for (var i = 0; i < ret.length; ++i)
|
||||||
|
ret[i] = [list[2 * i], list[2 * i + 1]];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
|
function formatCollectionIterator(ctx, value, recurseTimes, keys) {
|
||||||
const output = [];
|
const output = [];
|
||||||
for (const entry of previewEntries(value)) {
|
var [ entries, isKeyValue ] = previewEntries(value);
|
||||||
|
if (isKeyValue)
|
||||||
|
entries = zip2(entries);
|
||||||
|
for (const entry of entries) {
|
||||||
if (ctx.maxArrayLength === output.length) {
|
if (ctx.maxArrayLength === output.length) {
|
||||||
output.push('... more items');
|
output.push('... more items');
|
||||||
break;
|
break;
|
||||||
|
@ -53,29 +53,16 @@ static void PreviewEntries(const FunctionCallbackInfo<Value>& args) {
|
|||||||
if (!args[0]->IsObject())
|
if (!args[0]->IsObject())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
bool is_key_value;
|
bool is_key_value;
|
||||||
Local<Array> entries;
|
Local<Array> entries;
|
||||||
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
|
if (!args[0].As<Object>()->PreviewEntries(&is_key_value).ToLocal(&entries))
|
||||||
return;
|
return;
|
||||||
if (!is_key_value)
|
Local<Array> ret = Array::New(env->isolate(), 2);
|
||||||
return args.GetReturnValue().Set(entries);
|
ret->Set(env->context(), 0, entries).FromJust();
|
||||||
|
ret->Set(env->context(), 1, v8::Boolean::New(env->isolate(), is_key_value))
|
||||||
uint32_t length = entries->Length();
|
.FromJust();
|
||||||
CHECK_EQ(length % 2, 0);
|
return args.GetReturnValue().Set(ret);
|
||||||
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
Local<Context> context = env->context();
|
|
||||||
|
|
||||||
Local<Array> pairs = Array::New(env->isolate(), length / 2);
|
|
||||||
for (uint32_t i = 0; i < length / 2; i++) {
|
|
||||||
Local<Array> pair = Array::New(env->isolate(), 2);
|
|
||||||
pair->Set(context, 0, entries->Get(context, i * 2).ToLocalChecked())
|
|
||||||
.FromJust();
|
|
||||||
pair->Set(context, 1, entries->Get(context, i * 2 + 1).ToLocalChecked())
|
|
||||||
.FromJust();
|
|
||||||
pairs->Set(context, i, pair).FromJust();
|
|
||||||
}
|
|
||||||
args.GetReturnValue().Set(pairs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Side effect-free stringification that will never throw exceptions.
|
// Side effect-free stringification that will never throw exceptions.
|
||||||
|
@ -120,6 +120,26 @@ test(new Map([[1, 1], [2, 2], [3, 3]]).entries(), `
|
|||||||
└───────────────────┴─────┴────────┘
|
└───────────────────┴─────┴────────┘
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
test(new Map([[1, 1], [2, 2], [3, 3]]).values(), `
|
||||||
|
┌───────────────────┬────────┐
|
||||||
|
│ (iteration index) │ Values │
|
||||||
|
├───────────────────┼────────┤
|
||||||
|
│ 0 │ 1 │
|
||||||
|
│ 1 │ 2 │
|
||||||
|
│ 2 │ 3 │
|
||||||
|
└───────────────────┴────────┘
|
||||||
|
`);
|
||||||
|
|
||||||
|
test(new Map([[1, 1], [2, 2], [3, 3]]).keys(), `
|
||||||
|
┌───────────────────┬────────┐
|
||||||
|
│ (iteration index) │ Values │
|
||||||
|
├───────────────────┼────────┤
|
||||||
|
│ 0 │ 1 │
|
||||||
|
│ 1 │ 2 │
|
||||||
|
│ 2 │ 3 │
|
||||||
|
└───────────────────┴────────┘
|
||||||
|
`);
|
||||||
|
|
||||||
test(new Set([1, 2, 3]).values(), `
|
test(new Set([1, 2, 3]).values(), `
|
||||||
┌───────────────────┬────────┐
|
┌───────────────────┬────────┐
|
||||||
│ (iteration index) │ Values │
|
│ (iteration index) │ Values │
|
||||||
|
@ -447,13 +447,13 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324');
|
|||||||
{
|
{
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
map.set(1, 2);
|
map.set(1, 2);
|
||||||
const vals = previewEntries(map.entries());
|
const [ vals ] = previewEntries(map.entries());
|
||||||
const valsOutput = [];
|
const valsOutput = [];
|
||||||
for (const o of vals) {
|
for (const o of vals) {
|
||||||
valsOutput.push(o);
|
valsOutput.push(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.strictEqual(util.inspect(valsOutput), '[ [ 1, 2 ] ]');
|
assert.strictEqual(util.inspect(valsOutput), '[ 1, 2 ]');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for other constructors in different context.
|
// Test for other constructors in different context.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user