From 45a943be727784238da12d3fe0e35c6a4d527f91 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 2 Nov 2019 18:50:09 +0100 Subject: [PATCH 1/2] 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. --- lib/internal/util/inspect.js | 159 ++++++++++++----------------- test/parallel/test-util-inspect.js | 13 +++ 2 files changed, 80 insertions(+), 92 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 9f5cc8a45e84b0..ecd003449eab91 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) { diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index b8de4c9b86c23d..e8f8c3132f6fe0 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -2220,6 +2220,19 @@ assert.strictEqual( configurable: true }); assert.strictEqual(util.inspect(obj), '[Set: null prototype] { 1, 2 }'); + Object.defineProperty(obj, Symbol.iterator, { + value: true, + configurable: true + }); + Object.defineProperty(obj, 'size', { + value: NaN, + configurable: true, + enumerable: true + }); + assert.strictEqual( + util.inspect(obj), + '[Set: null prototype] { 1, 2, size: NaN }' + ); } // Check the getter option. From 96fb0b9bb3f3ca08ccd6dcfe637e8bbcba4c8113 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 2 Nov 2019 19:13:51 +0100 Subject: [PATCH 2/2] util: add Set and map size to inspect output This adds the size of a set and map to the output. This aligns the output with the one from Chromium. --- doc/api/util.md | 8 +-- lib/internal/util/inspect.js | 16 ++---- test/parallel/test-assert-deep.js | 2 +- test/parallel/test-util-inspect.js | 91 +++++++++++++++--------------- 4 files changed, 54 insertions(+), 63 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index f4e689a2130bd5..4727cdb88e9678 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -576,7 +576,7 @@ console.log(util.inspect(o, { compact: true, depth: 5, breakLength: 80 })); // 'test', // 'foo' ] ], // 4 ], -// b: Map { 'za' => 1, 'zb' => 'test' } } +// b: Map(2) { 'za' => 1, 'zb' => 'test' } } // Setting `compact` to false changes the output to be more reader friendly. console.log(util.inspect(o, { compact: false, depth: 5, breakLength: 80 })); @@ -597,7 +597,7 @@ console.log(util.inspect(o, { compact: false, depth: 5, breakLength: 80 })); // ], // 4 // ], -// b: Map { +// b: Map(2) { // 'za' => 1, // 'zb' => 'test' // } @@ -639,9 +639,9 @@ const o1 = { c: new Set([2, 3, 1]) }; console.log(inspect(o1, { sorted: true })); -// { a: '`a` comes before `b`', b: [ 2, 3, 1 ], c: Set { 1, 2, 3 } } +// { a: '`a` comes before `b`', b: [ 2, 3, 1 ], c: Set(3) { 1, 2, 3 } } console.log(inspect(o1, { sorted: (a, b) => b.localeCompare(a) })); -// { c: Set { 3, 2, 1 }, b: [ 2, 3, 1 ], a: '`a` comes before `b`' } +// { c: Set(3) { 3, 2, 1 }, b: [ 2, 3, 1 ], a: '`a` comes before `b`' } const o2 = { c: new Set([2, 1, 3]), diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index ecd003449eab91..701cc34cebff15 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -778,10 +778,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { if (constructor !== null) { if (constructor === tag) tag = ''; - prefix = getPrefix(`${constructor}`, tag, ''); + prefix = getPrefix(`${constructor}(${size})`, tag, ''); formatter = formatSet.bind(null, value, size); } else { - prefix = getPrefix(constructor, tag, 'Set'); + prefix = getPrefix(constructor, tag, `Set(${size})`); formatter = formatSet.bind(null, SetPrototypeValues(value), size); } if (size === 0 && keys.length === 0 && protoProps === undefined) @@ -794,10 +794,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { if (constructor !== null) { if (constructor === tag) tag = ''; - prefix = getPrefix(`${constructor}`, tag, ''); + prefix = getPrefix(`${constructor}(${size})`, tag, ''); formatter = formatMap.bind(null, value, size); } else { - prefix = getPrefix(constructor, tag, 'Map'); + prefix = getPrefix(constructor, tag, `Map(${size})`); formatter = formatMap.bind(null, MapPrototypeEntries(value), size); } if (size === 0 && keys.length === 0 && protoProps === undefined) @@ -1437,11 +1437,6 @@ function formatSet(value, size, ctx, ignored, recurseTimes) { output.push(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 ObjectGetOwnPropertyNames(). - if (ctx.showHidden) - output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); return output; } @@ -1453,9 +1448,6 @@ function formatMap(value, size, ctx, ignored, recurseTimes) { formatValue(ctx, v, recurseTimes)); } ctx.indentationLvl -= 2; - // See comment in formatSet - if (ctx.showHidden) - output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); return output; } diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index e9752184480a35..fc2f96741ca7ef 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -524,7 +524,7 @@ assertNotDeepOrStrict( { code: 'ERR_ASSERTION', message: `${defaultMsgStartFull}\n\n` + - " Map {\n+ 1 => 1\n- 1 => '1'\n }" + " Map(1) {\n+ 1 => 1\n- 1 => '1'\n }" } ); } diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index e8f8c3132f6fe0..3b09e0c0fa20c2 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -577,9 +577,9 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); let obj = vm.runInNewContext('(function(){return {}})()', {}); assert.strictEqual(util.inspect(obj), '{}'); obj = vm.runInNewContext('var m=new Map();m.set(1,2);m', {}); - assert.strictEqual(util.inspect(obj), 'Map { 1 => 2 }'); + assert.strictEqual(util.inspect(obj), 'Map(1) { 1 => 2 }'); obj = vm.runInNewContext('var s=new Set();s.add(1);s.add(2);s', {}); - assert.strictEqual(util.inspect(obj), 'Set { 1, 2 }'); + assert.strictEqual(util.inspect(obj), 'Set(2) { 1, 2 }'); obj = vm.runInNewContext('fn=function(){};new Promise(fn,fn)', {}); assert.strictEqual(util.inspect(obj), 'Promise { }'); } @@ -1068,13 +1068,13 @@ if (typeof Symbol !== 'undefined') { // Test Set. { - assert.strictEqual(util.inspect(new Set()), 'Set {}'); - assert.strictEqual(util.inspect(new Set([1, 2, 3])), 'Set { 1, 2, 3 }'); + assert.strictEqual(util.inspect(new Set()), 'Set(0) {}'); + assert.strictEqual(util.inspect(new Set([1, 2, 3])), 'Set(3) { 1, 2, 3 }'); const set = new Set(['foo']); set.bar = 42; assert.strictEqual( util.inspect(set, { showHidden: true }), - "Set { 'foo', [size]: 1, bar: 42 }" + "Set(1) { 'foo', bar: 42 }" ); } @@ -1082,33 +1082,39 @@ if (typeof Symbol !== 'undefined') { { const set = new Set(); set.add(set); - assert.strictEqual(util.inspect(set), ' Set { [Circular *1] }'); + assert.strictEqual(util.inspect(set), ' Set(1) { [Circular *1] }'); } // Test Map. { - assert.strictEqual(util.inspect(new Map()), 'Map {}'); + assert.strictEqual(util.inspect(new Map()), 'Map(0) {}'); assert.strictEqual(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])), - "Map { 1 => 'a', 2 => 'b', 3 => 'c' }"); + "Map(3) { 1 => 'a', 2 => 'b', 3 => 'c' }"); const map = new Map([['foo', null]]); map.bar = 42; assert.strictEqual(util.inspect(map, true), - "Map { 'foo' => null, [size]: 1, bar: 42 }"); + "Map(1) { 'foo' => null, bar: 42 }"); } // Test circular Map. { const map = new Map(); map.set(map, 'map'); - assert.strictEqual(inspect(map), " Map { [Circular *1] => 'map' }"); + assert.strictEqual( + inspect(map), + " Map(1) { [Circular *1] => 'map' }" + ); map.set(map, map); assert.strictEqual( inspect(map), - ' Map { [Circular *1] => [Circular *1] }' + ' Map(1) { [Circular *1] => [Circular *1] }' ); map.delete(map); map.set('map', map); - assert.strictEqual(inspect(map), " Map { 'map' => [Circular *1] }"); + assert.strictEqual( + inspect(map), + " Map(1) { 'map' => [Circular *1] }" + ); } // Test multiple circular references. @@ -1274,10 +1280,10 @@ if (typeof Symbol !== 'undefined') { }); checkAlignment(obj, '{', " 'X': null", '}'); - checkAlignment(new Set(bigArray), 'Set {', ' X', '}'); + checkAlignment(new Set(bigArray), 'Set(100) {', ' X', '}'); checkAlignment( new Map(bigArray.map((number) => [number, null])), - 'Map {', ' X => null', '}' + 'Map(100) {', ' X => null', '}' ); } @@ -1297,9 +1303,9 @@ if (typeof Symbol !== 'undefined') { assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)), 'ArraySubclass [ 1, 2, 3 ]'); assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])), - 'SetSubclass [Set] { 1, 2, 3 }'); + 'SetSubclass(3) [Set] { 1, 2, 3 }'); assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])), - "MapSubclass [Map] { 'foo' => 42 }"); + "MapSubclass(1) [Map] { 'foo' => 42 }"); assert.strictEqual(util.inspect(new PromiseSubclass(() => {})), 'PromiseSubclass [Promise] { }'); assert.strictEqual( @@ -1558,7 +1564,7 @@ util.inspect(process); " 'test',", " 'foo' ] ],", ' 4 ],', - " b: Map { 'za' => 1, 'zb' => 'test' } }", + " b: Map(2) { 'za' => 1, 'zb' => 'test' } }", ].join('\n'); assert.strictEqual(out, expect); @@ -1579,7 +1585,7 @@ util.inspect(process); ' ],', ' 4', ' ],', - ' b: Map {', + ' b: Map(2) {', " 'za' => 1,", " 'zb' => 'test'", ' }', @@ -1659,18 +1665,17 @@ util.inspect(process); let out = util.inspect(map, { compact: false, showHidden: true, depth: 9 }); let expected = [ - 'Map {', + 'Map(2) {', ' Promise {', ' [', ' [', ' 1,', - ' Set {', + ' Set(1) {', ' [', ' 1,', ' 2,', ' [length]: 2', - ' ],', - ' [size]: 1', + ' ]', ' },', ' [length]: 2', ' ],', @@ -1704,8 +1709,7 @@ util.inspect(process); ' }', ' ],', ' [Circular *1]', - ' },', - ' [size]: 2', + ' }', '}' ].join('\n'); @@ -1714,12 +1718,12 @@ util.inspect(process); out = util.inspect(map, { compact: 2, showHidden: true, depth: 9 }); expected = [ - 'Map {', + 'Map(2) {', ' Promise {', ' [', ' [', ' 1,', - ' Set { [ 1, 2, [length]: 2 ], [size]: 1 },', + ' Set(1) { [ 1, 2, [length]: 2 ] },', ' [length]: 2', ' ],', ' [length]: 1', @@ -1740,8 +1744,7 @@ util.inspect(process); ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', ' ],', ' [Circular *1]', - ' },', - ' [size]: 2', + ' }', '}' ].join('\n'); @@ -1751,14 +1754,13 @@ util.inspect(process); showHidden: true, depth: 9, breakLength: 4, compact: true }); expected = [ - 'Map {', + 'Map(2) {', ' Promise {', ' [ [ 1,', - ' Set {', + ' Set(1) {', ' [ 1,', ' 2,', - ' [length]: 2 ],', - ' [size]: 1 },', + ' [length]: 2 ] },', ' [length]: 2 ],', ' [length]: 1 ] } => Uint8Array [', ' [BYTES_PER_ELEMENT]: 1,', @@ -1780,8 +1782,7 @@ util.inspect(process); ' [buffer]: ArrayBuffer {', ' byteLength: 0,', ' foo: true } ],', - ' [Circular *1] },', - ' [size]: 2 }' + ' [Circular *1] } }' ].join('\n'); assert.strict.equal(out, expected); @@ -1945,8 +1946,8 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [[1, 2], '[ 1, 2 ]'], [[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'], [{ a: 5 }, '{ a: 5 }'], - [new Set([1, 2]), 'Set { 1, 2 }'], - [new Map([[1, 2]]), 'Map { 1 => 2 }'], + [new Set([1, 2]), 'Set(2) { 1, 2 }'], + [new Map([[1, 2]]), 'Map(1) { 1 => 2 }'], [new Set([1, 2]).entries(), '[Set Entries] { [ 1, 1 ], [ 2, 2 ] }'], [new Map([[1, 2]]).keys(), '[Map Iterator] { 1 }'], [new Date(2000), '1970-01-01T00:00:02.000Z'], @@ -1977,8 +1978,8 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); // Verify that having no prototype still produces nice results. [ [[1, 3, 4], '[Array: null prototype] [ 1, 3, 4 ]'], - [new Set([1, 2]), '[Set: null prototype] { 1, 2 }'], - [new Map([[1, 2]]), '[Map: null prototype] { 1 => 2 }'], + [new Set([1, 2]), '[Set(2): null prototype] { 1, 2 }'], + [new Map([[1, 2]]), '[Map(1): null prototype] { 1 => 2 }'], [new Promise((resolve) => setTimeout(resolve, 10)), '[Promise: null prototype] { }'], [new WeakSet(), '[WeakSet: null prototype] { }'], @@ -2219,7 +2220,7 @@ assert.strictEqual( value: iterator, configurable: true }); - assert.strictEqual(util.inspect(obj), '[Set: null prototype] { 1, 2 }'); + assert.strictEqual(util.inspect(obj), '[Set(2): null prototype] { 1, 2 }'); Object.defineProperty(obj, Symbol.iterator, { value: true, configurable: true @@ -2231,7 +2232,7 @@ assert.strictEqual( }); assert.strictEqual( util.inspect(obj), - '[Set: null prototype] { 1, 2, size: NaN }' + '[Set(2): null prototype] { 1, 2, size: NaN }' ); } @@ -2264,7 +2265,7 @@ assert.strictEqual( getset.foo = new Set([[{ a: true }, 2, {}], 'foobar', { x: 1 }]); assert.strictEqual( inspect(getset, { getters: true }), - '{\n foo: [Getter/Setter] Set { [ [Object], 2, {} ], ' + + '{\n foo: [Getter/Setter] Set(3) { [ [Object], 2, {} ], ' + "'foobar', { x: 1 } },\n inc: [Getter: NaN]\n}"); } @@ -2655,12 +2656,11 @@ assert.strictEqual( assert.strictEqual( inspect(bar), - 'Bar [Map] { prop: true, prop2: true, abc: true }' + 'Bar(0) [Map] { prop: true, prop2: true, abc: true }' ); assert.strictEqual( inspect(bar, { showHidden: true, getters: true, colors: false }), - 'Bar [Map] {\n' + - ' [size]: 0,\n' + + 'Bar(0) [Map] {\n' + ' prop: true,\n' + ' prop2: true,\n' + ' abc: true,\n' + @@ -2670,8 +2670,7 @@ assert.strictEqual( ); assert.strictEqual( inspect(bar, { showHidden: true, getters: false, colors: true }), - 'Bar [Map] {\n' + - ' [size]: \x1B[33m0\x1B[39m,\n' + + 'Bar(0) [Map] {\n' + ' prop: \x1B[33mtrue\x1B[39m,\n' + ' prop2: \x1B[33mtrue\x1B[39m,\n' + ' abc: \x1B[33mtrue\x1B[39m,\n' +