Skip to content

Commit

Permalink
Fix microsoft#59 - bps in merged files
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Dec 6, 2015
1 parent 4e88cef commit b1c05c4
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 32 deletions.
84 changes: 59 additions & 25 deletions adapter/sourceMaps/sourceMapTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ interface IPendingBreakpoint {
*/
export class SourceMapTransformer implements IDebugTransformer {
private _sourceMaps: ISourceMaps;
private _requestSeqToSetBreakpointsArgs: Map<number, DebugProtocol.SetBreakpointsArguments>;
private _requestSeqToSetBreakpointsArgs: Map<number, ISetBreakpointsArgs>;
private _allRuntimeScriptPaths: Set<string>;
private _pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
private _webRoot: string;
private _authoredPathsToBreakpoints: Map<string, number[]>;
private _authoredPathsToMappedBPLines: Map<string, number[]>;
private _authoredPathsToMappedBPCols: Map<string, number[]>;

public launch(args: ILaunchRequestArgs): void {
this.init(args);
Expand All @@ -37,9 +38,10 @@ export class SourceMapTransformer implements IDebugTransformer {
if (args.sourceMaps) {
this._webRoot = utils.getWebRoot(args);
this._sourceMaps = new SourceMaps(this._webRoot);
this._requestSeqToSetBreakpointsArgs = new Map<number, DebugProtocol.SetBreakpointsArguments>();
this._requestSeqToSetBreakpointsArgs = new Map<number, ISetBreakpointsArgs>();
this._allRuntimeScriptPaths = new Set<string>();
this._authoredPathsToBreakpoints = new Map<string, number[]>();
this._authoredPathsToMappedBPLines = new Map<string, number[]>();
this._authoredPathsToMappedBPCols = new Map<string, number[]>();
}
}

Expand All @@ -54,22 +56,47 @@ export class SourceMapTransformer implements IDebugTransformer {
return new Promise<void>((resolve, reject) => {
if (this._sourceMaps && args.source.path) {
const argsPath = args.source.path;
const mappedPath = this._sourceMaps.MapPathFromSource(args.source.path);
const mappedPath = this._sourceMaps.MapPathFromSource(argsPath);
if (mappedPath) {
utils.Logger.log(`SourceMaps.setBP: Mapped ${args.source.path} to ${mappedPath}`);
utils.Logger.log(`SourceMaps.setBP: Mapped ${argsPath} to ${mappedPath}`);
args.authoredPath = argsPath;
args.source.path = mappedPath;

// DebugProtocol doesn't send cols, but they need to be added from sourcemaps
args.cols = [];
args.lines = args.lines.map((line, i) => {
const mappedCols = [];
const mappedLines = args.lines.map((line, i) => {
const mapped = this._sourceMaps.MapFromSource(argsPath, line, /*column=*/0);
if (mapped) {
args.cols[i] = mapped.column;
utils.Logger.log(`SourceMaps.setBP: Mapped ${argsPath}:${line}:0 to ${mappedPath}:${mapped.line}:${mapped.column}`);
mappedCols[i] = mapped.column;
return mapped.line;
} else {
utils.Logger.log(`SourceMaps.setBP: Mapped ${argsPath} but not line ${line}, column 0`);
mappedCols[i] = 0;
return line;
}
});

this._authoredPathsToMappedBPLines.set(argsPath, mappedLines);
this._authoredPathsToMappedBPCols.set(argsPath, mappedCols);

// Include BPs from other files that map to the same file. Ensure the current file's breakpoints go first
args.lines = mappedLines;
args.cols = mappedCols;
this._sourceMaps.AllMappedSources(mappedPath).forEach(sourcePath => {
if (sourcePath === argsPath) {
return;
}

const sourceBPLines = this._authoredPathsToMappedBPLines.get(sourcePath);
const sourceBPCols = this._authoredPathsToMappedBPCols.get(sourcePath);

if (sourceBPLines && sourceBPCols) {
// Don't modify the cached array
args.lines = args.lines.concat(sourceBPLines);
args.cols = args.cols.concat(sourceBPCols);
}
});
} else if (this._allRuntimeScriptPaths.has(argsPath)) {
// It's a generated file which is loaded
utils.Logger.log(`SourceMaps.setBP: SourceMaps are enabled but ${argsPath} is a runtime script`);
Expand All @@ -94,22 +121,29 @@ export class SourceMapTransformer implements IDebugTransformer {
public setBreakpointsResponse(response: ISetBreakpointsResponseBody, requestSeq: number): void {
if (this._sourceMaps && this._requestSeqToSetBreakpointsArgs.has(requestSeq)) {
const args = this._requestSeqToSetBreakpointsArgs.get(requestSeq);
response.breakpoints.forEach(bp => {
const mapped = this._sourceMaps.MapToSource(args.source.path, bp.line, (<any>bp).column);
delete (<any>bp).column;
if (mapped) {
bp.line = mapped.line;
}

this._requestSeqToSetBreakpointsArgs.delete(requestSeq);
});
} else {
// Cleanup column, which is passed in here in case it's needed for sourcemaps, but isn't actually
// part of the DebugProtocol
response.breakpoints.forEach(bp => {
delete (<any>bp).column;
});
if (args.authoredPath) {
// authoredPath is set, so the file was mapped to source.
// Remove breakpoints from files that map to the same file, and map back to source.
response.breakpoints = response.breakpoints.filter((_, i) => i < args.lines.length);
response.breakpoints.forEach(bp => {
const mapped = this._sourceMaps.MapToSource(args.source.path, bp.line, bp.column);
if (mapped) {
utils.Logger.log(`SourceMaps.setBP: Mapped ${args.source.path}:${bp.line}:${bp.column} to ${mapped.path}:${mapped.line}`);
bp.line = mapped.line;
} else {
utils.Logger.log(`SourceMaps.setBP: Can't map ${args.source.path}:${bp.line}:${bp.column}, keeping the line number as-is.`);
}

this._requestSeqToSetBreakpointsArgs.delete(requestSeq);
});
}
}

// Cleanup column, which is passed in here in case it's needed for sourcemaps, but isn't actually
// part of the DebugProtocol
response.breakpoints.forEach(bp => {
delete bp.column;
});
}

/**
Expand Down Expand Up @@ -155,7 +189,7 @@ export class SourceMapTransformer implements IDebugTransformer {

const sources = this._sourceMaps.AllMappedSources(event.body.scriptUrl);
if (sources) {
utils.Logger.log(`SourceMaps.scriptParsed: ${event.body.scriptUrl} was just loaded and has mapped sources: ${JSON.stringify(sources)}`);
utils.Logger.log(`SourceMaps.scriptParsed: ${event.body.scriptUrl} was just loaded and has mapped sources: ${JSON.stringify(sources) }`);
sources.forEach(sourcePath => {
// If there's a setBreakpoints request waiting on this script, go through setBreakpoints again
if (this._pendingBreakpointsByPath.has(sourcePath)) {
Expand Down
7 changes: 6 additions & 1 deletion webkit/webKitAdapterInterfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ interface IAttachRequestArgs extends DebugProtocol.AttachRequestArguments {
interface ISetBreakpointsArgs extends DebugProtocol.SetBreakpointsArguments {
/** DebugProtocol does not send cols, maybe it will someday, but this is used internally when a location is sourcemapped */
cols?: number[];
authoredPath?: string;
}

interface IBreakpoint extends DebugProtocol.Breakpoint {
column?: number;
}

/*
* The ResponseBody interfaces are copied from debugProtocol.d.ts which defines these inline in the Response interfaces.
* They should always match those interfaces, see the original for comments.
*/
interface ISetBreakpointsResponseBody {
breakpoints: DebugProtocol.Breakpoint[];
breakpoints: IBreakpoint[];
}

interface ISourceResponseBody {
Expand Down
10 changes: 4 additions & 6 deletions webkit/webKitDebugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
return Promise.all(responsePs);
}

private _webkitBreakpointResponsesToODPBreakpoints(url: string, responses: WebKitProtocol.Debugger.SetBreakpointByUrlResponse[], requestLines: number[]): DebugProtocol.Breakpoint[] {
private _webkitBreakpointResponsesToODPBreakpoints(url: string, responses: WebKitProtocol.Debugger.SetBreakpointByUrlResponse[], requestLines: number[]): IBreakpoint[] {
// Don't cache errored responses
const committedBpIds = responses
.filter(response => !response.error)
Expand All @@ -347,26 +347,24 @@ export class WebKitDebugAdapter implements IDebugAdapter {
this._committedBreakpointsByUrl.set(url, committedBpIds);

// Map committed breakpoints to DebugProtocol response breakpoints
const bps = responses
return responses
.map((response, i) => {
// The output list needs to be the same length as the input list, so map errors to
// unverified breakpoints.
if (response.error || !response.result.locations.length) {
return <DebugProtocol.Breakpoint>{
return <IBreakpoint>{
verified: false,
line: requestLines[i],
column: 0
};
}

return <DebugProtocol.Breakpoint>{
return <IBreakpoint>{
verified: true,
line: response.result.locations[0].lineNumber,
column: response.result.locations[0].columnNumber
};
});

return bps;
}

public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise<void> {
Expand Down

0 comments on commit b1c05c4

Please sign in to comment.