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 inspect performance #20009

Closed
Closed
Show file tree
Hide file tree
Changes from all 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
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 @@ -396,6 +395,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 @@ -475,15 +503,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 @@ -496,22 +519,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 @@ -543,11 +569,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 @@ -579,27 +610,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 @@ -638,9 +670,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 @@ -675,8 +707,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 @@ -695,9 +727,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