Skip to content

Commit

Permalink
util: compact inspect() for sparse arrays
Browse files Browse the repository at this point in the history
This prevents `console.log(arr)` from crashing node when given a sparse
array with large length. Fixes nodejs#4905
  • Loading branch information
dcposch committed Feb 4, 2016
1 parent c41c093 commit d257683
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
35 changes: 27 additions & 8 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,20 +501,39 @@ function formatObject(ctx, value, recurseTimes, visibleKeys, keys) {

function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
var output = [];
for (var i = 0, l = value.length; i < l; ++i) {
if (hasOwnProperty(value, String(i))) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
String(i), true));
} else {
output.push('');
// if less than a tenth of the array's slots are populated, consider it sparse
// and return a compact representation. this prevents node from crashing when
// calling console.log() on a small array with large length.
// see https://github.com/nodejs/node/issues/4905
var isSparse = value.length > 10 && keys.length * 10 < value.length;
if (!isSparse) {
// output a normal dense array, eg. [ , , , "foo", "bar"]
for (var i = 0, l = value.length; i < l; ++i) {
if (hasOwnProperty(value, String(i))) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
String(i), true));
} else {
output.push('');
}
}
}
// output sparse arrays as eg. [10: "foo", 1000000: "bar"]
// also handle additional properties on an array, for example [1, 2, 3, foo:4]
keys.forEach(function(key) {
var str = formatProperty(ctx, value, recurseTimes, visibleKeys, key, true);
if (typeof key === 'symbol' || !key.match(/^\d+$/)) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
key, true));
output.push(str); // str in format "foo: bar"
} else if (isSparse) {
output.push(key + ': ' + str); // str in format "bar"
}
});
if (isSparse) {
if (keys.length === 0) {
output.push('<empty array, length ' + value.length + '>');
} else {
output.push('<sparse array, length ' + value.length + '>');
}
}
return output;
}

Expand Down
13 changes: 13 additions & 0 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ assert.equal(util.inspect(Object.create({},
'{ visible: 1 }'
);

// verify short output, no crash for long sparse arrays
var arr = [];
arr[2] = 1;
assert.equal(util.inspect(arr), '[ , , 1 ]'); // dense
arr[1000] = 1;
assert.equal(util.inspect(arr),
'[ 2: 1, 1000: 1, <sparse array, length 1001> ]'); // sparse
arr['foo'] = 'bar';
assert.equal(util.inspect(arr),
'[ 2: 1, 1000: 1, foo: \'bar\', <sparse array, length 1001> ]');
arr = new Array(1000000);
assert.equal(util.inspect(arr), '[ <empty array, length 1000000> ]');

for (const showHidden of [true, false]) {
const ab = new ArrayBuffer(4);
const dv = new DataView(ab, 1, 2);
Expand Down

0 comments on commit d257683

Please sign in to comment.