Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit d0e6499

Browse files
author
Jan Krems
committed
feat: exec .scope
1 parent ae7d759 commit d0e6499

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

lib/internal/inspect-repl.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ class RemoteObject {
168168
case 'symbol':
169169
return opts.stylize(this.description, 'special');
170170

171+
case 'function': {
172+
const fnNameMatch = (this.description).match(/^(?:function\*? )?([^(\s]+)\(/);
173+
const fnName = fnNameMatch ? `: ${fnNameMatch[1]}` : '';
174+
const formatted = `[${this.className}${fnName}]`;
175+
return opts.stylize(formatted, 'special');
176+
}
177+
171178
case 'object':
172179
switch (this.subtype) {
173180
case 'date':
@@ -286,10 +293,43 @@ function createRepl(inspector) {
286293
return formatScripts();
287294
};
288295

296+
class ScopeSnapshot {
297+
constructor(scope, properties) {
298+
Object.assign(this, scope);
299+
this.properties = new Map(properties.map((prop) => {
300+
// console.error(prop);
301+
const value = new RemoteObject(prop.value);
302+
return [prop.name, value];
303+
}));
304+
}
305+
306+
[util.inspect.custom](depth, opts) {
307+
const type = `${this.type[0].toUpperCase()}${this.type.slice(1)}`;
308+
const name = this.name ? `<${this.name}>` : '';
309+
const prefix = `${type}${name} `;
310+
return util.inspect(this.properties, opts)
311+
.replace(/^Map /, prefix);
312+
}
313+
}
314+
289315
class CallFrame {
290316
constructor(callFrame) {
291317
Object.assign(this, callFrame);
292318
}
319+
320+
loadScopes() {
321+
return Promise.all(
322+
this.scopeChain
323+
.filter((scope) => scope.type !== 'global')
324+
.map((scope) => {
325+
const { objectId } = scope.object;
326+
return Runtime.getProperties({
327+
objectId,
328+
generatePreview: true,
329+
}).then(({ result }) => new ScopeSnapshot(scope, result));
330+
})
331+
);
332+
}
293333
}
294334

295335
class Backtrace extends Array {
@@ -328,7 +368,10 @@ function createRepl(inspector) {
328368
function evalInCurrentContext(code) {
329369
// Repl asked for scope variables
330370
if (code === '.scope') {
331-
return Promise.reject('client.reqScopes not implemented');
371+
if (!selectedFrame) {
372+
return Promise.reject(new Error('Requires execution to be paused'));
373+
}
374+
return selectedFrame.loadScopes();
332375
}
333376

334377
if (selectedFrame) {

test/cli/exec.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,25 @@ test('examples/alive.js', (t) => {
4545
.then(() => cli.quit())
4646
.then(null, onFatal);
4747
});
48+
49+
test('exec .scope', (t) => {
50+
const cli = startCLI(['examples/backtrace.js']);
51+
52+
function onFatal(error) {
53+
cli.quit();
54+
throw error;
55+
}
56+
57+
return cli.waitFor(/break/)
58+
.then(() => cli.waitForPrompt())
59+
.then(() => cli.stepCommand('c'))
60+
.then(() => cli.command('exec .scope'))
61+
.then(() => {
62+
t.match(cli.output, '\'moduleScoped\'', 'displays closure from module body');
63+
t.match(cli.output, '\'a\'', 'displays local / function arg');
64+
t.match(cli.output, '\'l1\'', 'displays local scope');
65+
t.notMatch(cli.output, '\'encodeURIComponent\'', 'omits global scope');
66+
})
67+
.then(() => cli.quit())
68+
.then(null, onFatal);
69+
});

0 commit comments

Comments
 (0)