diff options
author | Ruben Bridgewater <ruben@bridgewater.de> | 2019-11-02 20:50:09 +0300 |
---|---|---|
committer | Ruben Bridgewater <ruben@bridgewater.de> | 2019-12-15 16:58:57 +0300 |
commit | eeae5986fdd77558cee2699afbe83a3a461b61fb (patch) | |
tree | e139e0ffda5767620a2f42f9479ca4ff5b77e750 /lib/internal/util | |
parent | b2bfacb9142c70e060f522cc5f05f937e66b07c7 (diff) |
util: refactor inspect code for constistency
This removes the special handling to inspect iterable objects with
a null prototype. It is now handled together with the regular
prototype.
PR-URL: https://github.com/nodejs/node/pull/30225
Reviewed-By: Michaƫl Zasso <targos@protonmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib/internal/util')
-rw-r--r-- | lib/internal/util/inspect.js | 159 |
1 files changed, 67 insertions, 92 deletions
diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 9f5cc8a45e8..ecd003449ea 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -10,6 +10,7 @@ const { DatePrototypeToString, ErrorPrototypeToString, JSONStringify, + MapPrototype, MapPrototypeEntries, MathFloor, MathMax, @@ -21,10 +22,8 @@ const { NumberPrototypeValueOf, ObjectAssign, ObjectCreate, - ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, - ObjectGetOwnPropertyDescriptors, ObjectGetOwnPropertyNames, ObjectGetOwnPropertySymbols, ObjectGetPrototypeOf, @@ -34,6 +33,7 @@ const { ObjectPrototypePropertyIsEnumerable, ObjectSeal, RegExpPrototypeToString, + SetPrototype, SetPrototypeValues, StringPrototypeValueOf, SymbolPrototypeToString, @@ -113,6 +113,11 @@ const assert = require('internal/assert'); const { NativeModule } = require('internal/bootstrap/loaders'); +const setSizeGetter = uncurryThis( + ObjectGetOwnPropertyDescriptor(SetPrototype, 'size').get); +const mapSizeGetter = uncurryThis( + ObjectGetOwnPropertyDescriptor(MapPrototype, 'size').get); + let hexSlice; const builtInObjects = new Set( @@ -651,51 +656,6 @@ function findTypedConstructor(value) { } } -let lazyNullPrototypeCache; -// Creates a subclass and name -// the constructor as `${clazz} : null prototype` -function clazzWithNullPrototype(clazz, name) { - if (lazyNullPrototypeCache === undefined) { - lazyNullPrototypeCache = new Map(); - } else { - const cachedClass = lazyNullPrototypeCache.get(clazz); - if (cachedClass !== undefined) { - return cachedClass; - } - } - class NullPrototype extends clazz { - get [SymbolToStringTag]() { - return ''; - } - } - ObjectDefineProperty(NullPrototype.prototype.constructor, 'name', - { value: `[${name}: null prototype]` }); - lazyNullPrototypeCache.set(clazz, NullPrototype); - return NullPrototype; -} - -function noPrototypeIterator(ctx, value, recurseTimes) { - let newVal; - if (isSet(value)) { - const clazz = clazzWithNullPrototype(Set, 'Set'); - newVal = new clazz(SetPrototypeValues(value)); - } else if (isMap(value)) { - const clazz = clazzWithNullPrototype(Map, 'Map'); - newVal = new clazz(MapPrototypeEntries(value)); - } else if (ArrayIsArray(value)) { - const clazz = clazzWithNullPrototype(Array, 'Array'); - newVal = new clazz(value.length); - } else if (isTypedArray(value)) { - const constructor = findTypedConstructor(value); - const clazz = clazzWithNullPrototype(constructor, constructor.name); - newVal = new clazz(value); - } - if (newVal !== undefined) { - ObjectDefineProperties(newVal, ObjectGetOwnPropertyDescriptors(value)); - return formatRaw(ctx, newVal, recurseTimes); - } -} - // Note: using `formatValue` directly requires the indentation level to be // corrected by setting `ctx.indentationLvL += diff` and then to decrease the // value afterwards again. @@ -798,7 +758,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { let extrasType = kObjectType; // Iterators and the rest are split to reduce checks. - if (value[SymbolIterator]) { + // We have to check all values in case the constructor is set to null. + // Otherwise it would not possible to identify all types properly. + if (value[SymbolIterator] || constructor === null) { noIterator = false; if (ArrayIsArray(value)) { keys = getOwnNonIndexProperties(value, filter); @@ -810,37 +772,66 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { extrasType = kArrayExtrasType; formatter = formatArray; } else if (isSet(value)) { + const size = setSizeGetter(value); keys = getKeys(value, ctx.showHidden); - const prefix = getPrefix(constructor, tag, 'Set'); - if (value.size === 0 && keys.length === 0 && protoProps === undefined) + let prefix = ''; + if (constructor !== null) { + if (constructor === tag) + tag = ''; + prefix = getPrefix(`${constructor}`, tag, ''); + formatter = formatSet.bind(null, value, size); + } else { + prefix = getPrefix(constructor, tag, 'Set'); + formatter = formatSet.bind(null, SetPrototypeValues(value), size); + } + if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; - formatter = formatSet; } else if (isMap(value)) { + const size = mapSizeGetter(value); keys = getKeys(value, ctx.showHidden); - const prefix = getPrefix(constructor, tag, 'Map'); - if (value.size === 0 && keys.length === 0 && protoProps === undefined) + let prefix = ''; + if (constructor !== null) { + if (constructor === tag) + tag = ''; + prefix = getPrefix(`${constructor}`, tag, ''); + formatter = formatMap.bind(null, value, size); + } else { + prefix = getPrefix(constructor, tag, 'Map'); + formatter = formatMap.bind(null, MapPrototypeEntries(value), size); + } + if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; - formatter = formatMap; } else if (isTypedArray(value)) { keys = getOwnNonIndexProperties(value, filter); - const prefix = constructor !== null ? - getPrefix(constructor, tag) : - getPrefix(constructor, tag, findTypedConstructor(value).name); + let bound = value; + let prefix = ''; + if (constructor === null) { + const constr = findTypedConstructor(value); + prefix = getPrefix(constructor, tag, constr.name); + // Reconstruct the array information. + bound = new constr(value); + } else { + prefix = getPrefix(constructor, tag); + } braces = [`${prefix}[`, ']']; if (value.length === 0 && keys.length === 0 && !ctx.showHidden) return `${braces[0]}]`; - formatter = formatTypedArray; + // Special handle the value. The original value is required below. The + // bound function is required to reconstruct missing information. + formatter = formatTypedArray.bind(null, bound); extrasType = kArrayExtrasType; } else if (isMapIterator(value)) { keys = getKeys(value, ctx.showHidden); braces = getIteratorBraces('Map', tag); - formatter = formatIterator; + // Add braces to the formatter parameters. + formatter = formatIterator.bind(null, braces); } else if (isSetIterator(value)) { keys = getKeys(value, ctx.showHidden); braces = getIteratorBraces('Set', tag); - formatter = formatIterator; + // Add braces to the formatter parameters. + formatter = formatIterator.bind(null, braces); } else { noIterator = true; } @@ -918,36 +909,20 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { formatter = ctx.showHidden ? formatWeakMap : formatWeakCollection; } else if (isModuleNamespaceObject(value)) { braces[0] = `[${tag}] {`; - formatter = formatNamespaceObject; + // Special handle keys for namespace objects. + formatter = formatNamespaceObject.bind(null, keys); } else if (isBoxedPrimitive(value)) { base = getBoxedBase(value, ctx, keys, constructor, tag); if (keys.length === 0 && protoProps === undefined) { return base; } } else { - // The input prototype got manipulated. Special handle these. We have to - // rebuild the information so we are able to display everything. - if (constructor === null) { - const specialIterator = noPrototypeIterator(ctx, value, recurseTimes); - if (specialIterator) { - return specialIterator; - } - } - if (isMapIterator(value)) { - braces = getIteratorBraces('Map', tag); - formatter = formatIterator; - } else if (isSetIterator(value)) { - braces = getIteratorBraces('Set', tag); - formatter = formatIterator; - // Handle other regular objects again. - } else { - if (keys.length === 0 && protoProps === undefined) { - if (isExternal(value)) - return ctx.stylize('[External]', 'special'); - return `${getCtxStyle(value, constructor, tag)}{}`; - } - braces[0] = `${getCtxStyle(value, constructor, tag)}{`; + if (keys.length === 0 && protoProps === undefined) { + if (isExternal(value)) + return ctx.stylize('[External]', 'special'); + return `${getCtxStyle(value, constructor, tag)}{}`; } + braces[0] = `${getCtxStyle(value, constructor, tag)}{`; } } @@ -964,7 +939,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { let output; const indentationLvl = ctx.indentationLvl; try { - output = formatter(ctx, value, recurseTimes, keys, braces); + output = formatter(ctx, value, recurseTimes); for (i = 0; i < keys.length; i++) { output.push( formatProperty(ctx, value, recurseTimes, keys[i], extrasType)); @@ -1322,7 +1297,7 @@ function formatPrimitive(fn, value, ctx) { return fn(SymbolPrototypeToString(value), 'symbol'); } -function formatNamespaceObject(ctx, value, recurseTimes, keys) { +function formatNamespaceObject(keys, ctx, value, recurseTimes) { const output = new Array(keys.length); for (let i = 0; i < keys.length; i++) { try { @@ -1424,7 +1399,7 @@ function formatArray(ctx, value, recurseTimes) { return output; } -function formatTypedArray(ctx, value, recurseTimes) { +function formatTypedArray(value, ctx, ignored, recurseTimes) { const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), value.length); const remaining = value.length - maxLength; const output = new Array(maxLength); @@ -1455,7 +1430,7 @@ function formatTypedArray(ctx, value, recurseTimes) { return output; } -function formatSet(ctx, value, recurseTimes) { +function formatSet(value, size, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; for (const v of value) { @@ -1466,11 +1441,11 @@ function formatSet(ctx, value, recurseTimes) { // arrays. For consistency's sake, do the same for `size`, even though this // property isn't selected by ObjectGetOwnPropertyNames(). if (ctx.showHidden) - output.push(`[size]: ${ctx.stylize(`${value.size}`, 'number')}`); + output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); return output; } -function formatMap(ctx, value, recurseTimes) { +function formatMap(value, size, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; for (const [k, v] of value) { @@ -1480,7 +1455,7 @@ function formatMap(ctx, value, recurseTimes) { ctx.indentationLvl -= 2; // See comment in formatSet if (ctx.showHidden) - output.push(`[size]: ${ctx.stylize(`${value.size}`, 'number')}`); + output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); return output; } @@ -1558,7 +1533,7 @@ function formatWeakMap(ctx, value, recurseTimes) { return formatMapIterInner(ctx, recurseTimes, entries, kWeak); } -function formatIterator(ctx, value, recurseTimes, keys, braces) { +function formatIterator(braces, ctx, value, recurseTimes) { const [entries, isKeyValue] = previewEntries(value, true); if (isKeyValue) { // Mark entry iterators as such. @@ -1593,7 +1568,7 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc) { desc = desc || ObjectGetOwnPropertyDescriptor(value, key) || { value: value[key], enumerable: true }; if (desc.value !== undefined) { - const diff = (type !== kObjectType || ctx.compact !== true) ? 2 : 3; + const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; ctx.indentationLvl += diff; str = formatValue(ctx, desc.value, recurseTimes); if (diff === 3) { |