diff --git a/lib/util.js b/lib/util.js index ee803afa50..500c1d1751 100644 --- a/lib/util.js +++ b/lib/util.js @@ -239,6 +239,28 @@ function formatValue(ctx, value, recurseTimes) { keys = Object.getOwnPropertyNames(value); } + // This could be a boxed primitive (new String(), etc.), check valueOf() + // NOTE: Avoid calling `valueOf` on `Date` instance because it will return + // a number which, when object has some additional user-stored `keys`, + // will be printed out. + var formatted; + var raw = value; + try { + // the .valueOf() call can fail for a multitude of reasons + if (!isDate(value)) + raw = value.valueOf(); + } catch (e) { + // ignore... + } + + if (isString(raw)) { + // for boxed Strings, we have to remove the 0-n indexed entries, + // since they just noisey up the output and are redundant + keys = keys.filter(function(key) { + return !(key >= 0 && key < raw.length); + }); + } + // Some type of object without properties can be shortcutted. if (keys.length === 0) { if (isFunction(value)) { @@ -254,6 +276,19 @@ function formatValue(ctx, value, recurseTimes) { if (isError(value)) { return formatError(value); } + // now check the `raw` value to handle boxed primitives + if (isString(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + return ctx.stylize('[String: ' + formatted + ']', 'string'); + } + if (isNumber(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + return ctx.stylize('[Number: ' + formatted + ']', 'number'); + } + if (isBoolean(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + return ctx.stylize('[Boolean: ' + formatted + ']', 'boolean'); + } } var base = '', array = false, braces = ['{', '}']; @@ -285,6 +320,24 @@ function formatValue(ctx, value, recurseTimes) { base = ' ' + formatError(value); } + // Make boxed primitive Strings look like such + if (isString(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + base = ' ' + '[String: ' + formatted + ']'; + } + + // Make boxed primitive Numbers look like such + if (isNumber(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + base = ' ' + '[Number: ' + formatted + ']'; + } + + // Make boxed primitive Booleans look like such + if (isBoolean(raw)) { + formatted = formatPrimitiveNoColor(ctx, raw); + base = ' ' + '[Boolean: ' + formatted + ']'; + } + if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } @@ -338,6 +391,15 @@ function formatPrimitive(ctx, value) { } +function formatPrimitiveNoColor(ctx, value) { + var stylize = ctx.stylize; + ctx.stylize = stylizeNoColor; + var str = formatPrimitive(ctx, value); + ctx.stylize = stylize; + return str; +} + + function formatError(value) { return '[' + Error.prototype.toString.call(value) + ']'; } diff --git a/test/simple/test-util-inspect.js b/test/simple/test-util-inspect.js index 474410e803..8651a2df01 100644 --- a/test/simple/test-util-inspect.js +++ b/test/simple/test-util-inspect.js @@ -213,3 +213,25 @@ test_lines({ very_long_key: 'very_long_value', even_longer_key: ['with even longer value in array'] }); + +// test boxed primitives output the correct values +assert.equal(util.inspect(new String('test')), '[String: \'test\']'); +assert.equal(util.inspect(new Boolean(false)), '[Boolean: false]'); +assert.equal(util.inspect(new Boolean(true)), '[Boolean: true]'); +assert.equal(util.inspect(new Number(0)), '[Number: 0]'); +assert.equal(util.inspect(new Number(-0)), '[Number: -0]'); +assert.equal(util.inspect(new Number(-1.1)), '[Number: -1.1]'); +assert.equal(util.inspect(new Number(13.37)), '[Number: 13.37]'); + +// test boxed primitives with own properties +var str = new String('baz'); +str.foo = 'bar'; +assert.equal(util.inspect(str), '{ [String: \'baz\'] foo: \'bar\' }'); + +var bool = new Boolean(true); +bool.foo = 'bar'; +assert.equal(util.inspect(bool), '{ [Boolean: true] foo: \'bar\' }'); + +var num = new Number(13.37); +num.foo = 'bar'; +assert.equal(util.inspect(num), '{ [Number: 13.37] foo: \'bar\' }');