diff --git a/lib/util.js b/lib/util.js index 4eb7a4a0962bc6..97c5c10fd87375 100644 --- a/lib/util.js +++ b/lib/util.js @@ -284,10 +284,17 @@ function formatValue(ctx, value, recurseTimes) { formatted = formatPrimitiveNoColor(ctx, raw); return ctx.stylize('[Boolean: ' + formatted + ']', 'boolean'); } + // Fast path for ArrayBuffer. Can't do the same for DataView because it + // has a non-primitive .buffer property that we need to recurse for. + if (value instanceof ArrayBuffer) { + return `${getConstructorOf(value).name}` + + ` { byteLength: ${formatNumber(ctx, value.byteLength)} }`; + } } var constructor = getConstructorOf(value); - var base = '', empty = false, braces, formatter; + var base = '', empty = false, braces; + var formatter = formatObject; if (Array.isArray(value)) { // We can't use `constructor === Array` because this could @@ -314,6 +321,28 @@ function formatValue(ctx, value, recurseTimes) { keys.unshift('size'); empty = value.size === 0; formatter = formatMap; + } else if (value instanceof ArrayBuffer) { + braces = ['{', '}']; + keys.unshift('byteLength'); + visibleKeys.byteLength = true; + } else if (value instanceof DataView) { + braces = ['{', '}']; + // .buffer goes last, it's not a primitive like the others. + keys.unshift('byteLength', 'byteOffset', 'buffer'); + visibleKeys.byteLength = true; + visibleKeys.byteOffset = true; + visibleKeys.buffer = true; + } else if (isTypedArray(value)) { + braces = ['[', ']']; + formatter = formatTypedArray; + if (ctx.showHidden) { + // .buffer goes last, it's not a primitive like the others. + keys.unshift('BYTES_PER_ELEMENT', + 'length', + 'byteLength', + 'byteOffset', + 'buffer'); + } } else { // Only create a mirror if the object superficially looks like a Promise. var promiseInternals = value instanceof Promise && inspectPromise(value); @@ -336,7 +365,6 @@ function formatValue(ctx, value, recurseTimes) { constructor = null; braces = ['{', '}']; empty = true; // No other data than keys. - formatter = formatObject; } } } @@ -408,6 +436,15 @@ function formatValue(ctx, value, recurseTimes) { } +function formatNumber(ctx, value) { + // Format -0 as '-0'. Strict equality won't distinguish 0 from -0, + // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 . + if (value === 0 && 1 / value < 0) + return ctx.stylize('-0', 'number'); + return ctx.stylize('' + value, 'number'); +} + + function formatPrimitive(ctx, value) { if (value === undefined) return ctx.stylize('undefined', 'undefined'); @@ -424,13 +461,8 @@ function formatPrimitive(ctx, value) { .replace(/\\"/g, '"') + '\''; return ctx.stylize(simple, 'string'); } - if (type === 'number') { - // Format -0 as '-0'. Strict equality won't distinguish 0 from -0, - // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 . - if (value === 0 && 1 / value < 0) - return ctx.stylize('-0', 'number'); - return ctx.stylize('' + value, 'number'); - } + if (type === 'number') + return formatNumber(ctx, value); if (type === 'boolean') return ctx.stylize('' + value, 'boolean'); // es6 symbol primitive @@ -480,6 +512,20 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { } +function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = new Array(value.length); + for (var i = 0, l = value.length; i < l; ++i) + output[i] = formatNumber(ctx, value[i]); + for (const key of keys) { + if (typeof key === 'symbol' || !key.match(/^\d+$/)) { + output.push( + formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); + } + } + return output; +} + + function formatSet(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; value.forEach(function(v) { @@ -626,6 +672,19 @@ function reduceToSingleString(output, base, braces) { } +function isTypedArray(value) { + return value instanceof Float32Array || + value instanceof Float64Array || + value instanceof Int16Array || + value instanceof Int32Array || + value instanceof Int8Array || + value instanceof Uint16Array || + value instanceof Uint32Array || + value instanceof Uint8Array || + value instanceof Uint8ClampedArray; +} + + // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. exports.isArray = Array.isArray; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 320d5e444ad80a..563fcc46d88c46 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -41,6 +41,59 @@ assert.equal(util.inspect(Object.create({}, '{ visible: 1 }' ); +for (const showHidden of [true, false]) { + const ab = new ArrayBuffer(4); + const dv = new DataView(ab, 1, 2); + assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }'); + assert.equal(util.inspect(new DataView(ab, 1, 2), showHidden), + 'DataView {\n' + + ' byteLength: 2,\n' + + ' byteOffset: 1,\n' + + ' buffer: ArrayBuffer { byteLength: 4 } }'); + assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }'); + assert.equal(util.inspect(dv, showHidden), + 'DataView {\n' + + ' byteLength: 2,\n' + + ' byteOffset: 1,\n' + + ' buffer: ArrayBuffer { byteLength: 4 } }'); + ab.x = 42; + dv.y = 1337; + assert.equal(util.inspect(ab, showHidden), + 'ArrayBuffer { byteLength: 4, x: 42 }'); + assert.equal(util.inspect(dv, showHidden), + 'DataView {\n' + + ' byteLength: 2,\n' + + ' byteOffset: 1,\n' + + ' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' + + ' y: 1337 }'); +} + +[ Float32Array, + Float64Array, + Int16Array, + Int32Array, + Int8Array, + Uint16Array, + Uint32Array, + Uint8Array, + Uint8ClampedArray ].forEach(constructor => { + const length = 2; + const byteLength = length * constructor.BYTES_PER_ELEMENT; + const array = new constructor(new ArrayBuffer(byteLength), 0, length); + array[0] = 65; + array[1] = 97; + assert.equal(util.inspect(array, true), + `${constructor.name} [\n` + + ` 65,\n` + + ` 97,\n` + + ` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` + + ` [length]: ${length},\n` + + ` [byteLength]: ${byteLength},\n` + + ` [byteOffset]: 0,\n` + + ` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`); + assert.equal(util.inspect(array, false), `${constructor.name} [ 65, 97 ]`); + }); + // Due to the hash seed randomization it's not deterministic the order that // the following ways this hash is displayed. // See http://codereview.chromium.org/9124004/