Skip to content

Commit 0d3d73c

Browse files
authored
Merge pull request #1307 from microsoft/feat/step-in-targets
feat: implement step in targets
2 parents 27637ab + 2ca542f commit 0d3d73c

14 files changed

Lines changed: 441 additions & 74 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he
88
- feat: add a new **Debug: Save Diagnostic JS Debug Logs** command ([#1301](https://github.com/microsoft/vscode-js-debug/issues/1301))
99
- feat: use custom `toString()` methods to generate object descriptions ([#1284](https://github.com/microsoft/vscode-js-debug/issues/1284))
1010
- feat: allow easy toggling between compiled and sourcemapped sources ([vscode#151412](https://github.com/microsoft/vscode/issues/151412))
11+
- feat: implement step in targets ([vscode#123879](https://github.com/microsoft/vscode/issues/123879))
1112
- fix: debugged child processes in ext host causing teardown ([#1289](https://github.com/microsoft/vscode-js-debug/issues/1289))
1213
- fix: errors thrown in process tree lookup not being visible ([vscode#150754](https://github.com/microsoft/vscode/issues/150754))
1314
- fix: extension debugging not working with two ipv6 interfaces ([vscode#144315](https://github.com/microsoft/vscode/issues/144315))

src/adapter/breakpoints.ts

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { inject, injectable } from 'inversify';
66
import Cdp from '../cdp/api';
77
import { ILogger, LogTag } from '../common/logging';
88
import { bisectArray, flatten } from '../common/objUtils';
9+
import { IPosition } from '../common/positions';
910
import { delay } from '../common/promiseUtil';
1011
import { SourceMap } from '../common/sourceMaps/sourceMap';
1112
import * as urlUtils from '../common/urlUtils';
@@ -73,6 +74,11 @@ export const enum EntryBreakpointMode {
7374
Greedy,
7475
}
7576

77+
export interface IPossibleBreakLocation {
78+
uiLocations: IUiLocation[];
79+
breakLocation: Cdp.Debugger.BreakLocation;
80+
}
81+
7682
@injectable()
7783
export class BreakpointManager {
7884
_dap: Dap.Api;
@@ -287,25 +293,22 @@ export class BreakpointManager {
287293
*/
288294
public async getBreakpointLocations(
289295
thread: Thread,
290-
request: Dap.BreakpointLocationsParams,
291-
): Promise<Dap.BreakpointLocation[]> {
292-
// Find the source we're querying in, then resolve all possibly sourcemapped
293-
// locations for that script.
294-
const source = this._sourceContainer.source(request.source);
295-
if (!source) {
296-
return [];
297-
}
298-
296+
source: Source,
297+
start: IPosition,
298+
end: IPosition,
299+
) {
300+
const start1 = start.base1;
299301
const startLocations = this._sourceContainer.currentSiblingUiLocations({
300302
source,
301-
lineNumber: request.line,
302-
columnNumber: request.column === undefined ? 1 : request.column,
303+
lineNumber: start1.lineNumber,
304+
columnNumber: start1.columnNumber,
303305
});
304306

307+
const end1 = end.base1;
305308
const endLocations = this._sourceContainer.currentSiblingUiLocations({
306309
source,
307-
lineNumber: request.endLine === undefined ? request.line + 1 : request.endLine,
308-
columnNumber: request.endColumn === undefined ? 1 : request.endColumn,
310+
lineNumber: end1.lineNumber,
311+
columnNumber: end1.columnNumber,
309312
});
310313

311314
// As far as I know the number of start and end locations should be the
@@ -321,7 +324,8 @@ export class BreakpointManager {
321324
// For each viable location, attempt to identify its script ID and then ask
322325
// Chrome for the breakpoints in the given range. For almost all scripts
323326
// we'll only every find one viable location with a script.
324-
const todo: Promise<Dap.BreakpointLocation[]>[] = [];
327+
const todo: Promise<void>[] = [];
328+
const result: IPossibleBreakLocation[] = [];
325329
for (let i = 0; i < startLocations.length; i++) {
326330
const start = startLocations[i];
327331
const end = endLocations[i];
@@ -354,39 +358,28 @@ export class BreakpointManager {
354358
})
355359
.then(r => {
356360
if (!r) {
357-
return [];
361+
return;
358362
}
359363

360364
// Map the locations from CDP back to their original source positions.
361365
// Discard any that map outside of the source we're interested in,
362366
// which is possible (e.g. if a section of code from one source is
363367
// inlined amongst the range we request).
364-
const result: Dap.BreakpointLocation[] = [];
365-
for (const location of r.locations) {
366-
const { lineNumber, columnNumber = 0 } = location;
367-
const sourceLocations = this._sourceContainer.currentSiblingUiLocations(
368-
{
369-
source: lsrc,
370-
...rawToUiOffset(
371-
base0To1({ lineNumber, columnNumber }),
372-
lsrc.runtimeScriptOffset,
373-
),
374-
},
375-
source,
376-
);
377-
378-
for (const srcLocation of sourceLocations) {
379-
result.push({ line: srcLocation.lineNumber, column: srcLocation.columnNumber });
380-
}
368+
for (const breakLocation of r.locations) {
369+
const { lineNumber, columnNumber = 0 } = breakLocation;
370+
const uiLocations = this._sourceContainer.currentSiblingUiLocations({
371+
source: lsrc,
372+
...rawToUiOffset(base0To1({ lineNumber, columnNumber }), lsrc.runtimeScriptOffset),
373+
});
374+
375+
result.push({ breakLocation, uiLocations });
381376
}
382-
383-
return result;
384377
}),
385378
);
386379
}
380+
await Promise.all(todo);
387381

388-
// Gather our results and flatten the array.
389-
return (await Promise.all(todo)).reduce((acc, r) => [...acc, ...r], []);
382+
return result;
390383
}
391384

392385
/**

src/adapter/debugAdapter.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import * as nls from 'vscode-nls';
77
import { Cdp } from '../cdp/api';
88
import { DisposableList, IDisposable } from '../common/disposable';
99
import { ILogger, LogTag } from '../common/logging';
10+
import { posInt32Counter, truthy } from '../common/objUtils';
11+
import { Base1Position } from '../common/positions';
1012
import { getDeferred, IDeferred } from '../common/promiseUtil';
1113
import { IRenameProvider } from '../common/sourceMaps/renameProvider';
1214
import * as sourceUtils from '../common/sourceUtils';
@@ -50,7 +52,7 @@ export class DebugAdapter implements IDisposable {
5052
private _thread: Thread | undefined;
5153
private _threadDeferred = getDeferred<Thread>();
5254
private _configurationDoneDeferred: IDeferred<void>;
53-
private lastBreakpointId = 0;
55+
private breakpointIdCounter = posInt32Counter();
5456
private readonly _cdpProxyProvider = this._services.get<ICdpProxyProvider>(ICdpProxyProvider);
5557

5658
constructor(
@@ -92,7 +94,7 @@ export class DebugAdapter implements IDisposable {
9294
this.dap.on('continue', () => this._withThread(thread => thread.resume()));
9395
this.dap.on('pause', () => this._withThread(thread => thread.pause()));
9496
this.dap.on('next', () => this._withThread(thread => thread.stepOver()));
95-
this.dap.on('stepIn', () => this._withThread(thread => thread.stepInto()));
97+
this.dap.on('stepIn', params => this._withThread(thread => thread.stepInto(params.targetId)));
9698
this.dap.on('stepOut', () => this._withThread(thread => thread.stepOut()));
9799
this.dap.on('restartFrame', params => this._withThread(thread => thread.restartFrame(params)));
98100
this.dap.on('scopes', params => this._withThread(thread => thread.scopes(params)));
@@ -107,16 +109,47 @@ export class DebugAdapter implements IDisposable {
107109
this.dap.on('getPerformance', () =>
108110
this._withThread(thread => performanceProvider.retrieve(thread.cdp())),
109111
);
110-
this.dap.on('breakpointLocations', params =>
111-
this._withThread(async thread => ({
112-
breakpoints: await this.breakpointManager.getBreakpointLocations(thread, params),
113-
})),
114-
);
112+
this.dap.on('breakpointLocations', params => this._breakpointLocations(params));
115113
this.dap.on('createDiagnostics', params => this._dumpDiagnostics(params));
116114
this.dap.on('requestCDPProxy', () => this._requestCDPProxy());
117115
this.dap.on('setExcludedCallers', params => this._onSetExcludedCallers(params));
118116
this.dap.on('saveDiagnosticLogs', ({ toFile }) => this._saveDiagnosticLogs(toFile));
119117
this.dap.on('setSourceMapStepping', params => this._setSourceMapStepping(params));
118+
this.dap.on('stepInTargets', params => this._stepInTargets(params));
119+
}
120+
121+
private _breakpointLocations(
122+
params: Dap.BreakpointLocationsParams,
123+
): Promise<Dap.BreakpointLocationsResult> {
124+
return this._withThread(async thread => {
125+
const source = this.sourceContainer.source(params.source);
126+
if (!source) {
127+
return { breakpoints: [] };
128+
}
129+
130+
const possibleBps = await this.breakpointManager.getBreakpointLocations(
131+
thread,
132+
source,
133+
new Base1Position(params.line, params.column || 1),
134+
new Base1Position(
135+
params.endLine || params.line + 1,
136+
params.endColumn || params.column || 1,
137+
),
138+
);
139+
140+
return {
141+
breakpoints: possibleBps
142+
.map(bp => bp.uiLocations.find(l => l.source === source))
143+
.filter(truthy)
144+
.map(bp => ({ line: bp.lineNumber, column: bp.columnNumber })),
145+
};
146+
});
147+
}
148+
149+
private _stepInTargets(params: Dap.StepInTargetsParams): Promise<Dap.StepInTargetsResult> {
150+
return this._withThread(async thread => ({
151+
targets: await thread.getStepInTargets(params.frameId),
152+
}));
120153
}
121154

122155
private _setSourceMapStepping({
@@ -194,7 +227,7 @@ export class DebugAdapter implements IDisposable {
194227
supportsSetVariable: true,
195228
supportsRestartFrame: true,
196229
supportsGotoTargetsRequest: false,
197-
supportsStepInTargetsRequest: false,
230+
supportsStepInTargetsRequest: true,
198231
supportsCompletionsRequest: true,
199232
supportsModulesRequest: false,
200233
additionalModuleColumns: [],
@@ -224,7 +257,7 @@ export class DebugAdapter implements IDisposable {
224257
): Promise<Dap.SetBreakpointsResult | Dap.Error> {
225258
return this.breakpointManager.setBreakpoints(
226259
params,
227-
params.breakpoints?.map(() => ++this.lastBreakpointId) ?? [],
260+
params.breakpoints?.map(() => this.breakpointIdCounter()) ?? [],
228261
);
229262
}
230263

src/adapter/stackTrace.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import * as nls from 'vscode-nls';
66
import Cdp from '../cdp/api';
7-
import { once } from '../common/objUtils';
7+
import { once, posInt32Counter } from '../common/objUtils';
88
import { Base0Position } from '../common/positions';
99
import Dap from '../dap/api';
1010
import { asyncScopesNotAvailable } from '../dap/errors';
@@ -17,6 +17,8 @@ import { IExtraProperty, IScopeRef, IVariableContainer } from './variableStore';
1717
const localize = nls.loadMessageBundle();
1818

1919
export interface IFrameElement {
20+
/** DAP stack frame ID */
21+
frameId: number;
2022
/** Formats the stack element as V8 would format it */
2123
formatAsNative(): Promise<string>;
2224
/** Pretty formats the stack element as text */
@@ -146,7 +148,7 @@ export class StackTrace {
146148
_appendFrame(frame: FrameElement) {
147149
this.frames.push(frame);
148150
if (frame instanceof StackFrame) {
149-
this._frameById.set(frame._id, frame);
151+
this._frameById.set(frame.frameId, frame);
150152
}
151153
}
152154

@@ -200,7 +202,11 @@ interface IScope {
200202
callFrameId: string;
201203
}
202204

205+
const frameIdCounter = posInt32Counter();
206+
203207
export class AsyncSeparator implements IFrameElement {
208+
public readonly frameId = frameIdCounter();
209+
204210
constructor(private readonly label = 'async') {}
205211

206212
public async toDap(): Promise<Dap.StackFrame> {
@@ -217,9 +223,8 @@ export class AsyncSeparator implements IFrameElement {
217223
}
218224

219225
export class StackFrame implements IFrameElement {
220-
private static _lastFrameId = 0;
226+
public readonly frameId = frameIdCounter();
221227

222-
_id: number;
223228
private _name: string;
224229
private _rawLocation: RawLocation;
225230
public readonly uiLocation: () =>
@@ -262,7 +267,6 @@ export class StackFrame implements IFrameElement {
262267
rawLocation: RawLocation,
263268
private readonly isAsync = false,
264269
) {
265-
this._id = ++StackFrame._lastFrameId;
266270
this._name = callFrame.functionName || '<anonymous>';
267271
this._rawLocation = rawLocation;
268272
this.uiLocation = once(() => thread.rawLocationToUiLocation(rawLocation));
@@ -403,7 +407,7 @@ export class StackFrame implements IFrameElement {
403407
}
404408

405409
return {
406-
id: this._id,
410+
id: this.frameId,
407411
name: formattedName, // TODO: Use params to format the name
408412
line,
409413
column,

0 commit comments

Comments
 (0)