Skip to content

Commit

Permalink
util: improve inspect performance
Browse files Browse the repository at this point in the history
This improves a slow code part in `util.inspect` by directly
retrieving the `Symbol.toStringTag` and by optimizing some code
paths.

PR-URL: #20009
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Yuta Hiroto <hello@hiroppy.me>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
  • Loading branch information
BridgeAR authored and jasnell committed Apr 20, 2018
1 parent 8a5b7b2 commit 7f3838b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 59 deletions.
38 changes: 0 additions & 38 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,43 +252,6 @@ function getSystemErrorName(err) {
return entry ? entry[0] : `Unknown system error ${err}`;
}

// getConstructorOf is wrapped into this to save iterations
function getIdentificationOf(obj) {
const original = obj;
let constructor;
let tag;

while (obj) {
if (constructor === undefined) {
const desc = Object.getOwnPropertyDescriptor(obj, 'constructor');
if (desc !== undefined &&
typeof desc.value === 'function' &&
desc.value.name !== '')
constructor = desc.value.name;
}

if (tag === undefined) {
const desc = Object.getOwnPropertyDescriptor(obj, Symbol.toStringTag);
if (desc !== undefined) {
if (typeof desc.value === 'string') {
tag = desc.value;
} else if (desc.get !== undefined) {
tag = desc.get.call(original);
if (typeof tag !== 'string')
tag = undefined;
}
}
}

if (constructor !== undefined && tag !== undefined)
break;

obj = Object.getPrototypeOf(obj);
}

return { constructor, tag };
}

const kCustomPromisifiedSymbol = Symbol('util.promisify.custom');
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');

Expand Down Expand Up @@ -431,7 +394,6 @@ module.exports = {
filterDuplicateStrings,
getConstructorOf,
getSystemErrorName,
getIdentificationOf,
isError,
isInsideNodeModules,
join,
Expand Down
74 changes: 53 additions & 21 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const {
customInspectSymbol,
deprecate,
getSystemErrorName: internalErrorName,
getIdentificationOf,
isError,
promisify,
join,
Expand Down Expand Up @@ -397,6 +396,35 @@ function stylizeNoColor(str, styleType) {
return str;
}

function getConstructorName(obj) {
while (obj) {
const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor');
if (descriptor !== undefined &&
typeof descriptor.value === 'function' &&
descriptor.value.name !== '') {
return descriptor.value.name;
}

obj = Object.getPrototypeOf(obj);
}

return '';
}

function getPrefix(constructor, tag) {
if (constructor !== '') {
if (tag !== '' && constructor !== tag) {
return `${constructor} [${tag}] `;
}
return `${constructor} `;
}

if (tag !== '')
return `[${tag}] `;

return '';
}

function formatValue(ctx, value, recurseTimes, ln) {
// Primitive types cannot have properties
if (typeof value !== 'object' && typeof value !== 'function') {
Expand Down Expand Up @@ -476,15 +504,10 @@ function formatValue(ctx, value, recurseTimes, ln) {

const keyLength = keys.length + symbols.length;

const { constructor, tag } = getIdentificationOf(value);
let prefix = '';
if (constructor && tag && constructor !== tag)
prefix = `${constructor} [${tag}] `;
else if (constructor)
prefix = `${constructor} `;
else if (tag)
prefix = `[${tag}] `;

const constructor = getConstructorName(value);
let tag = value[Symbol.toStringTag];
if (typeof tag !== 'string')
tag = '';
let base = '';
let formatter = formatObject;
let braces;
Expand All @@ -497,22 +520,25 @@ function formatValue(ctx, value, recurseTimes, ln) {
noIterator = false;
if (Array.isArray(value)) {
// Only set the constructor for non ordinary ("Array [...]") arrays.
const prefix = getPrefix(constructor, tag);
braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']'];
if (value.length === 0 && keyLength === 0)
return `${braces[0]}]`;
formatter = formatArray;
} else if (isSet(value)) {
const prefix = getPrefix(constructor, tag);
if (value.size === 0 && keyLength === 0)
return `${prefix}{}`;
braces = [`${prefix}{`, '}'];
formatter = formatSet;
} else if (isMap(value)) {
const prefix = getPrefix(constructor, tag);
if (value.size === 0 && keyLength === 0)
return `${prefix}{}`;
braces = [`${prefix}{`, '}'];
formatter = formatMap;
} else if (isTypedArray(value)) {
braces = [`${prefix}[`, ']'];
braces = [`${getPrefix(constructor, tag)}[`, ']'];
formatter = formatTypedArray;
} else if (isMapIterator(value)) {
braces = [`[${tag}] {`, '}'];
Expand Down Expand Up @@ -544,11 +570,16 @@ function formatValue(ctx, value, recurseTimes, ln) {
}
if (noIterator) {
braces = ['{', '}'];
if (prefix === 'Object ') {
if (constructor === 'Object') {
if (isArgumentsObject(value)) {
braces[0] = '[Arguments] {';
if (keyLength === 0)
return '[Arguments] {}';
} else if (tag !== '') {
braces[0] = `${getPrefix(constructor, tag)}{`;
if (keyLength === 0) {
return `${braces[0]}}`;
}
} else if (keyLength === 0) {
return '{}';
}
Expand Down Expand Up @@ -580,27 +611,28 @@ function formatValue(ctx, value, recurseTimes, ln) {
// Fast path for ArrayBuffer and SharedArrayBuffer.
// Can't do the same for DataView because it has a non-primitive
// .buffer property that we need to recurse for.
const prefix = getPrefix(constructor, tag);
if (keyLength === 0)
return prefix +
`{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`;
braces[0] = `${prefix}{`;
keys.unshift('byteLength');
} else if (isDataView(value)) {
braces[0] = `${prefix}{`;
braces[0] = `${getPrefix(constructor, tag)}{`;
// .buffer goes last, it's not a primitive like the others.
keys.unshift('byteLength', 'byteOffset', 'buffer');
} else if (isPromise(value)) {
braces[0] = `${prefix}{`;
braces[0] = `${getPrefix(constructor, tag)}{`;
formatter = formatPromise;
} else if (isWeakSet(value)) {
braces[0] = `${prefix}{`;
braces[0] = `${getPrefix(constructor, tag)}{`;
if (ctx.showHidden) {
formatter = formatWeakSet;
} else {
extra = '[items unknown]';
}
} else if (isWeakMap(value)) {
braces[0] = `${prefix}{`;
braces[0] = `${getPrefix(constructor, tag)}{`;
if (ctx.showHidden) {
formatter = formatWeakMap;
} else {
Expand Down Expand Up @@ -639,9 +671,9 @@ function formatValue(ctx, value, recurseTimes, ln) {
} else if (keyLength === 0) {
if (isExternal(value))
return ctx.stylize('[External]', 'special');
return `${prefix}{}`;
return `${getPrefix(constructor, tag)}{}`;
} else {
braces[0] = `${prefix}{`;
braces[0] = `${getPrefix(constructor, tag)}{`;
}
}
}
Expand Down Expand Up @@ -676,8 +708,8 @@ function formatNumber(fn, value) {
function formatPrimitive(fn, value, ctx) {
if (typeof value === 'string') {
if (ctx.compact === false &&
value.length > MIN_LINE_LENGTH &&
ctx.indentationLvl + value.length > ctx.breakLength) {
ctx.indentationLvl + value.length > ctx.breakLength &&
value.length > MIN_LINE_LENGTH) {
// eslint-disable-next-line max-len
const minLineLength = Math.max(ctx.breakLength - ctx.indentationLvl, MIN_LINE_LENGTH);
// eslint-disable-next-line max-len
Expand All @@ -696,9 +728,9 @@ function formatPrimitive(fn, value, ctx) {
// eslint-disable-next-line max-len, node-core/no-unescaped-regexp-dot
readableRegExps[divisor] = new RegExp(`(.|\\n){1,${divisor}}(\\s|$)|(\\n|.)+?(\\s|$)`, 'gm');
}
const indent = ' '.repeat(ctx.indentationLvl);
const matches = value.match(readableRegExps[divisor]);
if (matches.length > 1) {
const indent = ' '.repeat(ctx.indentationLvl);
res += `${fn(strEscape(matches[0]), 'string')} +\n`;
for (var i = 1; i < matches.length - 1; i++) {
res += `${indent} ${fn(strEscape(matches[i]), 'string')} +\n`;
Expand Down

0 comments on commit 7f3838b

Please sign in to comment.