From ae2333db65c5847f65ebb3bcdd902badccee25fc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 22 Apr 2019 23:29:56 +0200 Subject: [PATCH] util: add prototype support for boxed primitives This makes sure manipulated prototypes from boxed primitives will be highlighted. It also makes sure that a potential `Symbol.toStringTag` is taken into account. PR-URL: https://github.com/nodejs/node/pull/27351 Reviewed-By: Matteo Collina Reviewed-By: John-David Dalton Reviewed-By: Anto Aravinth --- lib/internal/util/inspect.js | 68 ++++++++++++++++++------------ test/parallel/test-util-inspect.js | 16 ++++++- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 4def103f42adeb..fccb46085d4ead 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -385,8 +385,6 @@ function getPrefix(constructor, tag, fallback) { return `${constructor} `; } -const getBoxedValue = formatPrimitive.bind(null, stylizeNoColor); - // Look up the keys of the object. function getKeys(value, showHidden) { let keys; @@ -709,31 +707,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { braces[0] = `[${tag}] {`; formatter = formatNamespaceObject; } else if (isBoxedPrimitive(value)) { - let type; - if (isNumberObject(value)) { - base = `[Number: ${getBoxedValue(NumberPrototype.valueOf(value))}]`; - type = 'number'; - } else if (isStringObject(value)) { - base = `[String: ${ - getBoxedValue(StringPrototype.valueOf(value), ctx) - }]`; - type = 'string'; - // For boxed Strings, we have to remove the 0-n indexed entries, - // since they just noisy up the output and are redundant - // Make boxed primitive Strings look like such - keys = keys.slice(value.length); - } else if (isBooleanObject(value)) { - base = `[Boolean: ${getBoxedValue(BooleanPrototype.valueOf(value))}]`; - type = 'boolean'; - } else if (isBigIntObject(value)) { - base = `[BigInt: ${getBoxedValue(BigIntPrototype.valueOf(value))}]`; - type = 'bigint'; - } else { - base = `[Symbol: ${getBoxedValue(SymbolPrototype.valueOf(value))}]`; - type = 'symbol'; - } + base = getBoxedBase(value, ctx, keys, constructor, tag); if (keys.length === 0) { - return ctx.stylize(base, type); + return base; } } else { // The input prototype got manipulated. Special handle these. We have to @@ -818,6 +794,46 @@ function getIteratorBraces(type, tag) { return [`[${tag}] {`, '}']; } +function getBoxedBase(value, ctx, keys, constructor, tag) { + let fn; + let type; + if (isNumberObject(value)) { + fn = NumberPrototype; + type = 'Number'; + } else if (isStringObject(value)) { + fn = StringPrototype; + type = 'String'; + // For boxed Strings, we have to remove the 0-n indexed entries, + // since they just noisy up the output and are redundant + // Make boxed primitive Strings look like such + keys.splice(0, value.length); + } else if (isBooleanObject(value)) { + fn = BooleanPrototype; + type = 'Boolean'; + } else if (isBigIntObject(value)) { + fn = BigIntPrototype; + type = 'BigInt'; + } else { + fn = SymbolPrototype; + type = 'Symbol'; + } + let base = `[${type}`; + if (type !== constructor) { + if (constructor === null) { + base += ' (null prototype)'; + } else { + base += ` (${constructor})`; + } + } + base += `: ${formatPrimitive(stylizeNoColor, fn.valueOf(value), ctx)}]`; + if (tag !== '' && tag !== constructor) { + base += ` [${tag}]`; + } + if (keys.length !== 0 || ctx.stylize === stylizeNoColor) + return base; + return ctx.stylize(base, type.toLowerCase()); +} + function formatError(err, constructor, tag, ctx) { // TODO(BridgeAR): Always show the error code if present. let stack = err.stack || ErrorPrototype.toString(err); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 82f773d62cfe9f..5ac270f5f84e9a 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -856,9 +856,21 @@ assert.strictEqual( '[Symbol: Symbol(test)]' ); assert.strictEqual(util.inspect(new Boolean(false)), '[Boolean: false]'); -assert.strictEqual(util.inspect(new Boolean(true)), '[Boolean: true]'); +assert.strictEqual( + util.inspect(Object.setPrototypeOf(new Boolean(true), null)), + '[Boolean (null prototype): true]' +); assert.strictEqual(util.inspect(new Number(0)), '[Number: 0]'); -assert.strictEqual(util.inspect(new Number(-0)), '[Number: -0]'); +assert.strictEqual( + util.inspect( + Object.defineProperty( + Object.setPrototypeOf(new Number(-0), Array.prototype), + Symbol.toStringTag, + { value: 'Foobar' } + ) + ), + '[Number (Array): -0] [Foobar]' +); assert.strictEqual(util.inspect(new Number(-1.1)), '[Number: -1.1]'); assert.strictEqual(util.inspect(new Number(13.37)), '[Number: 13.37]');