util: refactor inspect for performance and more
The main optimizations are - Removed visibleKeys - Removed proxy cache - Removed Object.assign - No key concatenating anymore - No key recalculating anymore - Improved indentation logic - Improved string escape logic - Added many fast paths - Optimized code branches a lot - Optimized (boxed) primitive handling - Inline code if possible - Only check extra keys if necessary - Guard against unnecessary more expensive calls This also fixes a bug with special array number keys as e.g. "00". Besides that there were lots of smaller optimizations, the code got a bit cleaned up and a few more tests got in. PR-URL: https://github.com/nodejs/node/pull/14881 Fixes: https://github.com/nodejs/node/issues/15288 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
parent
01652ccc68
commit
f9ad23dc91
997
lib/util.js
997
lib/util.js
File diff suppressed because it is too large
Load Diff
@ -114,6 +114,11 @@ const nestedObj = {
|
||||
func: function() {}
|
||||
}
|
||||
};
|
||||
const nestedObj2 = {
|
||||
foo: 'bar',
|
||||
foobar: 1,
|
||||
func: [{ a: function() {} }]
|
||||
};
|
||||
assert.strictEqual(util.format('%o'), '%o');
|
||||
assert.strictEqual(util.format('%o', 42), '42');
|
||||
assert.strictEqual(util.format('%o', 'foo'), '\'foo\'');
|
||||
@ -126,6 +131,17 @@ assert.strictEqual(
|
||||
' [length]: 0,\n' +
|
||||
' [name]: \'func\',\n' +
|
||||
' [prototype]: func { [constructor]: [Circular] } } }');
|
||||
assert.strictEqual(
|
||||
util.format('%o', nestedObj2),
|
||||
'{ foo: \'bar\',\n' +
|
||||
' foobar: 1,\n' +
|
||||
' func: \n' +
|
||||
' [ { a: \n' +
|
||||
' { [Function: a]\n' +
|
||||
' [length]: 0,\n' +
|
||||
' [name]: \'a\',\n' +
|
||||
' [prototype]: a { [constructor]: [Circular] } } },\n' +
|
||||
' [length]: 1 ] }');
|
||||
assert.strictEqual(
|
||||
util.format('%o', nestedObj),
|
||||
'{ foo: \'bar\',\n' +
|
||||
|
@ -55,7 +55,9 @@ const expected6 = 'Proxy [ Proxy [ Proxy [ Proxy [Array], Proxy [Array]' +
|
||||
' ],\n Proxy [ Proxy [Array], Proxy [Array] ] ],\n' +
|
||||
' Proxy [ Proxy [ Proxy [Array], Proxy [Array] ],\n' +
|
||||
' Proxy [ Proxy [Array], Proxy [Array] ] ] ]';
|
||||
assert.strictEqual(util.inspect(proxy1, opts), expected1);
|
||||
assert.strictEqual(
|
||||
util.inspect(proxy1, { showProxy: true, depth: null }),
|
||||
expected1);
|
||||
assert.strictEqual(util.inspect(proxy2, opts), expected2);
|
||||
assert.strictEqual(util.inspect(proxy3, opts), expected3);
|
||||
assert.strictEqual(util.inspect(proxy4, opts), expected4);
|
||||
|
@ -43,14 +43,15 @@ assert.strictEqual(
|
||||
new Date('2010-02-14T12:48:40+01:00').toISOString()
|
||||
);
|
||||
assert.strictEqual(util.inspect(new Date('')), (new Date('')).toString());
|
||||
|
||||
assert.strictEqual(util.inspect('\n\u0001'), "'\\n\\u0001'");
|
||||
|
||||
assert.strictEqual(
|
||||
util.inspect(`${Array(75).fill(1)}'\n\u001d\n\u0003`),
|
||||
`'${Array(75).fill(1)}\\'\\n\\u001d\\n\\u0003'`
|
||||
);
|
||||
assert.strictEqual(util.inspect([]), '[]');
|
||||
assert.strictEqual(util.inspect(Object.create([])), 'Array {}');
|
||||
assert.strictEqual(util.inspect([1, 2]), '[ 1, 2 ]');
|
||||
assert.strictEqual(util.inspect([1, [2, 3]]), '[ 1, [ 2, 3 ] ]');
|
||||
|
||||
assert.strictEqual(util.inspect({}), '{}');
|
||||
assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }');
|
||||
assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }');
|
||||
@ -76,6 +77,7 @@ assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 1),
|
||||
'{ a: { b: [Object] } }');
|
||||
assert.strictEqual(util.inspect({ 'a': { 'b': ['c'] } }, false, 1),
|
||||
'{ a: { b: [Array] } }');
|
||||
assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array [ ]');
|
||||
assert.strictEqual(
|
||||
util.inspect(
|
||||
Object.create(
|
||||
@ -327,6 +329,38 @@ assert.strictEqual(
|
||||
assert.strictEqual(util.inspect(arr), 'CustomArray [ <50 empty items> ]');
|
||||
}
|
||||
|
||||
// Array with extra properties
|
||||
{
|
||||
const arr = [1, 2, 3, , ];
|
||||
arr.foo = 'bar';
|
||||
assert.strictEqual(util.inspect(arr),
|
||||
"[ 1, 2, 3, <1 empty item>, foo: 'bar' ]");
|
||||
|
||||
const arr2 = [];
|
||||
assert.strictEqual(util.inspect([], { showHidden: true }), '[ [length]: 0 ]');
|
||||
arr2['00'] = 1;
|
||||
assert.strictEqual(util.inspect(arr2), "[ '00': 1 ]");
|
||||
assert.strictEqual(util.inspect(arr2, { showHidden: true }),
|
||||
"[ [length]: 0, '00': 1 ]");
|
||||
arr2[1] = 0;
|
||||
assert.strictEqual(util.inspect(arr2), "[ <1 empty item>, 0, '00': 1 ]");
|
||||
assert.strictEqual(util.inspect(arr2, { showHidden: true }),
|
||||
"[ <1 empty item>, 0, [length]: 2, '00': 1 ]");
|
||||
delete arr2[1];
|
||||
assert.strictEqual(util.inspect(arr2), "[ <2 empty items>, '00': 1 ]");
|
||||
assert.strictEqual(util.inspect(arr2, { showHidden: true }),
|
||||
"[ <2 empty items>, [length]: 2, '00': 1 ]");
|
||||
arr2['01'] = 2;
|
||||
assert.strictEqual(util.inspect(arr2),
|
||||
"[ <2 empty items>, '00': 1, '01': 2 ]");
|
||||
assert.strictEqual(util.inspect(arr2, { showHidden: true }),
|
||||
"[ <2 empty items>, [length]: 2, '00': 1, '01': 2 ]");
|
||||
|
||||
const arr3 = [];
|
||||
arr3[-1] = -1;
|
||||
assert.strictEqual(util.inspect(arr3), "[ '-1': -1 ]");
|
||||
}
|
||||
|
||||
// Function with properties
|
||||
{
|
||||
const value = () => {};
|
||||
@ -387,6 +421,11 @@ assert.strictEqual(util.inspect(-0), '-0');
|
||||
util.inspect(a, { breakLength: Infinity }),
|
||||
'[ \'foo\', <1 empty item>, \'baz\', \'bar\', <96 empty items>, \'qux\' ]'
|
||||
);
|
||||
delete a[3];
|
||||
assert.strictEqual(
|
||||
util.inspect(a, { maxArrayLength: 4 }),
|
||||
'[ \'foo\', <1 empty item>, \'baz\', <97 empty items>, ... 1 more item ]'
|
||||
);
|
||||
}
|
||||
|
||||
// test for Array constructor in different context
|
||||
@ -604,6 +643,10 @@ assert.doesNotThrow(() => {
|
||||
util.inspect(subject, { depth: null }).includes('{ d: 0 }'),
|
||||
true
|
||||
);
|
||||
assert.strictEqual(
|
||||
util.inspect(subject, { depth: undefined }).includes('{ d: 0 }'),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
@ -714,6 +757,7 @@ assert.doesNotThrow(() => {
|
||||
testLines([1, 2, 3, 4, 5, 6, 7]);
|
||||
testLines(bigArray);
|
||||
testLines({ foo: 'bar', baz: 35, b: { a: 35 } });
|
||||
testLines({ a: { a: 3, b: 1, c: 1, d: 1, e: 1, f: 1, g: 1, h: 1 }, b: 1 });
|
||||
testLines({
|
||||
foo: 'bar',
|
||||
baz: 35,
|
||||
@ -941,6 +985,20 @@ if (typeof Symbol !== 'undefined') {
|
||||
'PromiseSubclass { <pending> }');
|
||||
}
|
||||
|
||||
// Empty and circular before depth
|
||||
{
|
||||
const arr = [[[[]]]];
|
||||
assert.strictEqual(util.inspect(arr), '[ [ [ [] ] ] ]');
|
||||
arr[0][0][0][0] = [];
|
||||
assert.strictEqual(util.inspect(arr), '[ [ [ [Array] ] ] ]');
|
||||
arr[0][0][0] = {};
|
||||
assert.strictEqual(util.inspect(arr), '[ [ [ {} ] ] ]');
|
||||
arr[0][0][0] = { a: 2 };
|
||||
assert.strictEqual(util.inspect(arr), '[ [ [ [Object] ] ] ]');
|
||||
arr[0][0][0] = arr;
|
||||
assert.strictEqual(util.inspect(arr), '[ [ [ [Circular] ] ] ]');
|
||||
}
|
||||
|
||||
// Corner cases.
|
||||
{
|
||||
const x = { constructor: 42 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user