Skip to content

Commit

Permalink
debugger: fix inconsistent inspector output of exec new Map()
Browse files Browse the repository at this point in the history
PR-URL: #42423
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
  • Loading branch information
cola119 authored and juanarbol committed May 31, 2022
1 parent bc35640 commit 1247809
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 49 deletions.
144 changes: 95 additions & 49 deletions lib/internal/debugger/inspect_repl.js
Expand Up @@ -183,92 +183,138 @@ function convertResultToError(result) {
return err;
}

class RemoteObject {
class PropertyPreview {
constructor(attributes) {
ObjectAssign(this, attributes);
if (this.type === 'number') {
this.value =
this.unserializableValue ? +this.unserializableValue : +this.value;
}

[customInspectSymbol](depth, opts) {
switch (this.type) {
case 'string':
case 'undefined':
return utilInspect(this.value, opts);
case 'number':
case 'boolean':
return opts.stylize(this.value, this.type);
case 'object':
case 'symbol':
if (this.subtype === 'date') {
return utilInspect(new Date(this.value), opts);
}
if (this.subtype === 'array') {
return opts.stylize(this.value, 'special');
}
return opts.stylize(this.value, this.subtype || 'special');
default:
return this.value;
}
}
}

class ObjectPreview {
constructor(attributes) {
ObjectAssign(this, attributes);
}

[customInspectSymbol](depth, opts) {
function formatProperty(prop) {
switch (prop.type) {
case 'string':
case 'undefined':
return utilInspect(prop.value, opts);

case 'number':
case 'boolean':
return opts.stylize(prop.value, prop.type);

case 'object':
case 'symbol':
if (prop.subtype === 'date') {
return utilInspect(new Date(prop.value), opts);
switch (this.type) {
case 'object': {
switch (this.subtype) {
case 'date':
return utilInspect(new Date(this.description), opts);
case 'null':
return utilInspect(null, opts);
case 'regexp':
return opts.stylize(this.description, 'regexp');
case 'set': {
if (!this.entries) {
return `${this.description} ${this.overflow ? '{ ... }' : '{}'}`;
}
const values = ArrayPrototypeMap(this.entries, (entry) =>
utilInspect(new ObjectPreview(entry.value), opts));
return `${this.description} { ${ArrayPrototypeJoin(values, ', ')} }`;
}
if (prop.subtype === 'array') {
return opts.stylize(prop.value, 'special');
case 'map': {
if (!this.entries) {
return `${this.description} ${this.overflow ? '{ ... }' : '{}'}`;
}
const mappings = ArrayPrototypeMap(this.entries, (entry) => {
const key = utilInspect(new ObjectPreview(entry.key), opts);
const value = utilInspect(new ObjectPreview(entry.value), opts);
return `${key} => ${value}`;
});
return `${this.description} { ${ArrayPrototypeJoin(mappings, ', ')} }`;
}
return opts.stylize(prop.value, prop.subtype || 'special');

default:
return prop.value;
case 'array':
case undefined: {
if (this.properties.length === 0) {
return this.subtype === 'array' ? '[]' : '{}';
}
const props = ArrayPrototypeMap(this.properties, (prop, idx) => {
const value = utilInspect(new PropertyPreview(prop));
if (prop.name === `${idx}`) return value;
return `${prop.name}: ${value}`;
});
if (this.overflow) {
ArrayPrototypePush(props, '...');
}
const singleLine = ArrayPrototypeJoin(props, ', ');
const propString = singleLine.length > 60 ? ArrayPrototypeJoin(props, ',\n ') : singleLine;
return this.subtype === 'array' ? `[ ${propString} ]` : `{ ${propString} }`;
}
default:
return this.description;
}
}
default:
return this.description;
}
}
}

class RemoteObject {
constructor(attributes) {
ObjectAssign(this, attributes);
if (this.type === 'number') {
this.value =
this.unserializableValue ? +this.unserializableValue : +this.value;
}
}

[customInspectSymbol](depth, opts) {
switch (this.type) {
case 'boolean':
case 'number':
case 'string':
case 'undefined':
return utilInspect(this.value, opts);

case 'symbol':
return opts.stylize(this.description, 'special');

case 'function': {
const fnName = extractFunctionName(this.description);
const formatted = `[${this.className}${fnName}]`;
return opts.stylize(formatted, 'special');
}

case 'object':
switch (this.subtype) {
case 'date':
return utilInspect(new Date(this.description), opts);

case 'null':
return utilInspect(null, opts);

case 'regexp':
return opts.stylize(this.description, 'regexp');

case 'map':
case 'set': {
const preview = utilInspect(new ObjectPreview(this.preview), opts);
return `${this.description} ${preview}`;
}
default:
break;
}
if (this.preview) {
const props = ArrayPrototypeMap(
this.preview.properties,
(prop, idx) => {
const value = formatProperty(prop);
if (prop.name === `${idx}`) return value;
return `${prop.name}: ${value}`;
});
if (this.preview.overflow) {
ArrayPrototypePush(props, '...');
}
const singleLine = ArrayPrototypeJoin(props, ', ');
const propString =
singleLine.length > 60 ?
ArrayPrototypeJoin(props, ',\n ') :
singleLine;

return this.subtype === 'array' ?
`[ ${propString} ]` : `{ ${propString} }`;
return utilInspect(new ObjectPreview(this.preview), opts);
}
return this.description;

default:
return this.description;
}
Expand Down
42 changes: 42 additions & 0 deletions test/sequential/test-debugger-object-type-remote-object.js
@@ -0,0 +1,42 @@
'use strict';
const common = require('../common');

common.skipIfInspectorDisabled();

const fixtures = require('../common/fixtures');
const startCLI = require('../common/debugger');

const assert = require('assert');

const cli = startCLI([fixtures.path('debugger/empty.js')]);

(async () => {
await cli.waitForInitialBreak();
await cli.waitForPrompt();
await cli.command('exec new Date(0)');
assert.match(cli.output, /1970-01-01T00:00:00\.000Z/);
await cli.command('exec null');
assert.match(cli.output, /null/);
await cli.command('exec /regex/g');
assert.match(cli.output, /\/regex\/g/);
await cli.command('exec new Map()');
assert.match(cli.output, /Map\(0\) {}/);
await cli.command('exec new Map([["a",1],["b",2]])');
assert.match(cli.output, /Map\(2\) { a => 1, b => 2 }/);
await cli.command('exec new Set()');
assert.match(cli.output, /Set\(0\) {}/);
await cli.command('exec new Set([1,2])');
assert.match(cli.output, /Set\(2\) { 1, 2 }/);
await cli.command('exec new Set([{a:1},new Set([1])])');
assert.match(cli.output, /Set\(2\) { { a: 1 }, Set\(1\) { \.\.\. } }/);
await cli.command('exec a={}; a');
assert.match(cli.output, /{}/);
await cli.command('exec a={a:1,b:{c:1}}; a');
assert.match(cli.output, /{ a: 1, b: Object }/);
await cli.command('exec a=[]; a');
assert.match(cli.output, /\[\]/);
await cli.command('exec a=[1,2]; a');
assert.match(cli.output, /\[ 1, 2 \]/);
})()
.finally(() => cli.quit())
.then(common.mustCall());

0 comments on commit 1247809

Please sign in to comment.