Skip to content

Commit d257683

Browse files
committed
util: compact inspect() for sparse arrays
This prevents `console.log(arr)` from crashing node when given a sparse array with large length. Fixes nodejs#4905
1 parent c41c093 commit d257683

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

lib/util.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -501,20 +501,39 @@ function formatObject(ctx, value, recurseTimes, visibleKeys, keys) {
501501

502502
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
503503
var output = [];
504-
for (var i = 0, l = value.length; i < l; ++i) {
505-
if (hasOwnProperty(value, String(i))) {
506-
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
507-
String(i), true));
508-
} else {
509-
output.push('');
504+
// if less than a tenth of the array's slots are populated, consider it sparse
505+
// and return a compact representation. this prevents node from crashing when
506+
// calling console.log() on a small array with large length.
507+
// see https://github.com/nodejs/node/issues/4905
508+
var isSparse = value.length > 10 && keys.length * 10 < value.length;
509+
if (!isSparse) {
510+
// output a normal dense array, eg. [ , , , "foo", "bar"]
511+
for (var i = 0, l = value.length; i < l; ++i) {
512+
if (hasOwnProperty(value, String(i))) {
513+
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
514+
String(i), true));
515+
} else {
516+
output.push('');
517+
}
510518
}
511519
}
520+
// output sparse arrays as eg. [10: "foo", 1000000: "bar"]
521+
// also handle additional properties on an array, for example [1, 2, 3, foo:4]
512522
keys.forEach(function(key) {
523+
var str = formatProperty(ctx, value, recurseTimes, visibleKeys, key, true);
513524
if (typeof key === 'symbol' || !key.match(/^\d+$/)) {
514-
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
515-
key, true));
525+
output.push(str); // str in format "foo: bar"
526+
} else if (isSparse) {
527+
output.push(key + ': ' + str); // str in format "bar"
516528
}
517529
});
530+
if (isSparse) {
531+
if (keys.length === 0) {
532+
output.push('<empty array, length ' + value.length + '>');
533+
} else {
534+
output.push('<sparse array, length ' + value.length + '>');
535+
}
536+
}
518537
return output;
519538
}
520539

test/parallel/test-util-inspect.js

+13
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ assert.equal(util.inspect(Object.create({},
4242
'{ visible: 1 }'
4343
);
4444

45+
// verify short output, no crash for long sparse arrays
46+
var arr = [];
47+
arr[2] = 1;
48+
assert.equal(util.inspect(arr), '[ , , 1 ]'); // dense
49+
arr[1000] = 1;
50+
assert.equal(util.inspect(arr),
51+
'[ 2: 1, 1000: 1, <sparse array, length 1001> ]'); // sparse
52+
arr['foo'] = 'bar';
53+
assert.equal(util.inspect(arr),
54+
'[ 2: 1, 1000: 1, foo: \'bar\', <sparse array, length 1001> ]');
55+
arr = new Array(1000000);
56+
assert.equal(util.inspect(arr), '[ <empty array, length 1000000> ]');
57+
4558
for (const showHidden of [true, false]) {
4659
const ab = new ArrayBuffer(4);
4760
const dv = new DataView(ab, 1, 2);

0 commit comments

Comments
 (0)