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}`;
|
||||
}
|
||||
|
||||
// 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');
|
||||
|
||||
@ -431,7 +394,6 @@ module.exports = {
|
||||
filterDuplicateStrings,
|
||||
getConstructorOf,
|
||||
getSystemErrorName,
|
||||
getIdentificationOf,
|
||||
isError,
|
||||
isInsideNodeModules,
|
||||
join,
|
||||
|
74
lib/util.js
74
lib/util.js
@ -72,7 +72,6 @@ const {
|
||||
customInspectSymbol,
|
||||
deprecate,
|
||||
getSystemErrorName: internalErrorName,
|
||||
getIdentificationOf,
|
||||
isError,
|
||||
promisify,
|
||||
join,
|
||||
@ -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') {
|
||||
@ -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;
|
||||
@ -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}] {`, '}'];
|
||||
@ -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 '{}';
|
||||
}
|
||||
@ -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 {
|
||||
@ -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)}{`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user