Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
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.
  • Loading branch information
BridgeAR committed Apr 16, 2018
commit de7e7e7a2d192fd70f36e041d065b31ba44c32aa
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
69 changes: 48 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,7 +569,7 @@ function formatValue(ctx, value, recurseTimes, ln) {
}
if (noIterator) {
braces = ['{', '}'];
if (prefix === 'Object ') {
if (constructor === 'Object') {
if (isArgumentsObject(value)) {
braces[0] = '[Arguments] {';
if (keyLength === 0)
Expand Down Expand Up @@ -579,27 +605,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 +665,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 +702,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 +722,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