Skip to content

Commit a4d876f

Browse files
committed
util: mark special properties when inspecting them
This makes sure special properties (such as a byteLength, buffer, and more) are marked that they are not regular properties. They are mostly getters, that just seemed even more of a breaking change. Thus, they just use square brackets for now. On top of that, it makes inspecting detached DataViews robust. Inspecting those failed so far. Closes: #56669
1 parent 0c1fb98 commit a4d876f

File tree

2 files changed

+103
-77
lines changed

2 files changed

+103
-77
lines changed

lib/internal/util/inspect.js

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
12231223
const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE;
12241224

12251225
let extrasType = kObjectType;
1226+
let extraKeys;
12261227

12271228
// Iterators and the rest are split to reduce checks.
12281229
// We have to check all values in case the constructor is set to null.
@@ -1278,6 +1279,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
12781279
// bound function is required to reconstruct missing information.
12791280
formatter = FunctionPrototypeBind(formatTypedArray, null, bound, size);
12801281
extrasType = kArrayExtrasType;
1282+
1283+
if (ctx.showHidden) {
1284+
extraKeys = ['BYTES_PER_ELEMENT', 'length', 'byteLength', 'byteOffset', 'buffer'];
1285+
typedArray = true;
1286+
}
12811287
} else if (isMapIterator(value)) {
12821288
keys = getKeys(value, ctx.showHidden);
12831289
braces = getIteratorBraces('Map', tag);
@@ -1347,14 +1353,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
13471353
formatter = formatArrayBuffer;
13481354
} else if (keys.length === 0 && protoProps === undefined) {
13491355
return prefix +
1350-
`{ byteLength: ${formatNumber(ctx.stylize, value.byteLength, false)} }`;
1356+
`{ [byteLength]: ${formatNumber(ctx.stylize, value.byteLength, false)} }`;
13511357
}
13521358
braces[0] = `${prefix}{`;
1353-
ArrayPrototypeUnshift(keys, 'byteLength');
1359+
extraKeys = ['byteLength'];
13541360
} else if (isDataView(value)) {
13551361
braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`;
13561362
// .buffer goes last, it's not a primitive like the others.
1357-
ArrayPrototypeUnshift(keys, 'byteLength', 'byteOffset', 'buffer');
1363+
extraKeys = ['byteLength', 'byteOffset', 'buffer'];
13581364
} else if (isPromise(value)) {
13591365
braces[0] = `${getPrefix(constructor, tag, 'Promise')}{`;
13601366
formatter = formatPromise;
@@ -1404,6 +1410,18 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
14041410
const indentationLvl = ctx.indentationLvl;
14051411
try {
14061412
output = formatter(ctx, value, recurseTimes);
1413+
if (extraKeys !== undefined) {
1414+
for (i = 0; i < extraKeys.length; i++) {
1415+
let formatted;
1416+
try {
1417+
formatted = formatExtraProperties(ctx, value, recurseTimes, extraKeys[i], typedArray);
1418+
} catch {
1419+
const tempValue = { [extraKeys[i]]: value.buffer[extraKeys[i]] };
1420+
formatted = formatExtraProperties(ctx, tempValue, recurseTimes, extraKeys[i], typedArray);
1421+
}
1422+
ArrayPrototypePush(output, formatted);
1423+
}
1424+
}
14071425
for (i = 0; i < keys.length; i++) {
14081426
ArrayPrototypePush(
14091427
output,
@@ -2263,11 +2281,15 @@ function formatArrayBuffer(ctx, value) {
22632281
}
22642282
if (hexSlice === undefined)
22652283
hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice);
2266-
let str = StringPrototypeTrim(RegExpPrototypeSymbolReplace(
2267-
/(.{2})/g,
2268-
hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)),
2269-
'$1 ',
2270-
));
2284+
let rawString = hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length));
2285+
let str = '';
2286+
let i = 0;
2287+
for (; i < rawString.length - 2; i += 2) {
2288+
str += `${rawString[i]}${rawString[i + 1]} `;
2289+
}
2290+
if (rawString.length > 0) {
2291+
str += `${rawString[i]}${rawString[i + 1]}`;
2292+
}
22712293
const remaining = buffer.length - ctx.maxArrayLength;
22722294
if (remaining > 0)
22732295
str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
@@ -2294,7 +2316,7 @@ function formatArray(ctx, value, recurseTimes) {
22942316
return output;
22952317
}
22962318

2297-
function formatTypedArray(value, length, ctx, ignored, recurseTimes) {
2319+
function formatTypedArray(value, length, ctx) {
22982320
const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), length);
22992321
const remaining = value.length - maxLength;
23002322
const output = new Array(maxLength);
@@ -2307,22 +2329,6 @@ function formatTypedArray(value, length, ctx, ignored, recurseTimes) {
23072329
if (remaining > 0) {
23082330
output[maxLength] = remainingText(remaining);
23092331
}
2310-
if (ctx.showHidden) {
2311-
// .buffer goes last, it's not a primitive like the others.
2312-
// All besides `BYTES_PER_ELEMENT` are actually getters.
2313-
ctx.indentationLvl += 2;
2314-
for (const key of [
2315-
'BYTES_PER_ELEMENT',
2316-
'length',
2317-
'byteLength',
2318-
'byteOffset',
2319-
'buffer',
2320-
]) {
2321-
const str = formatValue(ctx, value[key], recurseTimes, true);
2322-
ArrayPrototypePush(output, `[${key}]: ${str}`);
2323-
}
2324-
ctx.indentationLvl -= 2;
2325-
}
23262332
return output;
23272333
}
23282334

@@ -2470,12 +2476,21 @@ function formatPromise(ctx, value, recurseTimes) {
24702476
return output;
24712477
}
24722478

2479+
function formatExtraProperties(ctx, value, recurseTimes, key, typedArray) {
2480+
let extra = ' ';
2481+
ctx.indentationLvl += 2;
2482+
const str = formatValue(ctx, value[key], recurseTimes, typedArray);
2483+
ctx.indentationLvl -= 2;
2484+
2485+
// These entries are mainly getters. Should they be formatted like getters?
2486+
return ctx.stylize(`[${key}]: ${str}`, 'string');
2487+
}
2488+
24732489
function formatProperty(ctx, value, recurseTimes, key, type, desc,
24742490
original = value) {
24752491
let name, str;
24762492
let extra = ' ';
2477-
desc ||= ObjectGetOwnPropertyDescriptor(value, key) ||
2478-
{ value: value[key], enumerable: true };
2493+
desc ??= ObjectGetOwnPropertyDescriptor(value, key);
24792494
if (desc.value !== undefined) {
24802495
const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3;
24812496
ctx.indentationLvl += diff;

test/parallel/test-util-inspect.js

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -172,56 +172,67 @@ assert.doesNotMatch(
172172
const dv = new DataView(ab, 1, 2);
173173
assert.strictEqual(
174174
util.inspect(ab, showHidden),
175-
'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }'
175+
'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }'
176176
);
177177
assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
178178
'DataView {\n' +
179-
' byteLength: 2,\n' +
180-
' byteOffset: 1,\n' +
181-
' buffer: ArrayBuffer {' +
182-
' [Uint8Contents]: <01 02 03 04>, byteLength: 4 }\n}');
179+
' [byteLength]: 2,\n' +
180+
' [byteOffset]: 1,\n' +
181+
' [buffer]: ArrayBuffer {' +
182+
' [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }\n}');
183183
assert.strictEqual(
184184
util.inspect(ab, showHidden),
185-
'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }'
185+
'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }'
186186
);
187187
assert.strictEqual(util.inspect(dv, showHidden),
188188
'DataView {\n' +
189-
' byteLength: 2,\n' +
190-
' byteOffset: 1,\n' +
191-
' buffer: ArrayBuffer { [Uint8Contents]: ' +
192-
'<01 02 03 04>, byteLength: 4 }\n}');
189+
' [byteLength]: 2,\n' +
190+
' [byteOffset]: 1,\n' +
191+
' [buffer]: ArrayBuffer { [Uint8Contents]: ' +
192+
'<01 02 03 04>, [byteLength]: 4 }\n}');
193193
ab.x = 42;
194194
dv.y = 1337;
195195
assert.strictEqual(util.inspect(ab, showHidden),
196196
'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, ' +
197-
'byteLength: 4, x: 42 }');
198-
assert.strictEqual(util.inspect(dv, showHidden),
197+
'[byteLength]: 4, x: 42 }');
198+
assert.strictEqual(util.inspect(dv, { showHidden, breakLength: 82 }),
199199
'DataView {\n' +
200-
' byteLength: 2,\n' +
201-
' byteOffset: 1,\n' +
202-
' buffer: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' +
203-
' byteLength: 4, x: 42 },\n' +
200+
' [byteLength]: 2,\n' +
201+
' [byteOffset]: 1,\n' +
202+
' [buffer]: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' +
203+
' [byteLength]: 4, x: 42 },\n' +
204204
' y: 1337\n}');
205205
}
206206

207207
{
208208
const ab = new ArrayBuffer(42);
209+
const dv = new DataView(ab);
210+
209211
assert.strictEqual(ab.byteLength, 42);
210212
new MessageChannel().port1.postMessage(ab, [ ab ]);
211213
assert.strictEqual(ab.byteLength, 0);
212214
assert.strictEqual(util.inspect(ab),
213-
'ArrayBuffer { (detached), byteLength: 0 }');
215+
'ArrayBuffer { (detached), [byteLength]: 0 }');
216+
217+
assert.strictEqual(
218+
util.inspect(dv),
219+
'DataView {\n' +
220+
' [byteLength]: 0,\n' +
221+
' [byteOffset]: undefined,\n' +
222+
' [buffer]: ArrayBuffer { (detached), [byteLength]: 0 }\n' +
223+
' }',
224+
);
214225
}
215226

216227
// Truncate output for ArrayBuffers using plural or singular bytes
217228
{
218229
const ab = new ArrayBuffer(3);
219-
assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2 }),
230+
assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2, breakLength: 82 }),
220231
'ArrayBuffer { [Uint8Contents]' +
221-
': <00 00 ... 1 more byte>, byteLength: 3 }');
222-
assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1 }),
232+
': <00 00 ... 1 more byte>, [byteLength]: 3 }');
233+
assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1, breakLength: 82 }),
223234
'ArrayBuffer { [Uint8Contents]' +
224-
': <00 ... 2 more bytes>, byteLength: 3 }');
235+
': <00 ... 2 more bytes>, [byteLength]: 3 }');
225236
}
226237

227238
// Now do the same checks but from a different context.
@@ -231,35 +242,35 @@ assert.doesNotMatch(
231242
const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab });
232243
assert.strictEqual(
233244
util.inspect(ab, showHidden),
234-
'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }'
245+
'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, [byteLength]: 4 }'
235246
);
236247
assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
237248
'DataView {\n' +
238-
' byteLength: 2,\n' +
239-
' byteOffset: 1,\n' +
240-
' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
241-
' byteLength: 4 }\n}');
249+
' [byteLength]: 2,\n' +
250+
' [byteOffset]: 1,\n' +
251+
' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
252+
' [byteLength]: 4 }\n}');
242253
assert.strictEqual(
243254
util.inspect(ab, showHidden),
244-
'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }'
255+
'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, [byteLength]: 4 }'
245256
);
246257
assert.strictEqual(util.inspect(dv, showHidden),
247258
'DataView {\n' +
248-
' byteLength: 2,\n' +
249-
' byteOffset: 1,\n' +
250-
' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
251-
' byteLength: 4 }\n}');
259+
' [byteLength]: 2,\n' +
260+
' [byteOffset]: 1,\n' +
261+
' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
262+
' [byteLength]: 4 }\n}');
252263
ab.x = 42;
253264
dv.y = 1337;
254265
assert.strictEqual(util.inspect(ab, showHidden),
255266
'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' +
256-
'byteLength: 4, x: 42 }');
257-
assert.strictEqual(util.inspect(dv, showHidden),
267+
'[byteLength]: 4, x: 42 }');
268+
assert.strictEqual(util.inspect(dv, { showHidden, breakLength: 82 }),
258269
'DataView {\n' +
259-
' byteLength: 2,\n' +
260-
' byteOffset: 1,\n' +
261-
' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
262-
' byteLength: 4, x: 42 },\n' +
270+
' [byteLength]: 2,\n' +
271+
' [byteOffset]: 1,\n' +
272+
' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
273+
' [byteLength]: 4, x: 42 },\n' +
263274
' y: 1337\n}');
264275
}
265276

@@ -286,7 +297,7 @@ assert.doesNotMatch(
286297
` [length]: ${length},\n` +
287298
` [byteLength]: ${byteLength},\n` +
288299
' [byteOffset]: 0,\n' +
289-
` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
300+
` [buffer]: ArrayBuffer { [byteLength]: ${byteLength} }\n]`);
290301
assert.strictEqual(
291302
util.inspect(array, false),
292303
`${constructor.name}(${length}) [ 65, 97 ]`
@@ -320,7 +331,7 @@ assert.doesNotMatch(
320331
` [length]: ${length},\n` +
321332
` [byteLength]: ${byteLength},\n` +
322333
' [byteOffset]: 0,\n' +
323-
` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
334+
` [buffer]: ArrayBuffer { [byteLength]: ${byteLength} }\n]`);
324335
assert.strictEqual(
325336
util.inspect(array, false),
326337
`${constructor.name}(${length}) [ 65, 97 ]`
@@ -1837,7 +1848,7 @@ util.inspect(process);
18371848
' [byteLength]: 0,',
18381849
' [byteOffset]: 0,',
18391850
' [buffer]: ArrayBuffer {',
1840-
' byteLength: 0,',
1851+
' [byteLength]: 0,',
18411852
' foo: true',
18421853
' }',
18431854
' ],',
@@ -1855,7 +1866,7 @@ util.inspect(process);
18551866
' [byteLength]: 0,',
18561867
' [byteOffset]: 0,',
18571868
' [buffer]: ArrayBuffer {',
1858-
' byteLength: 0,',
1869+
' [byteLength]: 0,',
18591870
' foo: true',
18601871
' }',
18611872
' ],',
@@ -1885,7 +1896,7 @@ util.inspect(process);
18851896
' [length]: 0,',
18861897
' [byteLength]: 0,',
18871898
' [byteOffset]: 0,',
1888-
' [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
1899+
' [buffer]: ArrayBuffer { [byteLength]: 0, foo: true }',
18891900
' ],',
18901901
' [Set Iterator] {',
18911902
' [ 1, 2, [length]: 2 ],',
@@ -1896,7 +1907,7 @@ util.inspect(process);
18961907
' [length]: 0,',
18971908
' [byteLength]: 0,',
18981909
' [byteOffset]: 0,',
1899-
' [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
1910+
' [buffer]: ArrayBuffer { [byteLength]: 0, foo: true }',
19001911
' ],',
19011912
' [Circular *1],',
19021913
" [Symbol(Symbol.toStringTag)]: 'Map Iterator'",
@@ -1924,7 +1935,7 @@ util.inspect(process);
19241935
' [byteLength]: 0,',
19251936
' [byteOffset]: 0,',
19261937
' [buffer]: ArrayBuffer {',
1927-
' byteLength: 0,',
1938+
' [byteLength]: 0,',
19281939
' foo: true } ],',
19291940
' [Set Iterator] {',
19301941
' [ 1,',
@@ -1938,7 +1949,7 @@ util.inspect(process);
19381949
' [byteLength]: 0,',
19391950
' [byteOffset]: 0,',
19401951
' [buffer]: ArrayBuffer {',
1941-
' byteLength: 0,',
1952+
' [byteLength]: 0,',
19421953
' foo: true } ],',
19431954
' [Circular *1],',
19441955
' [Symbol(Symbol.toStringTag)]:',
@@ -2244,12 +2255,12 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
22442255
[new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'],
22452256
[new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' +
22462257
' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' +
2247-
' byteLength: undefined\n}'],
2258+
' [byteLength]: undefined\n}'],
22482259
[new DataView(new ArrayBuffer(16)),
2249-
'[DataView: null prototype] {\n byteLength: undefined,\n ' +
2250-
'byteOffset: undefined,\n buffer: undefined\n}'],
2260+
'[DataView: null prototype] {\n [byteLength]: undefined,\n ' +
2261+
'[byteOffset]: undefined,\n [buffer]: undefined\n}'],
22512262
[new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' +
2252-
'{\n [Uint8Contents]: <00 00>,\n byteLength: undefined\n}'],
2263+
'{\n [Uint8Contents]: <00 00>,\n [byteLength]: undefined\n}'],
22532264
[/foobar/, '[RegExp: null prototype] /foobar/'],
22542265
[new Date('Sun, 14 Feb 2010 11:48:40 GMT'),
22552266
'[Date: null prototype] 2010-02-14T11:48:40.000Z'],
@@ -3632,7 +3643,7 @@ assert.strictEqual(
36323643
assert.strictEqual(
36333644
util.inspect(o),
36343645
'{\n' +
3635-
' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, byteLength: 0 },\n' +
3646+
' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, [byteLength]: 0 },\n' +
36363647
' buffer: <Buffer 48 65 6c 6c 6f>,\n' +
36373648
' typedArray: TypedArray(5) [Uint8Array] [ 72, 101, 108, 108, 111 ],\n' +
36383649
' array: [],\n' +

0 commit comments

Comments
 (0)