Skip to content

Commit 946f57c

Browse files
cola119RafaelGSS
authored andcommitted
debugger: fix inconsistent inspector output of exec new Map()
PR-URL: #42423 Reviewed-By: Jan Krems <jan.krems@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 64daaca commit 946f57c

File tree

2 files changed

+137
-49
lines changed

2 files changed

+137
-49
lines changed

lib/internal/debugger/inspect_repl.js

+95-49
Original file line numberDiff line numberDiff line change
@@ -183,92 +183,138 @@ function convertResultToError(result) {
183183
return err;
184184
}
185185

186-
class RemoteObject {
186+
class PropertyPreview {
187187
constructor(attributes) {
188188
ObjectAssign(this, attributes);
189-
if (this.type === 'number') {
190-
this.value =
191-
this.unserializableValue ? +this.unserializableValue : +this.value;
189+
}
190+
191+
[customInspectSymbol](depth, opts) {
192+
switch (this.type) {
193+
case 'string':
194+
case 'undefined':
195+
return utilInspect(this.value, opts);
196+
case 'number':
197+
case 'boolean':
198+
return opts.stylize(this.value, this.type);
199+
case 'object':
200+
case 'symbol':
201+
if (this.subtype === 'date') {
202+
return utilInspect(new Date(this.value), opts);
203+
}
204+
if (this.subtype === 'array') {
205+
return opts.stylize(this.value, 'special');
206+
}
207+
return opts.stylize(this.value, this.subtype || 'special');
208+
default:
209+
return this.value;
192210
}
193211
}
212+
}
213+
214+
class ObjectPreview {
215+
constructor(attributes) {
216+
ObjectAssign(this, attributes);
217+
}
194218

195219
[customInspectSymbol](depth, opts) {
196-
function formatProperty(prop) {
197-
switch (prop.type) {
198-
case 'string':
199-
case 'undefined':
200-
return utilInspect(prop.value, opts);
201-
202-
case 'number':
203-
case 'boolean':
204-
return opts.stylize(prop.value, prop.type);
205-
206-
case 'object':
207-
case 'symbol':
208-
if (prop.subtype === 'date') {
209-
return utilInspect(new Date(prop.value), opts);
220+
switch (this.type) {
221+
case 'object': {
222+
switch (this.subtype) {
223+
case 'date':
224+
return utilInspect(new Date(this.description), opts);
225+
case 'null':
226+
return utilInspect(null, opts);
227+
case 'regexp':
228+
return opts.stylize(this.description, 'regexp');
229+
case 'set': {
230+
if (!this.entries) {
231+
return `${this.description} ${this.overflow ? '{ ... }' : '{}'}`;
232+
}
233+
const values = ArrayPrototypeMap(this.entries, (entry) =>
234+
utilInspect(new ObjectPreview(entry.value), opts));
235+
return `${this.description} { ${ArrayPrototypeJoin(values, ', ')} }`;
210236
}
211-
if (prop.subtype === 'array') {
212-
return opts.stylize(prop.value, 'special');
237+
case 'map': {
238+
if (!this.entries) {
239+
return `${this.description} ${this.overflow ? '{ ... }' : '{}'}`;
240+
}
241+
const mappings = ArrayPrototypeMap(this.entries, (entry) => {
242+
const key = utilInspect(new ObjectPreview(entry.key), opts);
243+
const value = utilInspect(new ObjectPreview(entry.value), opts);
244+
return `${key} => ${value}`;
245+
});
246+
return `${this.description} { ${ArrayPrototypeJoin(mappings, ', ')} }`;
213247
}
214-
return opts.stylize(prop.value, prop.subtype || 'special');
215-
216-
default:
217-
return prop.value;
248+
case 'array':
249+
case undefined: {
250+
if (this.properties.length === 0) {
251+
return this.subtype === 'array' ? '[]' : '{}';
252+
}
253+
const props = ArrayPrototypeMap(this.properties, (prop, idx) => {
254+
const value = utilInspect(new PropertyPreview(prop));
255+
if (prop.name === `${idx}`) return value;
256+
return `${prop.name}: ${value}`;
257+
});
258+
if (this.overflow) {
259+
ArrayPrototypePush(props, '...');
260+
}
261+
const singleLine = ArrayPrototypeJoin(props, ', ');
262+
const propString = singleLine.length > 60 ? ArrayPrototypeJoin(props, ',\n ') : singleLine;
263+
return this.subtype === 'array' ? `[ ${propString} ]` : `{ ${propString} }`;
264+
}
265+
default:
266+
return this.description;
267+
}
218268
}
269+
default:
270+
return this.description;
219271
}
272+
}
273+
}
274+
275+
class RemoteObject {
276+
constructor(attributes) {
277+
ObjectAssign(this, attributes);
278+
if (this.type === 'number') {
279+
this.value =
280+
this.unserializableValue ? +this.unserializableValue : +this.value;
281+
}
282+
}
283+
284+
[customInspectSymbol](depth, opts) {
220285
switch (this.type) {
221286
case 'boolean':
222287
case 'number':
223288
case 'string':
224289
case 'undefined':
225290
return utilInspect(this.value, opts);
226-
227291
case 'symbol':
228292
return opts.stylize(this.description, 'special');
229-
230293
case 'function': {
231294
const fnName = extractFunctionName(this.description);
232295
const formatted = `[${this.className}${fnName}]`;
233296
return opts.stylize(formatted, 'special');
234297
}
235-
236298
case 'object':
237299
switch (this.subtype) {
238300
case 'date':
239301
return utilInspect(new Date(this.description), opts);
240-
241302
case 'null':
242303
return utilInspect(null, opts);
243-
244304
case 'regexp':
245305
return opts.stylize(this.description, 'regexp');
246-
306+
case 'map':
307+
case 'set': {
308+
const preview = utilInspect(new ObjectPreview(this.preview), opts);
309+
return `${this.description} ${preview}`;
310+
}
247311
default:
248312
break;
249313
}
250314
if (this.preview) {
251-
const props = ArrayPrototypeMap(
252-
this.preview.properties,
253-
(prop, idx) => {
254-
const value = formatProperty(prop);
255-
if (prop.name === `${idx}`) return value;
256-
return `${prop.name}: ${value}`;
257-
});
258-
if (this.preview.overflow) {
259-
ArrayPrototypePush(props, '...');
260-
}
261-
const singleLine = ArrayPrototypeJoin(props, ', ');
262-
const propString =
263-
singleLine.length > 60 ?
264-
ArrayPrototypeJoin(props, ',\n ') :
265-
singleLine;
266-
267-
return this.subtype === 'array' ?
268-
`[ ${propString} ]` : `{ ${propString} }`;
315+
return utilInspect(new ObjectPreview(this.preview), opts);
269316
}
270317
return this.description;
271-
272318
default:
273319
return this.description;
274320
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
common.skipIfInspectorDisabled();
5+
6+
const fixtures = require('../common/fixtures');
7+
const startCLI = require('../common/debugger');
8+
9+
const assert = require('assert');
10+
11+
const cli = startCLI([fixtures.path('debugger/empty.js')]);
12+
13+
(async () => {
14+
await cli.waitForInitialBreak();
15+
await cli.waitForPrompt();
16+
await cli.command('exec new Date(0)');
17+
assert.match(cli.output, /1970-01-01T00:00:00\.000Z/);
18+
await cli.command('exec null');
19+
assert.match(cli.output, /null/);
20+
await cli.command('exec /regex/g');
21+
assert.match(cli.output, /\/regex\/g/);
22+
await cli.command('exec new Map()');
23+
assert.match(cli.output, /Map\(0\) {}/);
24+
await cli.command('exec new Map([["a",1],["b",2]])');
25+
assert.match(cli.output, /Map\(2\) { a => 1, b => 2 }/);
26+
await cli.command('exec new Set()');
27+
assert.match(cli.output, /Set\(0\) {}/);
28+
await cli.command('exec new Set([1,2])');
29+
assert.match(cli.output, /Set\(2\) { 1, 2 }/);
30+
await cli.command('exec new Set([{a:1},new Set([1])])');
31+
assert.match(cli.output, /Set\(2\) { { a: 1 }, Set\(1\) { \.\.\. } }/);
32+
await cli.command('exec a={}; a');
33+
assert.match(cli.output, /{}/);
34+
await cli.command('exec a={a:1,b:{c:1}}; a');
35+
assert.match(cli.output, /{ a: 1, b: Object }/);
36+
await cli.command('exec a=[]; a');
37+
assert.match(cli.output, /\[\]/);
38+
await cli.command('exec a=[1,2]; a');
39+
assert.match(cli.output, /\[ 1, 2 \]/);
40+
})()
41+
.finally(() => cli.quit())
42+
.then(common.mustCall());

0 commit comments

Comments
 (0)