-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathpathTransformer.ts
145 lines (125 loc) · 6.37 KB
/
pathTransformer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as utils from '../utilities';
import {DebugProtocol} from 'vscode-debugprotocol';
import * as path from 'path';
import {ISetBreakpointsArgs, IDebugTransformer, ILaunchRequestArgs, IAttachRequestArgs, IStackTraceResponseBody} from '../WebKitAdapterInterfaces';
interface IPendingBreakpoint {
resolve: () => void;
reject: (e: Error) => void;
args: ISetBreakpointsArgs;
}
/**
* Converts a local path from Code to a path on the target.
*/
export class PathTransformer implements IDebugTransformer {
private _webRoot: string;
private _platform: string;
private _clientPathToWebkitUrl = new Map<string, string>();
private _webkitUrlToClientPath = new Map<string, string>();
private _pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
private inferedDeviceRoot :string = null;
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 {
this._webRoot = utils.getAppRoot(args);
this._platform = args.platform;
}
public setBreakpoints(args: ISetBreakpointsArgs): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (!args.source.path) {
resolve();
return;
}
if (utils.isURL(args.source.path)) {
// already a url, use as-is
utils.Logger.log(`Paths.setBP: ${args.source.path} is already a URL`);
resolve();
return;
}
const url = utils.canonicalizeUrl(args.source.path);
if (this._clientPathToWebkitUrl.has(url)) {
args.source.path = this._clientPathToWebkitUrl.get(url);
utils.Logger.log(`Paths.setBP: Resolved ${url} to ${args.source.path}`);
resolve();
}
else if (this.inferedDeviceRoot) {
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
inferedUrl = inferedUrl.replace(`.${this._platform}.`, '.');
args.source.path = inferedUrl;
utils.Logger.log(`Paths.setBP: Resolved (by infering) ${url} to ${args.source.path}`);
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 });
}
});
}
public clearClientContext(): void {
this._pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
}
public clearTargetContext(): void {
this._clientPathToWebkitUrl = new Map<string, string>();
this._webkitUrlToClientPath = new Map<string, string>();
}
public scriptParsed(event: DebugProtocol.Event): void {
const webkitUrl: string = event.body.scriptUrl;
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");
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) {
utils.Logger.log(`Paths.scriptParsed: could not resolve ${webkitUrl} to a file in the workspace. webRoot: ${this._webRoot}`);
} else {
utils.Logger.log(`Paths.scriptParsed: resolved ${webkitUrl} to ${clientPath}. webRoot: ${this._webRoot}`);
this._clientPathToWebkitUrl.set(clientPath, webkitUrl);
this._webkitUrlToClientPath.set(webkitUrl, clientPath);
event.body.scriptUrl = clientPath;
}
if (this._pendingBreakpointsByPath.has(event.body.scriptUrl)) {
utils.Logger.log(`Paths.scriptParsed: Resolving pending breakpoints for ${event.body.scriptUrl}`);
const pendingBreakpoint = this._pendingBreakpointsByPath.get(event.body.scriptUrl);
this._pendingBreakpointsByPath.delete(event.body.scriptUrl);
this.setBreakpoints(pendingBreakpoint.args).then(pendingBreakpoint.resolve, pendingBreakpoint.reject);
}
}
public stackTraceResponse(response: IStackTraceResponseBody): void {
response.stackFrames.forEach(frame => {
// Try to resolve the url to a path in the workspace. If it's not in the workspace,
// just use the script.url as-is. It will be resolved or cleared by the SourceMapTransformer.
if (frame.source && frame.source.path) {
const clientPath = this._webkitUrlToClientPath.has(frame.source.path) ?
this._webkitUrlToClientPath.get(frame.source.path) :
utils.webkitUrlToClientPath(this._webRoot, this._platform, frame.source.path);
// Incoming stackFrames have sourceReference and path set. If the path was resolved to a file in the workspace,
// clear the sourceReference since it's not needed.
if (clientPath) {
frame.source.path = clientPath;
frame.source.sourceReference = 0;
}
}
});
}
}