util: improve inspect performance
This improves a slow code part in `util.inspect` by directly retrieving the `Symbol.toStringTag` and by optimizing some code paths. PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
parent
94596560c2
commit
ad1d1057f9
@ -252,43 +252,6 @@ function getSystemErrorName(err) {
|
|||||||
return entry ? entry[0] : `Unknown system error ${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 kCustomPromisifiedSymbol = Symbol('util.promisify.custom');
|
||||||
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
|
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
|
||||||
|
|
||||||
@ -431,7 +394,6 @@ module.exports = {
|
|||||||
filterDuplicateStrings,
|
filterDuplicateStrings,
|
||||||
getConstructorOf,
|
getConstructorOf,
|
||||||
getSystemErrorName,
|
getSystemErrorName,
|
||||||
getIdentificationOf,
|
|
||||||
isError,
|
isError,
|
||||||
isInsideNodeModules,
|
isInsideNodeModules,
|
||||||
join,
|
join,
|
||||||
|
74
lib/util.js
74
lib/util.js
@ -72,7 +72,6 @@ const {
|
|||||||
customInspectSymbol,
|
customInspectSymbol,
|
||||||
deprecate,
|
deprecate,
|
||||||
getSystemErrorName: internalErrorName,
|
getSystemErrorName: internalErrorName,
|
||||||
getIdentificationOf,
|
|
||||||
isError,
|
isError,
|
||||||
promisify,
|
promisify,
|
||||||
join,
|
join,
|
||||||
@ -396,6 +395,35 @@ function stylizeNoColor(str, styleType) {
|
|||||||
return str;
|
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) {
|
function formatValue(ctx, value, recurseTimes, ln) {
|
||||||
// Primitive types cannot have properties
|
// Primitive types cannot have properties
|
||||||
if (typeof value !== 'object' && typeof value !== 'function') {
|
if (typeof value !== 'object' && typeof value !== 'function') {
|
||||||
@ -475,15 +503,10 @@ function formatValue(ctx, value, recurseTimes, ln) {
|
|||||||
|
|
||||||
const keyLength = keys.length + symbols.length;
|
const keyLength = keys.length + symbols.length;
|
||||||
|
|
||||||
const { constructor, tag } = getIdentificationOf(value);
|
const constructor = getConstructorName(value);
|
||||||
let prefix = '';
|
let tag = value[Symbol.toStringTag];
|
||||||
if (constructor && tag && constructor !== tag)
|
if (typeof tag !== 'string')
|
||||||
prefix = `${constructor} [${tag}] `;
|
tag = '';
|
||||||
else if (constructor)
|
|
||||||
prefix = `${constructor} `;
|
|
||||||
else if (tag)
|
|
||||||
prefix = `[${tag}] `;
|
|
||||||
|
|
||||||
let base = '';
|
let base = '';
|
||||||
let formatter = formatObject;
|
let formatter = formatObject;
|
||||||
let braces;
|
let braces;
|
||||||
@ -496,22 +519,25 @@ function formatValue(ctx, value, recurseTimes, ln) {
|
|||||||
noIterator = false;
|
noIterator = false;
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
// Only set the constructor for non ordinary ("Array [...]") arrays.
|
// Only set the constructor for non ordinary ("Array [...]") arrays.
|
||||||
|
const prefix = getPrefix(constructor, tag);
|
||||||
braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']'];
|
braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']'];
|
||||||
if (value.length === 0 && keyLength === 0)
|
if (value.length === 0 && keyLength === 0)
|
||||||
return `${braces[0]}]`;
|
return `${braces[0]}]`;
|
||||||
formatter = formatArray;
|
formatter = formatArray;
|
||||||
} else if (isSet(value)) {
|
} else if (isSet(value)) {
|
||||||
|
const prefix = getPrefix(constructor, tag);
|
||||||
if (value.size === 0 && keyLength === 0)
|
if (value.size === 0 && keyLength === 0)
|
||||||
return `${prefix}{}`;
|
return `${prefix}{}`;
|
||||||
braces = [`${prefix}{`, '}'];
|
braces = [`${prefix}{`, '}'];
|
||||||
formatter = formatSet;
|
formatter = formatSet;
|
||||||
} else if (isMap(value)) {
|
} else if (isMap(value)) {
|
||||||
|
const prefix = getPrefix(constructor, tag);
|
||||||
if (value.size === 0 && keyLength === 0)
|
if (value.size === 0 && keyLength === 0)
|
||||||
return `${prefix}{}`;
|
return `${prefix}{}`;
|
||||||
braces = [`${prefix}{`, '}'];
|
braces = [`${prefix}{`, '}'];
|
||||||
formatter = formatMap;
|
formatter = formatMap;
|
||||||
} else if (isTypedArray(value)) {
|
} else if (isTypedArray(value)) {
|
||||||
braces = [`${prefix}[`, ']'];
|
braces = [`${getPrefix(constructor, tag)}[`, ']'];
|
||||||
formatter = formatTypedArray;
|
formatter = formatTypedArray;
|
||||||
} else if (isMapIterator(value)) {
|
} else if (isMapIterator(value)) {
|
||||||
braces = [`[${tag}] {`, '}'];
|
braces = [`[${tag}] {`, '}'];
|
||||||
@ -543,11 +569,16 @@ function formatValue(ctx, value, recurseTimes, ln) {
|
|||||||
}
|
}
|
||||||
if (noIterator) {
|
if (noIterator) {
|
||||||
braces = ['{', '}'];
|
braces = ['{', '}'];
|
||||||
if (prefix === 'Object ') {
|
if (constructor === 'Object') {
|
||||||
if (isArgumentsObject(value)) {
|
if (isArgumentsObject(value)) {
|
||||||
braces[0] = '[Arguments] {';
|
braces[0] = '[Arguments] {';
|
||||||
if (keyLength === 0)
|
if (keyLength === 0)
|
||||||
return '[Arguments] {}';
|
return '[Arguments] {}';
|
||||||
|
} else if (tag !== '') {
|
||||||
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
|
if (keyLength === 0) {
|
||||||
|
return `${braces[0]}}`;
|
||||||
|
}
|
||||||
} else if (keyLength === 0) {
|
} else if (keyLength === 0) {
|
||||||
return '{}';
|
return '{}';
|
||||||
}
|
}
|
||||||
@ -579,27 +610,28 @@ function formatValue(ctx, value, recurseTimes, ln) {
|
|||||||
// Fast path for ArrayBuffer and SharedArrayBuffer.
|
// Fast path for ArrayBuffer and SharedArrayBuffer.
|
||||||
// Can't do the same for DataView because it has a non-primitive
|
// Can't do the same for DataView because it has a non-primitive
|
||||||
// .buffer property that we need to recurse for.
|
// .buffer property that we need to recurse for.
|
||||||
|
const prefix = getPrefix(constructor, tag);
|
||||||
if (keyLength === 0)
|
if (keyLength === 0)
|
||||||
return prefix +
|
return prefix +
|
||||||
`{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`;
|
`{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`;
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${prefix}{`;
|
||||||
keys.unshift('byteLength');
|
keys.unshift('byteLength');
|
||||||
} else if (isDataView(value)) {
|
} else if (isDataView(value)) {
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
// .buffer goes last, it's not a primitive like the others.
|
// .buffer goes last, it's not a primitive like the others.
|
||||||
keys.unshift('byteLength', 'byteOffset', 'buffer');
|
keys.unshift('byteLength', 'byteOffset', 'buffer');
|
||||||
} else if (isPromise(value)) {
|
} else if (isPromise(value)) {
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
formatter = formatPromise;
|
formatter = formatPromise;
|
||||||
} else if (isWeakSet(value)) {
|
} else if (isWeakSet(value)) {
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
if (ctx.showHidden) {
|
if (ctx.showHidden) {
|
||||||
formatter = formatWeakSet;
|
formatter = formatWeakSet;
|
||||||
} else {
|
} else {
|
||||||
extra = '[items unknown]';
|
extra = '[items unknown]';
|
||||||
}
|
}
|
||||||
} else if (isWeakMap(value)) {
|
} else if (isWeakMap(value)) {
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
if (ctx.showHidden) {
|
if (ctx.showHidden) {
|
||||||
formatter = formatWeakMap;
|
formatter = formatWeakMap;
|
||||||
} else {
|
} else {
|
||||||
@ -638,9 +670,9 @@ function formatValue(ctx, value, recurseTimes, ln) {
|
|||||||
} else if (keyLength === 0) {
|
} else if (keyLength === 0) {
|
||||||
if (isExternal(value))
|
if (isExternal(value))
|
||||||
return ctx.stylize('[External]', 'special');
|
return ctx.stylize('[External]', 'special');
|
||||||
return `${prefix}{}`;
|
return `${getPrefix(constructor, tag)}{}`;
|
||||||
} else {
|
} else {
|
||||||
braces[0] = `${prefix}{`;
|
braces[0] = `${getPrefix(constructor, tag)}{`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,8 +707,8 @@ function formatNumber(fn, value) {
|
|||||||
function formatPrimitive(fn, value, ctx) {
|
function formatPrimitive(fn, value, ctx) {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
if (ctx.compact === false &&
|
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
|
// eslint-disable-next-line max-len
|
||||||
const minLineLength = Math.max(ctx.breakLength - ctx.indentationLvl, MIN_LINE_LENGTH);
|
const minLineLength = Math.max(ctx.breakLength - ctx.indentationLvl, MIN_LINE_LENGTH);
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
@ -695,9 +727,9 @@ function formatPrimitive(fn, value, ctx) {
|
|||||||
// eslint-disable-next-line max-len, node-core/no-unescaped-regexp-dot
|
// eslint-disable-next-line max-len, node-core/no-unescaped-regexp-dot
|
||||||
readableRegExps[divisor] = new RegExp(`(.|\\n){1,${divisor}}(\\s|$)|(\\n|.)+?(\\s|$)`, 'gm');
|
readableRegExps[divisor] = new RegExp(`(.|\\n){1,${divisor}}(\\s|$)|(\\n|.)+?(\\s|$)`, 'gm');
|
||||||
}
|
}
|
||||||
const indent = ' '.repeat(ctx.indentationLvl);
|
|
||||||
const matches = value.match(readableRegExps[divisor]);
|
const matches = value.match(readableRegExps[divisor]);
|
||||||
if (matches.length > 1) {
|
if (matches.length > 1) {
|
||||||
|
const indent = ' '.repeat(ctx.indentationLvl);
|
||||||
res += `${fn(strEscape(matches[0]), 'string')} +\n`;
|
res += `${fn(strEscape(matches[0]), 'string')} +\n`;
|
||||||
for (var i = 1; i < matches.length - 1; i++) {
|
for (var i = 1; i < matches.length - 1; i++) {
|
||||||
res += `${indent} ${fn(strEscape(matches[i]), 'string')} +\n`;
|
res += `${indent} ${fn(strEscape(matches[i]), 'string')} +\n`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user