Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: improve util.inspect performance #14492

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const inspectDefaultOptions = Object.seal({

const numbersOnlyRE = /^\d+$/;

const objectHasOwnProperty = Object.prototype.hasOwnProperty;
const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
const regExpToString = RegExp.prototype.toString;
const dateToISOString = Date.prototype.toISOString;
Expand Down Expand Up @@ -672,22 +671,33 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
var output = [];
let visibleLength = 0;
let index = 0;
while (index < value.length && visibleLength < ctx.maxArrayLength) {
let emptyItems = 0;
while (index < value.length && !hasOwnProperty(value, String(index))) {
emptyItems++;
index++;
}
if (emptyItems > 0) {
for (const elem in value) {
if (visibleLength === ctx.maxArrayLength)
break;
const i = +elem;
if (index !== i) {
// Skip zero and negative numbers as well as non numbers
if (i > 0 === false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment on why this is necessary, and different from i <= 0? (I understand it because I saw an earlier version of the PR, but comments are more for future than now anyway.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

continue;
const emptyItems = i - index;
const ending = emptyItems > 1 ? 's' : '';
const message = `<${emptyItems} empty item${ending}>`;
output.push(ctx.stylize(message, 'undefined'));
} else {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
String(index), true));
index++;
index = i;
if (++visibleLength === ctx.maxArrayLength)
break;
}
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
elem, true));
visibleLength++;
index++;
}
if (index < value.length && visibleLength !== ctx.maxArrayLength) {
const len = value.length - index;
const ending = len > 1 ? 's' : '';
const message = `<${len} empty item${ending}>`;
output.push(ctx.stylize(message, 'undefined'));
index = value.length;
}
var remaining = value.length - index;
if (remaining > 0) {
Expand Down Expand Up @@ -803,7 +813,7 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
str = ctx.stylize('[Setter]', 'special');
}
}
if (!hasOwnProperty(visibleKeys, key)) {
if (visibleKeys[key] === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth to eventually make visibleKeys an ES2015 Set instead of emulating it via an object, but I don't know how it will affect performance with the current V8 (if it will at all).

Copy link
Member Author

@BridgeAR BridgeAR Jul 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I looked at the code there were a couple more optimizations possible but I tried to be more surgical here. E.g. for arrays there is no need for Object.keys when using for in and ctx.seen should be a Set instead of an Array.

if (typeof key === 'symbol') {
name = `[${ctx.stylize(key.toString(), 'symbol')}]`;
} else {
Expand Down Expand Up @@ -982,11 +992,6 @@ function _extend(target, source) {
return target;
}

function hasOwnProperty(obj, prop) {
return objectHasOwnProperty.call(obj, prop);
}


// Deprecated old stuff.

function print(...args) {
Expand Down
10 changes: 9 additions & 1 deletion test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,16 @@ assert.strictEqual(
get: function() { this.push(true); return this.length; }
}
);
Object.defineProperty(
value,
'-1',
{
enumerable: true,
value: -1
}
);
assert.strictEqual(util.inspect(value),
'[ 1, 2, 3, growingLength: [Getter] ]');
'[ 1, 2, 3, growingLength: [Getter], \'-1\': -1 ]');
}

// Function with properties
Expand Down