diff --git a/lib/util.js b/lib/util.js index 2908d4ece9192f..14149cbf95a4d6 100644 --- a/lib/util.js +++ b/lib/util.js @@ -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(''); + } else { + output.push(''); + } + } return output; } diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 57833d65f5bb95..01bd948ad648a6 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -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 +arr['foo'] = 'bar'; +assert.equal(util.inspect(arr), + '[ 2: 1, 1000: 1, foo: \'bar\', ]'); +arr = new Array(1000000); +assert.equal(util.inspect(arr), '[ ]'); + for (const showHidden of [true, false]) { const ab = new ArrayBuffer(4); const dv = new DataView(ab, 1, 2);