From e9ac683efcfb006f64bbb229b2fda59194172139 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 13 Aug 2018 01:38:59 +0200 Subject: [PATCH] util: properly indent special properties Calling `formatValue()` directly requires the indentation level to be set manually. This was not the case so far in most cases and the indentation was off in all these cases. PR-URL: https://github.com/nodejs/node/pull/22291 Reviewed-By: Gus Caplan Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- lib/util.js | 37 +++++++--- test/parallel/test-util-inspect.js | 104 ++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 11 deletions(-) diff --git a/lib/util.js b/lib/util.js index e45d7de617e808..7803d327c2ed3e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -492,6 +492,9 @@ function findTypedConstructor(value) { const getBoxedValue = formatPrimitive.bind(null, stylizeNoColor); +// Note: using `formatValue` directly requires the indentation level to be +// corrected by setting `ctx.indentationLvL += diff` and then to decrease the +// value afterwards again. function formatValue(ctx, value, recurseTimes) { // Primitive types cannot have properties if (typeof value !== 'object' && typeof value !== 'function') { @@ -1011,17 +1014,18 @@ function formatTypedArray(ctx, value, recurseTimes, keys) { output[i] = `... ${remaining} more item${remaining > 1 ? 's' : ''}`; if (ctx.showHidden) { // .buffer goes last, it's not a primitive like the others. - const extraKeys = [ + ctx.indentationLvl += 2; + for (const key of [ 'BYTES_PER_ELEMENT', 'length', 'byteLength', 'byteOffset', 'buffer' - ]; - for (i = 0; i < extraKeys.length; i++) { - const str = formatValue(ctx, value[extraKeys[i]], recurseTimes); - output.push(`[${extraKeys[i]}]: ${str}`); + ]) { + const str = formatValue(ctx, value[key], recurseTimes); + output.push(`[${key}]: ${str}`); } + ctx.indentationLvl -= 2; } // TypedArrays cannot have holes. Therefore it is safe to assume that all // extra keys are indexed after value.length. @@ -1034,8 +1038,11 @@ function formatTypedArray(ctx, value, recurseTimes, keys) { function formatSet(ctx, value, recurseTimes, keys) { const output = new Array(value.size + keys.length + (ctx.showHidden ? 1 : 0)); let i = 0; - for (const v of value) + ctx.indentationLvl += 2; + for (const v of value) { output[i++] = formatValue(ctx, v, recurseTimes); + } + ctx.indentationLvl -= 2; // With `showHidden`, `length` will display as a hidden property for // arrays. For consistency's sake, do the same for `size`, even though this // property isn't selected by Object.getOwnPropertyNames(). @@ -1050,9 +1057,12 @@ function formatSet(ctx, value, recurseTimes, keys) { function formatMap(ctx, value, recurseTimes, keys) { const output = new Array(value.size + keys.length + (ctx.showHidden ? 1 : 0)); let i = 0; - for (const [k, v] of value) + ctx.indentationLvl += 2; + for (const [k, v] of value) { output[i++] = `${formatValue(ctx, k, recurseTimes)} => ` + - formatValue(ctx, v, recurseTimes); + formatValue(ctx, v, recurseTimes); + } + ctx.indentationLvl -= 2; // See comment in formatSet if (ctx.showHidden) output[i++] = `[size]: ${ctx.stylize(`${value.size}`, 'number')}`; @@ -1066,8 +1076,11 @@ function formatSetIterInner(ctx, value, recurseTimes, keys, entries, state) { const maxArrayLength = Math.max(ctx.maxArrayLength, 0); const maxLength = Math.min(maxArrayLength, entries.length); let output = new Array(maxLength); - for (var i = 0; i < maxLength; ++i) + ctx.indentationLvl += 2; + for (var i = 0; i < maxLength; i++) { output[i] = formatValue(ctx, entries[i], recurseTimes); + } + ctx.indentationLvl -= 2; if (state === kWeak) { // Sort all entries to have a halfway reliable output (if more entries than // retrieved ones exist, we can not reliably return the same output). @@ -1098,11 +1111,13 @@ function formatMapIterInner(ctx, value, recurseTimes, keys, entries, state) { end = ' ]'; middle = ', '; } + ctx.indentationLvl += 2; for (; i < maxLength; i++) { const pos = i * 2; output[i] = `${start}${formatValue(ctx, entries[pos], recurseTimes)}` + `${middle}${formatValue(ctx, entries[pos + 1], recurseTimes)}${end}`; } + ctx.indentationLvl -= 2; if (state === kWeak) { // Sort all entries to have a halfway reliable output (if more entries // than retrieved ones exist, we can not reliably return the same output). @@ -1147,7 +1162,11 @@ function formatPromise(ctx, value, recurseTimes, keys) { if (state === kPending) { output = ['']; } else { + // Using `formatValue` is correct here without the need to fix the + // indentation level. + ctx.indentationLvl += 2; const str = formatValue(ctx, result, recurseTimes); + ctx.indentationLvl -= 2; output = [state === kRejected ? ` ${str}` : str]; } for (var n = 0; n < keys.length; n++) { diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 7c43e5ee10fedd..6f3f5213935ae1 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -179,7 +179,6 @@ for (const showHidden of [true, false]) { ' y: 1337 }'); } - [ Float32Array, Float64Array, Int16Array, @@ -195,7 +194,7 @@ for (const showHidden of [true, false]) { array[0] = 65; array[1] = 97; assert.strictEqual( - util.inspect(array, true), + util.inspect(array, { showHidden: true }), `${constructor.name} [\n` + ' 65,\n' + ' 97,\n' + @@ -1424,6 +1423,107 @@ util.inspect(process); assert.strictEqual(out, expect); } +// Check compact indentation. +{ + const typed = new Uint8Array(); + typed.buffer.foo = true; + const set = new Set([[1, 2]]); + const promise = Promise.resolve([[1, set]]); + const map = new Map([[promise, typed]]); + map.set(set.values(), map.values()); + + let out = util.inspect(map, { compact: false, showHidden: true, depth: 9 }); + let expected = [ + 'Map {', + ' Promise {', + ' [', + ' [', + ' 1,', + ' Set {', + ' [', + ' 1,', + ' 2,', + ' [length]: 2', + ' ],', + ' [size]: 1', + ' },', + ' [length]: 2', + ' ],', + ' [length]: 1', + ' ]', + ' } => Uint8Array [', + ' [BYTES_PER_ELEMENT]: 1,', + ' [length]: 0,', + ' [byteLength]: 0,', + ' [byteOffset]: 0,', + ' [buffer]: ArrayBuffer {', + ' byteLength: 0,', + ' foo: true', + ' }', + ' ],', + ' [Set Iterator] {', + ' [', + ' 1,', + ' 2,', + ' [length]: 2', + ' ]', + ' } => [Map Iterator] {', + ' Uint8Array [', + ' [BYTES_PER_ELEMENT]: 1,', + ' [length]: 0,', + ' [byteLength]: 0,', + ' [byteOffset]: 0,', + ' [buffer]: ArrayBuffer {', + ' byteLength: 0,', + ' foo: true', + ' }', + ' ],', + ' [Circular]', + ' },', + ' [size]: 2', + '}' + ].join('\n'); + + assert.strict.equal(out, expected); + + out = util.inspect(map, { showHidden: true, depth: 9, breakLength: 4 }); + expected = [ + 'Map {', + ' Promise {', + ' [ [ 1,', + ' Set {', + ' [ 1,', + ' 2,', + ' [length]: 2 ],', + ' [size]: 1 },', + ' [length]: 2 ],', + ' [length]: 1 ] } => Uint8Array [', + ' [BYTES_PER_ELEMENT]: 1,', + ' [length]: 0,', + ' [byteLength]: 0,', + ' [byteOffset]: 0,', + ' [buffer]: ArrayBuffer {', + ' byteLength: 0,', + ' foo: true } ],', + ' [Set Iterator] {', + ' [ 1,', + ' 2,', + ' [length]: 2 ] } => [Map Iterator] {', + ' Uint8Array [', + ' [BYTES_PER_ELEMENT]: 1,', + ' [length]: 0,', + ' [byteLength]: 0,', + ' [byteOffset]: 0,', + ' [buffer]: ArrayBuffer {', + ' byteLength: 0,', + ' foo: true } ],', + ' [Circular] },', + ' [size]: 2 }' + ].join('\n'); + + assert.strict.equal(out, expected); +} + { // Test WeakMap const obj = {}; const arr = [];