From 26bb1cf73ce9f19fb0e47a15311fdc3644f139a0 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 8 Mar 2016 14:28:36 +0200 Subject: [PATCH 1/5] support preliminary breakpoints (android only, no source maps) --- adapter/pathTransformer.ts | 46 +++++++++++++++++++++++++- webkit/utilities.ts | 68 +++++++++++++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/adapter/pathTransformer.ts b/adapter/pathTransformer.ts index 55684e0..1d9c008 100644 --- a/adapter/pathTransformer.ts +++ b/adapter/pathTransformer.ts @@ -4,6 +4,7 @@ import * as utils from '../webkit/utilities'; import {DebugProtocol} from 'vscode-debugprotocol'; +import * as path from 'path'; import {ISetBreakpointsArgs, IDebugTransformer, ILaunchRequestArgs, IAttachRequestArgs, IStackTraceResponseBody} from '../webkit/WebKitAdapterInterfaces'; interface IPendingBreakpoint { @@ -21,6 +22,7 @@ export class PathTransformer implements IDebugTransformer { private _clientPathToWebkitUrl = new Map(); private _webkitUrlToClientPath = new Map(); private _pendingBreakpointsByPath = new Map(); + private inferedDeviceRoot :string = null; public launch(args: ILaunchRequestArgs): void { this._webRoot = utils.getAppRoot(args); @@ -51,7 +53,38 @@ export class PathTransformer implements IDebugTransformer { args.source.path = this._clientPathToWebkitUrl.get(url); utils.Logger.log(`Paths.setBP: Resolved ${url} to ${args.source.path}`); resolve(); - } else { + } + else if (this.inferedDeviceRoot) { + utils.Logger.log(`Paths.setBP: No target url cached for client path: ${url}. Using inffered device root to set breakpoint`); + + let inferedUrl = url.replace(this._webRoot, this.inferedDeviceRoot).replace(/\\/g, "/"); + + //change device path if {N} core module or {N} module + if (inferedUrl.indexOf("/node_modules/tns-core-modules/") != -1) + { + inferedUrl = inferedUrl.replace("/node_modules/tns-core-modules/", "/app/tns_modules/"); + } + else if (inferedUrl.indexOf("/node_modules/") != -1) + { + inferedUrl = inferedUrl.replace("/node_modules/", "/app/tns_modules/"); + } + + //change platform specific paths + if (inferedUrl.indexOf(".android.") != -1) + { + inferedUrl = inferedUrl.replace(".android.", "."); + } + else if (inferedUrl.indexOf(".ios.") != -1) + { + inferedUrl = inferedUrl.replace(".ios.", "."); + } + + args.source.path = inferedUrl; + this._pendingBreakpointsByPath.set(args.source.path, { resolve, reject, args }); + utils.Logger.log("resolving infered promise on path " + url); + resolve(); + } + else { utils.Logger.log(`Paths.setBP: No target url cached for client path: ${url}, waiting for target script to be loaded.`); args.source.path = url; this._pendingBreakpointsByPath.set(args.source.path, { resolve, reject, args }); @@ -70,6 +103,17 @@ export class PathTransformer implements IDebugTransformer { public scriptParsed(event: DebugProtocol.Event): void { const webkitUrl: string = event.body.scriptUrl; + if (!this.inferedDeviceRoot) + { + this.inferedDeviceRoot = utils.inferDeviceRoot(this._webRoot, this._platform, webkitUrl); + utils.Logger.log("\n\n\n ***Inferred device root: " + this.inferedDeviceRoot + "\n\n\n"); + + if (this.inferedDeviceRoot.indexOf("/data/user/0/") != -1) + { + this.inferedDeviceRoot = this.inferedDeviceRoot.replace("/data/user/0/", "/data/data/"); + } + } + const clientPath = utils.webkitUrlToClientPath(this._webRoot, this._platform, webkitUrl); if (!clientPath) { diff --git a/webkit/utilities.ts b/webkit/utilities.ts index bb9bded..5ff370a 100644 --- a/webkit/utilities.ts +++ b/webkit/utilities.ts @@ -240,13 +240,13 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return ''; } - aUrl = decodeURI(aUrl); - - // If we don't have the client workingDirectory for some reason, don't try to map the url to a client path + // If we don't have the client workingDirectory for some reason, don't try to map the url to a client path if (!webRoot) { return ''; } + aUrl = decodeURI(aUrl); + // Search the filesystem under the webRoot for the file that best matches the given url let pathName = url.parse(canonicalizeUrl(aUrl)).pathname; if (!pathName || pathName === '/') { @@ -261,6 +261,7 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return nsProjectFile; } + let shiftedParts = []; let pathParts = pathName.split(path.sep); while (pathParts.length > 0) { const clientPath = path.join(webRoot, pathParts.join(path.sep)); @@ -268,7 +269,8 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return canonicalizeUrl(clientPath); } - pathParts.shift(); + let shifted = pathParts.shift(); + shiftedParts.push(shifted); } //check for {N} android internal files @@ -285,6 +287,64 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return ''; } +/** + * Infers the device root of a given path. + * The device root is the parent directory of all {N} source files + * This implementation assumes that all files are all under one common root on the device + * Returns all the device parent directories of a source file until the file is found on the client by client path + */ +export function inferDeviceRoot(projectRoot: string, additionalFileExtension: string, aUrl: string): string { + if (!aUrl) { + return null; + } + + // If we don't have the projectRoot for some reason, don't try to map the url to a client path + if (!projectRoot) { + return null; + } + + aUrl = decodeURI(aUrl); + + // Search the filesystem under the webRoot for the file that best matches the given url + let pathName = url.parse(canonicalizeUrl(aUrl)).pathname; + if (!pathName || pathName === '/') { + return null; + } + + // Dealing with the path portion of either a url or an absolute path to remote file. + // Need to force path.sep separator + pathName = pathName.replace(/\//g, path.sep); + + let shiftedParts = []; + let pathParts = pathName.split(path.sep); + while (pathParts.length > 0) { + const clientPath = path.join(projectRoot, pathParts.join(path.sep)); + if (existsSync(clientPath)) { + //return canonicalizeUrl(clientPath); + return shiftedParts.join(path.sep).replace(/\\/g, "/"); + } + + let shifted = pathParts.shift(); + shiftedParts.push(shifted); + } + + //check for {N} android internal files + shiftedParts = []; + pathParts = pathName.split(path.sep); + while (pathParts.length > 0) { + const clientPath = path.join(projectRoot, "platforms/android/src/main/assets", pathParts.join(path.sep)); + if (existsSync(clientPath)) { + //return canonicalizeUrl(clientPath); + return shiftedParts.join(path.sep).replace(/\\/g, "/"); + } + + let shifted = pathParts.shift(); + shiftedParts.push(shifted); + } + + return null; +} + /** * Modify a url either from the client or the webkit target to a common format for comparing. * The client can handle urls in this format too. From cd95e3b9d6a44bddeff1dd4a15c173a19d9bd8ce Mon Sep 17 00:00:00 2001 From: blagoev Date: Fri, 11 Mar 2016 16:28:34 +0200 Subject: [PATCH 2/5] Fix preliminary breakpoints (for android) --- adapter/sourceMaps/sourceMapTransformer.ts | 52 ++-- adapter/sourceMaps/sourceMaps.ts | 299 +++++++++++++++++---- webkit/webKitDebugAdapter.ts | 2 +- 3 files changed, 283 insertions(+), 70 deletions(-) diff --git a/adapter/sourceMaps/sourceMapTransformer.ts b/adapter/sourceMaps/sourceMapTransformer.ts index e13081c..166371e 100644 --- a/adapter/sourceMaps/sourceMapTransformer.ts +++ b/adapter/sourceMaps/sourceMapTransformer.ts @@ -189,26 +189,8 @@ export class SourceMapTransformer implements IDebugTransformer { let sourceMapUrlValue = event.body.sourceMapURL; - if (!event.body.sourceMapURL) { - - let fileContents = fs.readFileSync(event.body.scriptUrl, 'utf8'); - - var baseRegex = "\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)"; - - // Matches /* ... */ comments - var blockCommentRegex = new RegExp("/\\*" + baseRegex + "\\s*\\*/"); - - // Matches // .... comments - var commentRegex = new RegExp("//" + baseRegex + "($|\n|\r\n?)"); - - let match = fileContents.match(commentRegex); - if (!match) { - match = fileContents.match(blockCommentRegex); - } - - if (match) { - sourceMapUrlValue = match[1]; - } + if (!sourceMapUrlValue) { + sourceMapUrlValue = this._sourceMaps.FindSourceMapUrlInFile(event.body.scriptUrl); } if (!sourceMapUrlValue || sourceMapUrlValue === "") { @@ -226,6 +208,36 @@ export class SourceMapTransformer implements IDebugTransformer { } } + // private getSourceMappingFile(filePathOrSourceMapValue: string): string { + + // let result = filePathOrSourceMapValue; + + // if (!fs.existsSync(filePathOrSourceMapValue)) { + // return result; + // } + + // let fileContents = fs.readFileSync(filePathOrSourceMapValue, 'utf8'); + + // var baseRegex = "\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)"; + + // // Matches /* ... */ comments + // var blockCommentRegex = new RegExp("/\\*" + baseRegex + "\\s*\\*/"); + + // // Matches // .... comments + // var commentRegex = new RegExp("//" + baseRegex + "($|\n|\r\n?)"); + + // let match = fileContents.match(commentRegex); + // if (!match) { + // match = fileContents.match(blockCommentRegex); + // } + + // if (match) { + // result = match[1]; + // } + + // return result; + // } + private resolvePendingBreakpoints(sourcePath: string): void { // If there's a setBreakpoints request waiting on this script, go through setBreakpoints again if (this._pendingBreakpointsByPath.has(sourcePath)) { diff --git a/adapter/sourceMaps/sourceMaps.ts b/adapter/sourceMaps/sourceMaps.ts index 678e272..790cada 100644 --- a/adapter/sourceMaps/sourceMaps.ts +++ b/adapter/sourceMaps/sourceMaps.ts @@ -47,6 +47,8 @@ export interface ISourceMaps { * With a known sourceMapURL for a generated script, process create the SourceMap and cache for later */ ProcessNewSourceMap(path: string, sourceMapURL: string): Promise; + + FindSourceMapUrlInFile(generatedFilePath: string): string; } @@ -70,7 +72,7 @@ export class SourceMaps implements ISourceMaps { var map = this._findSourceToGeneratedMapping(pathToSource); if (map) return map.generatedPath(); - return null;; + return null; } public MapFromSource(pathToSource: string, line: number, column: number): MappingResult { @@ -110,92 +112,280 @@ export class SourceMaps implements ISourceMaps { //---- private ----------------------------------------------------------------------- - private _findSourceToGeneratedMapping(pathToSource: string): SourceMap { + private _findSourceToGeneratedMapping(pathToSource: string): SourceMap { - if (pathToSource) { - - if (pathToSource in this._sourceToGeneratedMaps) { - return this._sourceToGeneratedMaps[pathToSource]; - } + if (!pathToSource) { + return null; + } - for (let key in this._generatedToSourceMaps) { - const m = this._generatedToSourceMaps[key]; - if (m.doesOriginateFrom(pathToSource)) { - this._sourceToGeneratedMaps[pathToSource] = m; - return m; - } - } + if (pathToSource in this._sourceToGeneratedMaps) { + return this._sourceToGeneratedMaps[pathToSource]; + } - // not found in existing maps - } - return null; - } + // a reverse lookup: in all source maps try to find pathToSource in the sources array + for (let key in this._generatedToSourceMaps) { + const m = this._generatedToSourceMaps[key]; + if (m.doesOriginateFrom(pathToSource)) { + this._sourceToGeneratedMaps[pathToSource] = m; + return m; + } + } - /** - * pathToGenerated - an absolute local path or a URL. - * mapPath - a path relative to pathToGenerated. - */ - private _findGeneratedToSourceMapping(pathToGenerated: string, mapPath: string): Promise { - if (!pathToGenerated) { - return Promise.resolve(null); + //try finding a map file next to the source file + let generatedFilePath = null; + const pos = pathToSource.lastIndexOf('.'); + if (pos >= 0) { + generatedFilePath = pathToSource.substr(0, pos) + '.js'; } - if (pathToGenerated in this._generatedToSourceMaps) { - return Promise.resolve(this._generatedToSourceMaps[pathToGenerated]); + if (FS.existsSync(generatedFilePath)) { + let parsedSourceMap = this.findGeneratedToSourceMappingSync(generatedFilePath); + if (parsedSourceMap) { + if (parsedSourceMap.doesOriginateFrom(pathToSource)) { + this._sourceToGeneratedMaps[pathToSource] = parsedSourceMap; + return parsedSourceMap; + } + } } - if (mapPath.indexOf("data:application/json;base64,") >= 0) { - Logger.log(`SourceMaps.findGeneratedToSourceMapping: Using inlined sourcemap in ${pathToGenerated}`); + //try finding all js files in app root and parse their source maps + let files = this.walkPath(this._webRoot); + files.forEach(file => { + let parsedSourceMap = this.findGeneratedToSourceMappingSync(file); + if (parsedSourceMap) { + if (parsedSourceMap.doesOriginateFrom(pathToSource)) { + this._sourceToGeneratedMaps[pathToSource] = parsedSourceMap; + return parsedSourceMap; + } + } + }); - // sourcemap is inlined - const pos = mapPath.indexOf(','); - const data = mapPath.substr(pos+1); - try { - const buffer = new Buffer(data, 'base64'); - const json = buffer.toString(); - if (json) { - const map = new SourceMap(pathToGenerated, json, this._webRoot); - this._generatedToSourceMaps[pathToGenerated] = map; - return Promise.resolve(map); + + // let module_files = this.walkPath(Path.join(this._webRoot, "node_modules")); + // module_files.forEach(file => { + // let parsedSourceMap = this.findGeneratedToSourceMappingSync(file); + // if (parsedSourceMap) + // { + // if (parsedSourceMap.doesOriginateFrom(pathToSource)) + // { + // this._sourceToGeneratedMaps[pathToSource] = parsedSourceMap; + // return parsedSourceMap; + // } + // } + // }); + + return null; + // not found in existing maps + } + + /** + * try to find the 'sourceMappingURL' in the file with the given path. + * Returns null in case of errors. + */ + public FindSourceMapUrlInFile(generatedFilePath: string): string { + + try { + const contents = FS.readFileSync(generatedFilePath).toString(); + const lines = contents.split('\n'); + for (let line of lines) { + const matches = SourceMaps.SOURCE_MAPPING_MATCHER.exec(line); + if (matches && matches.length === 2) { + const uri = matches[1].trim(); + Logger.log(`_findSourceMapUrlInFile: source map url at end of generated file '${generatedFilePath}''`); + return uri; } } - catch (e) { - Logger.log(`SourceMaps.findGeneratedToSourceMapping: exception while processing data url (${e.stack})`); + } catch (e) { + // ignore exception + } + return null; + } + + private walkPath(path: string): string[] { + var results = []; + var list = FS.readdirSync(path); + list.forEach(file => { + file = Path.join(path, file); + var stat = FS.statSync(file); + if (stat && stat.isDirectory()) { + results = results.concat(this.walkPath(file)); + } + else { + results.push(file); } + }); - return null; + return results + } + + // /** + // * Loads source map from file system. + // * If no generatedPath is given, the 'file' attribute of the source map is used. + // */ + // private _loadSourceMap(map_path: string, generatedPath?: string): SourceMap { + + // if (map_path in this._allSourceMaps) { + // return this._allSourceMaps[map_path]; + // } + + // try { + // const mp = Path.join(map_path); + // const contents = FS.readFileSync(mp).toString(); + + // const map = new SourceMap(mp, generatedPath, contents); + // this._allSourceMaps[map_path] = map; + + // this._registerSourceMap(map); + + // Logger.log(`_loadSourceMap: successfully loaded source map '${map_path}'`); + + // return map; + // } + // catch (e) { + // Logger.log(`_loadSourceMap: loading source map '${map_path}' failed with exception: ${e}`); + // } + // return null; + // } + + // private _registerSourceMap(map: SourceMap) { + // const gp = map.generatedPath(); + // if (gp) { + // this._generatedToSourceMaps[gp] = map; + // } + // } + + /** + * pathToGenerated - an absolute local path or a URL. + * mapPath - a path relative to pathToGenerated. + */ + private _findGeneratedToSourceMapping(generatedFilePath: string, mapPath: string): Promise { + if (!generatedFilePath) { + return Promise.resolve(null); + } + + if (generatedFilePath in this._generatedToSourceMaps) { + return Promise.resolve(this._generatedToSourceMaps[generatedFilePath]); } + let parsedSourceMap = this.parseInlineSourceMap(mapPath, generatedFilePath); + if (parsedSourceMap) + { + return Promise.resolve(parsedSourceMap); + } + // if path is relative make it absolute if (!Path.isAbsolute(mapPath)) { - if (Path.isAbsolute(pathToGenerated)) { + if (Path.isAbsolute(generatedFilePath)) { // runtime script is on disk, so map should be too - mapPath = PathUtils.makePathAbsolute(pathToGenerated, mapPath); + mapPath = PathUtils.makePathAbsolute(generatedFilePath, mapPath); } else { // runtime script is not on disk, construct the full url for the source map - const scriptUrl = URL.parse(pathToGenerated); + const scriptUrl = URL.parse(generatedFilePath); mapPath = `${scriptUrl.protocol}//${scriptUrl.host}${Path.dirname(scriptUrl.pathname)}/${mapPath}`; } } - return this._createSourceMap(mapPath, pathToGenerated).then(map => { + return this._createSourceMap(mapPath, generatedFilePath).then(map => { if (!map) { - const mapPathNextToSource = pathToGenerated + ".map"; + const mapPathNextToSource = generatedFilePath + ".map"; if (mapPathNextToSource !== mapPath) { - return this._createSourceMap(mapPathNextToSource, pathToGenerated); + return this._createSourceMap(mapPathNextToSource, generatedFilePath); } } return map; }).then(map => { if (map) { - this._generatedToSourceMaps[pathToGenerated] = map; + this._generatedToSourceMaps[generatedFilePath] = map; } return map || null; }); } + + /** + * generatedFilePath - an absolute local path to the generated file + * returns the SourceMap parsed from inlined value or from a map file available next to the generated file + */ + private findGeneratedToSourceMappingSync(generatedFilePath: string): SourceMap { + if (!generatedFilePath) { + return null; + } + + if (generatedFilePath in this._generatedToSourceMaps) { + return this._generatedToSourceMaps[generatedFilePath]; + } + + let sourceMapUrlValue = this.FindSourceMapUrlInFile(generatedFilePath); + if (!sourceMapUrlValue) + { + return null; + } + + let parsedSourceMap = this.parseInlineSourceMap(sourceMapUrlValue, generatedFilePath); + if (parsedSourceMap) { + return parsedSourceMap; + } + + if (!FS.existsSync(generatedFilePath)) { + Logger.log("findGeneratedToSourceMappingSync: can't find the sourceMapping for file: " + generatedFilePath); + return null; + } + + // if path is relative make it absolute + if (!Path.isAbsolute(sourceMapUrlValue)) { + if (Path.isAbsolute(generatedFilePath)) { + // runtime script is on disk, so map should be too + sourceMapUrlValue = PathUtils.makePathAbsolute(generatedFilePath, sourceMapUrlValue); + } else { + // runtime script is not on disk, construct the full url for the source map + // const scriptUrl = URL.parse(generatedFilePath); + // mapPath = `${scriptUrl.protocol}//${scriptUrl.host}${Path.dirname(scriptUrl.pathname)}/${mapPath}`; + + return null; + } + } + + let map = this._createSourceMapSync(sourceMapUrlValue, generatedFilePath); + if (!map) { + const mapPathNextToSource = generatedFilePath + ".map"; + if (mapPathNextToSource !== sourceMapUrlValue) { + map = this._createSourceMapSync(mapPathNextToSource, generatedFilePath); + } + } + + if (map) { + this._generatedToSourceMaps[generatedFilePath] = map; + return map; + } + + return null; + } + + private parseInlineSourceMap(sourceMapContents: string, generatedFilePath: string) : SourceMap + { + if (sourceMapContents.indexOf("data:application/json;base64,") >= 0) { + // sourcemap is inlined + const pos = sourceMapContents.indexOf(','); + const data = sourceMapContents.substr(pos+1); + try { + const buffer = new Buffer(data, 'base64'); + const json = buffer.toString(); + if (json) { + const map = new SourceMap(generatedFilePath, json, this._webRoot); + this._generatedToSourceMaps[generatedFilePath] = map; + return map; + } + } + catch (e) { + Logger.log(`can't parse inlince sourcemap. exception while processing data url (${e.stack})`); + } + } + + return null; + } + private _createSourceMap(mapPath: string, pathToGenerated: string): Promise { let contentsP: Promise; if (utils.isURL(mapPath)) { @@ -230,6 +420,17 @@ export class SourceMaps implements ISourceMaps { } }); } + + private _createSourceMapSync(mapPath: string, pathToGenerated: string): SourceMap { + let contents = FS.readFileSync(mapPath, 'utf8'); + try { + // Throws for invalid contents JSON + return new SourceMap(pathToGenerated, contents, this._webRoot); + } catch (e) { + Logger.log(`SourceMaps.createSourceMap: exception while processing sourcemap: ${e.stack}`); + return null; + } + } } enum Bias { diff --git a/webkit/webKitDebugAdapter.ts b/webkit/webKitDebugAdapter.ts index 7d94a60..bd5df18 100644 --- a/webkit/webKitDebugAdapter.ts +++ b/webkit/webKitDebugAdapter.ts @@ -411,7 +411,7 @@ export class WebKitDebugAdapter implements IDebugAdapter { // unverified breakpoints. if (response.error || !response.result.locations.length) { return { - verified: false, + verified: !response.error, line: requestLines[i], column: 0 }; From 8d4b3f4543bcf7c9f411ec0309e233bce0531e5d Mon Sep 17 00:00:00 2001 From: blagoev Date: Fri, 11 Mar 2016 16:58:27 +0200 Subject: [PATCH 3/5] fix support for ios platform in pathTransformer when setting breakpoints --- adapter/pathTransformer.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/adapter/pathTransformer.ts b/adapter/pathTransformer.ts index 1d9c008..97edd13 100644 --- a/adapter/pathTransformer.ts +++ b/adapter/pathTransformer.ts @@ -57,7 +57,16 @@ export class PathTransformer implements IDebugTransformer { else if (this.inferedDeviceRoot) { utils.Logger.log(`Paths.setBP: No target url cached for client path: ${url}. Using inffered device root to set breakpoint`); - let inferedUrl = url.replace(this._webRoot, this.inferedDeviceRoot).replace(/\\/g, "/"); + let inferedUrl = null; + if (this._platform == "android") { + inferedUrl = url.replace(this._webRoot, this.inferedDeviceRoot).replace(/\\/g, "/"); + } + else if (this._platform === "ios") { + inferedUrl = url.replace(this._webRoot, "").replace(/\\/g, "/"); + } + else { + throw new Error("Not supported platform"); + } //change device path if {N} core module or {N} module if (inferedUrl.indexOf("/node_modules/tns-core-modules/") != -1) @@ -103,7 +112,7 @@ export class PathTransformer implements IDebugTransformer { public scriptParsed(event: DebugProtocol.Event): void { const webkitUrl: string = event.body.scriptUrl; - if (!this.inferedDeviceRoot) + if (!this.inferedDeviceRoot && this._platform === "android") { this.inferedDeviceRoot = utils.inferDeviceRoot(this._webRoot, this._platform, webkitUrl); utils.Logger.log("\n\n\n ***Inferred device root: " + this.inferedDeviceRoot + "\n\n\n"); From 6cb858351db97c8bbb48f49855812f1448480515 Mon Sep 17 00:00:00 2001 From: blagoev Date: Fri, 11 Mar 2016 19:53:21 +0200 Subject: [PATCH 4/5] fix breakpoints for js files with no sourceMap --- adapter/sourceMaps/sourceMapTransformer.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/adapter/sourceMaps/sourceMapTransformer.ts b/adapter/sourceMaps/sourceMapTransformer.ts index 166371e..ef27204 100644 --- a/adapter/sourceMaps/sourceMapTransformer.ts +++ b/adapter/sourceMaps/sourceMapTransformer.ts @@ -45,8 +45,20 @@ export class SourceMapTransformer implements IDebugTransformer { this._authoredPathsToMappedBPLines = new Map(); this._authoredPathsToMappedBPCols = new Map(); } + + let stringProto: any = String.prototype; + if (!stringProto.endsWith) + { + stringProto.endsWith = function(str) + { + var lastIndex = this.lastIndexOf(str); + return (lastIndex !== -1) && (lastIndex + str.length === this.length); + } + } } + + public clearTargetContext(): void { this._allRuntimeScriptPaths = new Set(); } @@ -56,7 +68,7 @@ export class SourceMapTransformer implements IDebugTransformer { */ public setBreakpoints(args: ISetBreakpointsArgs, requestSeq: number): Promise { return new Promise((resolve, reject) => { - if (this._sourceMaps && args.source.path) { + if (this._sourceMaps && args.source.path && path.extname(args.source.path) !== ".js") { const argsPath = args.source.path; const mappedPath = this._sourceMaps.MapPathFromSource(argsPath); if (mappedPath) { From eba6ed01c5cf63bc9f2f62da70e829342377edf5 Mon Sep 17 00:00:00 2001 From: ivanbuhov Date: Fri, 18 Mar 2016 16:07:56 +0200 Subject: [PATCH 5/5] Add ios support --- adapter/pathTransformer.ts | 26 ++++------------------ adapter/sourceMaps/sourceMapTransformer.ts | 12 ---------- webkit/utilities.ts | 4 +--- 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/adapter/pathTransformer.ts b/adapter/pathTransformer.ts index 97edd13..198814d 100644 --- a/adapter/pathTransformer.ts +++ b/adapter/pathTransformer.ts @@ -27,6 +27,7 @@ export class PathTransformer implements IDebugTransformer { public launch(args: ILaunchRequestArgs): void { this._webRoot = utils.getAppRoot(args); this._platform = args.platform; + this.inferedDeviceRoot = (this._platform === 'ios') ? 'file://' : this.inferedDeviceRoot; } public attach(args: IAttachRequestArgs): void { @@ -55,18 +56,7 @@ export class PathTransformer implements IDebugTransformer { resolve(); } else if (this.inferedDeviceRoot) { - utils.Logger.log(`Paths.setBP: No target url cached for client path: ${url}. Using inffered device root to set breakpoint`); - - let inferedUrl = null; - if (this._platform == "android") { - inferedUrl = url.replace(this._webRoot, this.inferedDeviceRoot).replace(/\\/g, "/"); - } - else if (this._platform === "ios") { - inferedUrl = url.replace(this._webRoot, "").replace(/\\/g, "/"); - } - else { - throw new Error("Not supported platform"); - } + let inferedUrl = url.replace(this._webRoot, this.inferedDeviceRoot).replace(/\\/g, "/"); //change device path if {N} core module or {N} module if (inferedUrl.indexOf("/node_modules/tns-core-modules/") != -1) @@ -79,18 +69,10 @@ export class PathTransformer implements IDebugTransformer { } //change platform specific paths - if (inferedUrl.indexOf(".android.") != -1) - { - inferedUrl = inferedUrl.replace(".android.", "."); - } - else if (inferedUrl.indexOf(".ios.") != -1) - { - inferedUrl = inferedUrl.replace(".ios.", "."); - } + inferedUrl = inferedUrl.replace(`.${this._platform}.`, '.'); args.source.path = inferedUrl; - this._pendingBreakpointsByPath.set(args.source.path, { resolve, reject, args }); - utils.Logger.log("resolving infered promise on path " + url); + utils.Logger.log(`Paths.setBP: Resolved (by infering) ${url} to ${args.source.path}`); resolve(); } else { diff --git a/adapter/sourceMaps/sourceMapTransformer.ts b/adapter/sourceMaps/sourceMapTransformer.ts index ef27204..55cb021 100644 --- a/adapter/sourceMaps/sourceMapTransformer.ts +++ b/adapter/sourceMaps/sourceMapTransformer.ts @@ -45,20 +45,8 @@ export class SourceMapTransformer implements IDebugTransformer { this._authoredPathsToMappedBPLines = new Map(); this._authoredPathsToMappedBPCols = new Map(); } - - let stringProto: any = String.prototype; - if (!stringProto.endsWith) - { - stringProto.endsWith = function(str) - { - var lastIndex = this.lastIndexOf(str); - return (lastIndex !== -1) && (lastIndex + str.length === this.length); - } - } } - - public clearTargetContext(): void { this._allRuntimeScriptPaths = new Set(); } diff --git a/webkit/utilities.ts b/webkit/utilities.ts index 5ff370a..5db388c 100644 --- a/webkit/utilities.ts +++ b/webkit/utilities.ts @@ -261,7 +261,6 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return nsProjectFile; } - let shiftedParts = []; let pathParts = pathName.split(path.sep); while (pathParts.length > 0) { const clientPath = path.join(webRoot, pathParts.join(path.sep)); @@ -269,8 +268,7 @@ export function webkitUrlToClientPath(webRoot: string, additionalFileExtension: return canonicalizeUrl(clientPath); } - let shifted = pathParts.shift(); - shiftedParts.push(shifted); + pathParts.shift(); } //check for {N} android internal files