Skip to content
This repository has been archived by the owner on Oct 2, 2021. It is now read-only.

Commit

Permalink
Fix #128 - handle marking sourcemapped files as library code
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Nov 16, 2016
1 parent f164cea commit 1ae6a90
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 20 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
"dependencies": {
"@types/source-map": "^0.1.27",
"glob": "^7.0.6",
"request-light": "^0.1.0",
"noice-json-rpc": "^1.0.0",
"request-light": "^0.1.0",
"source-map": "^0.5.6",
"vscode-debugadapter": "^1.14.0-pre.2",
"vscode-debugprotocol": "^1.14.0-pre.2",
"vscode-debugadapter": "^1.15.0-pre.2",
"vscode-debugprotocol": "^1.15.0-pre.2",
"ws": "^1.1.1"
},
"devDependencies": {
Expand All @@ -28,8 +28,8 @@
"@types/mockery": "^1.4.29",
"@types/node": "^6.0.40",
"@types/ws": "0.0.32",
"del": "^2.2.2",
"chrome-remote-debug-protocol": "git://github.com/roblourens/chrome-remote-debug-protocol.git",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-debug": "^2.1.2",
"gulp-mocha": "^3.0.1",
Expand Down
51 changes: 47 additions & 4 deletions src/chrome/chromeDebugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
protected _inShutdown: boolean;
protected _attachMode: boolean;
protected _launchAttachArgs: ICommonRequestArgs;
private _libCodeRegexes: RegExp[];

private _currentStep = Promise.resolve();
private _nextUnboundBreakpointId = 0;
Expand Down Expand Up @@ -282,6 +283,7 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
this.hookConnectionEvents();
if (this._launchAttachArgs.experimentalLibraryCode) {
const patterns = this._launchAttachArgs.experimentalLibraryCode.map(glob => utils.pathGlobToBlackboxedRegex(glob));
this._libCodeRegexes = patterns.map(pattern => new RegExp(pattern, 'i'));
this.chrome.Debugger.setBlackboxPatterns({ patterns });
}

Expand Down Expand Up @@ -431,7 +433,7 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
this._scriptsById.set(script.scriptId, script);
this._scriptsByUrl.set(script.url, script);

const tryToResolve = source => {
const resolvePendingBPs = source => {
if (this._pendingBreakpointsByUrl.has(source)) {
this.resolvePendingBreakpoint(this._pendingBreakpointsByUrl.get(source))
.then(() => this._pendingBreakpointsByUrl.delete(source));
Expand All @@ -441,17 +443,58 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
const mappedUrl = this._pathTransformer.scriptParsed(script.url);
const sourceMapsP = this._sourceMapTransformer.scriptParsed(mappedUrl, script.sourceMapURL).then(sources => {
if (sources) {
sources.forEach(tryToResolve);
sources.forEach(resolvePendingBPs);
}

tryToResolve(mappedUrl);
resolvePendingBPs(mappedUrl);
this.resolveLibCode(script.scriptId, mappedUrl, sources);
});

if (this._initialSourceMapsP) {
this._initialSourceMapsP = Promise.all([this._initialSourceMapsP, sourceMapsP]);
}
}

private resolveLibCode(scriptId: string, mappedUrl: string, sources: string[]): void {
if (this.isLibCode(mappedUrl)) {
// set the whole script as lib code
this.chrome.Debugger.setBlackboxedRanges({
scriptId: scriptId,
positions: [{ lineNumber: 0, columnNumber: 0 }]
});
} else if (sources) {
const librarySources = new Set(sources.filter(sourcePath => this.isLibCode(sourcePath)));
if (librarySources.size) {
this._sourceMapTransformer.allSourcePathDetails(mappedUrl).then(details => {
const libPositions: Crdp.Debugger.ScriptPosition[] = [];

let inLibRange = false;
details.forEach((detail, i) => {
const isLibCode = librarySources.has(detail.inferredPath);
if ((isLibCode && !inLibRange) || (!isLibCode && inLibRange)) {
libPositions.push({
lineNumber: detail.startPosition.line,
columnNumber: detail.startPosition.column
});
inLibRange = !inLibRange;
}
});

this.chrome.Debugger.setBlackboxedRanges({
scriptId: scriptId,
positions: libPositions
});
});
}
}
}

private isLibCode(sourcePath: string): boolean {
return this._libCodeRegexes.some(regex => {
return regex.test(sourcePath);
});
}

private resolvePendingBreakpoint(pendingBP: IPendingBreakpoint): Promise<void> {
return this.setBreakpoints(pendingBP.args, pendingBP.requestSeq, pendingBP.ids).then(response => {
response.breakpoints.forEach((bp, i) => {
Expand Down Expand Up @@ -1232,7 +1275,7 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
// Should be like '3' or 'Infinity'.
value = object.description;
} else {
value = stringify ? JSON.stringify(object.value) : object.value;
value = stringify ? `"${object.value}"` : object.value;
}
}
}
Expand Down
45 changes: 33 additions & 12 deletions src/sourceMaps/sourceMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type MappedPosition = MappedPosition;
export interface ISourcePathDetails {
originalPath: string;
inferredPath: string;
startPosition: MappedPosition;
}

export class SourceMap {
Expand All @@ -26,9 +27,37 @@ export class SourceMap {
private _smc: SourceMapConsumer; // the source map
private _authoredPathCaseMap = new Map<string, string>(); // Maintain pathCase map because VSCode is case sensitive

private _allSourcePathDetails: ISourcePathDetails[] = []; // A list of all original paths from the sourcemap, and their inferred local paths
private _allSourcePathDetails: ISourcePathDetails[]; // A list of all original paths from the sourcemap, and their inferred local paths

// Original sourcemap details
private _originalSources: string[];
private _originalSourceRoot: string;

/**
* Returns list of ISourcePathDetails for all sources in this sourcemap, sorted by their
* positions within the sourcemap.
*/
public get allSourcePathDetails(): ISourcePathDetails[] {
if (!this._allSourcePathDetails) {
// Lazy compute because the source-map lib handles the bulk of the sourcemap parsing lazily, and this info
// is not always needed.
this._allSourcePathDetails = this._sources.map((inferredPath, i) => {
const originalSource = this._originalSources[i];
const originalPath = this._originalSourceRoot ? path.join(this._originalSourceRoot, originalSource) : originalSource;
return <ISourcePathDetails>{
inferredPath,
originalPath,
startPosition: this.generatedPositionFor(inferredPath, 0, 0)
};
}).sort((a, b) => {
if (a.startPosition.line === b.startPosition.line) {
return a.startPosition.column - b.startPosition.column;
} else {
return a.startPosition.line - b.startPosition.line;
}
});
}

return this._allSourcePathDetails;
}

Expand Down Expand Up @@ -56,15 +85,15 @@ export class SourceMap {

// Overwrite the sourcemap's sourceRoot with the version that's resolved to an absolute path,
// so the work above only has to be done once
const origSourceRoot = sm.sourceRoot;
this._originalSourceRoot = sm.sourceRoot;
sm.sourceRoot = null;

// sm.sources are initially relative paths, file:/// urls, made-up urls like webpack:///./app.js, or paths that start with /.
// resolve them to file:/// urls, using computedSourceRoot, to be simpler and unambiguous, since
// it needs to look them up later in exactly the same format.
this._sources = sm.sources.map(sourcePath => {
if (sourceMapPathOverrides) {
const fullSourceEntry = origSourceRoot ? path.join(origSourceRoot, sourcePath) : sourcePath;
const fullSourceEntry = this._originalSourceRoot ? path.join(this._originalSourceRoot, sourcePath) : sourcePath;
const mappedFullSourceEntry = sourceMapUtils.applySourceMapPathOverrides(fullSourceEntry, sourceMapPathOverrides);
if (fullSourceEntry !== mappedFullSourceEntry) {
return utils.canonicalizeUrl(mappedFullSourceEntry);
Expand All @@ -84,16 +113,8 @@ export class SourceMap {
return utils.canonicalizeUrl(sourcePath);
});

this._allSourcePathDetails = this._sources.map((inferredPath, i) => {
const originalSource = sm.sources[i];
const originalPath = origSourceRoot ? path.join(origSourceRoot, originalSource) : originalSource;
return <ISourcePathDetails>{
inferredPath,
originalPath
};
});

// Rewrite sm.sources to same as this._sources but file url with forward slashes
this._originalSources = sm.sources;
sm.sources = this._sources.map(sourceAbsPath => {
// Convert to file:/// url. After this, it's a file URL for an absolute path to a file on disk with forward slashes.
// We lowercase so authored <-> generated mapping is not case sensitive.
Expand Down

0 comments on commit 1ae6a90

Please sign in to comment.