From de7e7e7a2d192fd70f36e041d065b31ba44c32aa Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 13 Apr 2018 15:56:37 +0200 Subject: [PATCH 1/2] 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. --- lib/internal/util.js | 38 ------------------------ lib/util.js | 69 ++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/lib/internal/util.js b/lib/internal/util.js index 953b1ce0f577e5..3cd370258bf72c 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -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, diff --git a/lib/util.js b/lib/util.js index 762461402e99c7..105ad32bded775 100644 --- a/lib/util.js +++ b/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,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) @@ -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 { @@ -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)}{`; } } } @@ -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 @@ -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`; From b1ae606332e6fbc80a44d06ec7c6bbbbaf03d1f7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 18 Apr 2018 01:09:34 +0200 Subject: [PATCH 2/2] fixup --- lib/util.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/util.js b/lib/util.js index 105ad32bded775..91404d254dc9a4 100644 --- a/lib/util.js +++ b/lib/util.js @@ -574,6 +574,11 @@ function formatValue(ctx, value, recurseTimes, ln) { 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 '{}'; }