Skip to content

Commit 837ca9f

Browse files
committed
feat: allow easy toggling between compiled and sourcemapped sources
Fixes microsoft/vscode#151412
1 parent dbc076e commit 837ca9f

22 files changed

+385
-62
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he
77
- feat: simplify pretty print to align with devtools ([vscode#151410](https://github.com/microsoft/vscode/issues/151410))
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))
10+
- feat: allow easy toggling between compiled and sourcemapped sources ([vscode#151412](https://github.com/microsoft/vscode/issues/151412))
1011
- fix: debugged child processes in ext host causing teardown ([#1289](https://github.com/microsoft/vscode-js-debug/issues/1289))
1112
- fix: errors thrown in process tree lookup not being visible ([vscode#150754](https://github.com/microsoft/vscode/issues/150754))
1213
- fix: extension debugging not working with two ipv6 interfaces ([vscode#144315](https://github.com/microsoft/vscode/issues/144315))

package-lock.json

Lines changed: 22 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/adapter/breakpoints.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ export class BreakpointManager {
144144

145145
_breakpointsPredictor?.onLongParse(() => dap.longPrediction({}));
146146

147+
sourceContainer.onSourceMappedSteppingChange(() => {
148+
if (this._thread) {
149+
for (const bp of this.allUserBreakpoints) {
150+
bp.refreshUiLocations(this._thread);
151+
}
152+
}
153+
});
154+
147155
this._scriptSourceMapHandler = async (script, sources) => {
148156
if (
149157
!logger.assert(

src/adapter/breakpoints/breakpointBase.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export abstract class Breakpoint {
136136

137137
/**
138138
* Updates the source location for the breakpoint. It is assumed that the
139-
* updated location is equivalent to the original on. This is used to move
139+
* updated location is equivalent to the original one. This is used to move
140140
* the breakpoints when we pretty print a source. This is dangerous with
141141
* sharp edges, use with caution.
142142
*/
@@ -156,6 +156,20 @@ export abstract class Breakpoint {
156156
);
157157
}
158158

159+
/**
160+
* Refreshes the `uiLocations` of the breakpoint. Should be used when the
161+
* resolution strategy for sources change.
162+
*/
163+
public async refreshUiLocations(thread: Thread) {
164+
await Promise.all(
165+
this.cdpBreakpoints
166+
.filter(
167+
(bp): bp is IBreakpointCdpReferenceApplied => bp.state === CdpReferenceState.Applied,
168+
)
169+
.map(bp => this.updateUiLocations(thread, bp.cdpId, bp.locations)),
170+
);
171+
}
172+
159173
/**
160174
* Sets the breakpoint in the provided thread and marks the "enabled" bit.
161175
*/
@@ -201,7 +215,7 @@ export abstract class Breakpoint {
201215
public async updateUiLocations(
202216
thread: Thread,
203217
cdpId: Cdp.Debugger.BreakpointId,
204-
resolvedLocations: Cdp.Debugger.Location[],
218+
resolvedLocations: readonly Cdp.Debugger.Location[],
205219
) {
206220
const uiLocation = (
207221
await Promise.all(
@@ -219,18 +233,18 @@ export abstract class Breakpoint {
219233
}
220234

221235
this.updateCdpRefs(list =>
222-
list.map(bp =>
223-
bp.state === CdpReferenceState.Applied && bp.cdpId === cdpId
224-
? {
225-
...bp,
226-
locations: resolvedLocations,
227-
uiLocations: [
228-
...bp.uiLocations,
229-
...this._manager._sourceContainer.currentSiblingUiLocations(uiLocation, source),
230-
],
231-
}
232-
: bp,
233-
),
236+
list.map(bp => {
237+
if (bp.state !== CdpReferenceState.Applied || bp.cdpId !== cdpId) {
238+
return bp;
239+
}
240+
const locations = this._manager._sourceContainer.currentSiblingUiLocations(uiLocation);
241+
const inPreferredSource = locations.filter(l => l.source === source);
242+
return {
243+
...bp,
244+
locations: resolvedLocations,
245+
uiLocations: inPreferredSource.length ? inPreferredSource : locations,
246+
};
247+
}),
234248
);
235249
}
236250

src/adapter/debugAdapter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ export class DebugAdapter implements IDisposable {
116116
this.dap.on('requestCDPProxy', () => this._requestCDPProxy());
117117
this.dap.on('setExcludedCallers', params => this._onSetExcludedCallers(params));
118118
this.dap.on('saveDiagnosticLogs', ({ toFile }) => this._saveDiagnosticLogs(toFile));
119+
this.dap.on('setSourceMapStepping', params => this._setSourceMapStepping(params));
120+
}
121+
122+
private _setSourceMapStepping({
123+
enabled,
124+
}: Dap.SetSourceMapSteppingParams): Promise<Dap.SetSourceMapSteppingResult> {
125+
this.sourceContainer.doSourceMappedStepping = enabled;
126+
return Promise.resolve({});
119127
}
120128

121129
private async _saveDiagnosticLogs(toFile: string): Promise<Dap.SaveDiagnosticLogsResult> {

src/adapter/sources.ts

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ export class SourceContainer {
465465
*/
466466
private readonly scriptsById: Map<Cdp.Runtime.ScriptId, Script> = new Map();
467467

468+
private onSourceMappedSteppingChangeEmitter = new EventEmitter<boolean>();
468469
private onScriptEmitter = new EventEmitter<Script>();
469470
private _dap: Dap.Api;
470471
private _sourceByOriginalUrl: Map<string, Source> = new MapUsingProjection(s => s.toLowerCase());
@@ -513,6 +514,30 @@ export class SourceContainer {
513514
return this._statistics;
514515
}
515516

517+
private _doSourceMappedStepping = this.launchConfig.sourceMaps;
518+
519+
/**
520+
* Gets whether source stepping is enabled.
521+
*/
522+
public get doSourceMappedStepping() {
523+
return this._doSourceMappedStepping;
524+
}
525+
526+
/**
527+
* Sets whether source stepping is enabled.
528+
*/
529+
public set doSourceMappedStepping(enabled: boolean) {
530+
if (enabled !== this._doSourceMappedStepping) {
531+
this._doSourceMappedStepping = enabled;
532+
this.onSourceMappedSteppingChangeEmitter.fire(enabled);
533+
}
534+
}
535+
536+
/**
537+
* Fires whenever `doSourceMappedStepping` is changed.
538+
*/
539+
public readonly onSourceMappedSteppingChange = this.onSourceMappedSteppingChangeEmitter.event;
540+
516541
constructor(
517542
@inject(IDapApi) dap: Dap.Api,
518543
@inject(ISourceMapFactory) private readonly sourceMapFactory: ISourceMapFactory,
@@ -637,32 +662,34 @@ export class SourceContainer {
637662
public async preferredUiLocation(uiLocation: IUiLocation): Promise<IPreferredUiLocation> {
638663
let isMapped = false;
639664
let unmappedReason: UnmappedReason | undefined = UnmappedReason.CannotMap;
640-
while (true) {
641-
if (!isSourceWithMap(uiLocation.source)) {
642-
break;
643-
}
665+
if (this._doSourceMappedStepping) {
666+
while (true) {
667+
if (!isSourceWithMap(uiLocation.source)) {
668+
break;
669+
}
644670

645-
const sourceMap = this._sourceMaps.get(uiLocation.source.sourceMap.url);
646-
if (
647-
!this.logger.assert(
648-
sourceMap,
649-
`Expected to have sourcemap for loaded source ${uiLocation.source.sourceMap.url}`,
650-
)
651-
) {
652-
break;
653-
}
671+
const sourceMap = this._sourceMaps.get(uiLocation.source.sourceMap.url);
672+
if (
673+
!this.logger.assert(
674+
sourceMap,
675+
`Expected to have sourcemap for loaded source ${uiLocation.source.sourceMap.url}`,
676+
)
677+
) {
678+
break;
679+
}
654680

655-
await Promise.race([sourceMap.loaded, delay(this._sourceMapTimeouts.resolveLocation)]);
656-
if (!sourceMap.map) return { ...uiLocation, isMapped, unmappedReason };
657-
const sourceMapped = this._sourceMappedUiLocation(uiLocation, sourceMap.map);
658-
if (!isUiLocation(sourceMapped)) {
659-
unmappedReason = isMapped ? undefined : sourceMapped;
660-
break;
661-
}
681+
await Promise.race([sourceMap.loaded, delay(this._sourceMapTimeouts.resolveLocation)]);
682+
if (!sourceMap.map) return { ...uiLocation, isMapped, unmappedReason };
683+
const sourceMapped = this._sourceMappedUiLocation(uiLocation, sourceMap.map);
684+
if (!isUiLocation(sourceMapped)) {
685+
unmappedReason = isMapped ? undefined : sourceMapped;
686+
break;
687+
}
662688

663-
uiLocation = sourceMapped;
664-
isMapped = true;
665-
unmappedReason = undefined;
689+
uiLocation = sourceMapped;
690+
isMapped = true;
691+
unmappedReason = undefined;
692+
}
666693
}
667694

668695
return { ...uiLocation, isMapped, unmappedReason };
@@ -711,7 +738,7 @@ export class SourceContainer {
711738
* Returns all UI locations the given location maps to.
712739
*/
713740
private getSourceMapUiLocations(uiLocation: IUiLocation): IUiLocation[] {
714-
if (!isSourceWithMap(uiLocation.source)) return [];
741+
if (!isSourceWithMap(uiLocation.source) || !this._doSourceMappedStepping) return [];
715742
const map = this._sourceMaps.get(uiLocation.source.sourceMap.url)?.map;
716743
if (!map) return [];
717744
const sourceMapUiLocation = this._sourceMappedUiLocation(uiLocation, map);

0 commit comments

Comments
 (0)