OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of debugger; | 5 part of debugger; |
6 | 6 |
7 class DebuggerLocation { | 7 class DebuggerLocation { |
8 DebuggerLocation.file(this.script, this.line, this.col); | 8 DebuggerLocation.file(this.script, this.line, this.col); |
9 DebuggerLocation.func(this.function); | 9 DebuggerLocation.func(this.function); |
10 DebuggerLocation.error(this.errorMessage); | 10 DebuggerLocation.error(this.errorMessage); |
(...skipping 20 matching lines...) Expand all Loading... |
31 | 31 |
32 // Parse the location description. | 32 // Parse the location description. |
33 var match = sourceLocMatcher.firstMatch(locDesc); | 33 var match = sourceLocMatcher.firstMatch(locDesc); |
34 if (match != null) { | 34 if (match != null) { |
35 return _parseScriptLine(debugger, match); | 35 return _parseScriptLine(debugger, match); |
36 } | 36 } |
37 match = functionMatcher.firstMatch(locDesc); | 37 match = functionMatcher.firstMatch(locDesc); |
38 if (match != null) { | 38 if (match != null) { |
39 return _parseFunction(debugger, match); | 39 return _parseFunction(debugger, match); |
40 } | 40 } |
41 return new Future.value(new DebuggerLocation.error( | 41 return new Future.value( |
42 "Invalid source location '${locDesc}'")); | 42 new DebuggerLocation.error("Invalid source location '${locDesc}'")); |
43 } | 43 } |
44 | 44 |
45 static Future<Frame> _currentFrame(Debugger debugger) async { | 45 static Future<Frame> _currentFrame(Debugger debugger) async { |
46 ServiceMap stack = debugger.stack; | 46 ServiceMap stack = debugger.stack; |
47 if (stack == null || stack['frames'].length == 0) { | 47 if (stack == null || stack['frames'].length == 0) { |
48 return null; | 48 return null; |
49 } | 49 } |
50 return stack['frames'][debugger.currentFrame]; | 50 return stack['frames'][debugger.currentFrame]; |
51 } | 51 } |
52 | 52 |
53 static Future<DebuggerLocation> _currentLocation(Debugger debugger) async { | 53 static Future<DebuggerLocation> _currentLocation(Debugger debugger) async { |
54 var frame = await _currentFrame(debugger); | 54 var frame = await _currentFrame(debugger); |
55 if (frame == null) { | 55 if (frame == null) { |
56 return new DebuggerLocation.error( | 56 return new DebuggerLocation.error( |
57 'A script must be provided when the stack is empty'); | 57 'A script must be provided when the stack is empty'); |
58 } | 58 } |
59 Script script = frame.location.script; | 59 Script script = frame.location.script; |
60 await script.load(); | 60 await script.load(); |
61 var line = script.tokenToLine(frame.location.tokenPos); | 61 var line = script.tokenToLine(frame.location.tokenPos); |
62 var col = script.tokenToCol(frame.location.tokenPos); | 62 var col = script.tokenToCol(frame.location.tokenPos); |
63 return new DebuggerLocation.file(script, line, col); | 63 return new DebuggerLocation.file(script, line, col); |
64 } | 64 } |
65 | 65 |
66 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger, | 66 static Future<DebuggerLocation> _parseScriptLine( |
67 Match match) async { | 67 Debugger debugger, Match match) async { |
68 var scriptName = match.group(1); | 68 var scriptName = match.group(1); |
69 if (scriptName != null) { | 69 if (scriptName != null) { |
70 scriptName = scriptName.substring(0, scriptName.length - 1); | 70 scriptName = scriptName.substring(0, scriptName.length - 1); |
71 } | 71 } |
72 var lineStr = match.group(2); | 72 var lineStr = match.group(2); |
73 assert(lineStr != null); | 73 assert(lineStr != null); |
74 var colStr = match.group(3); | 74 var colStr = match.group(3); |
75 if (colStr != null) { | 75 if (colStr != null) { |
76 colStr = colStr.substring(1); | 76 colStr = colStr.substring(1); |
77 } | 77 } |
78 var line = int.parse(lineStr, onError:(_) => -1); | 78 var line = int.parse(lineStr, onError: (_) => -1); |
79 var col = (colStr != null | 79 var col = (colStr != null ? int.parse(colStr, onError: (_) => -1) : null); |
80 ? int.parse(colStr, onError:(_) => -1) | |
81 : null); | |
82 if (line == -1) { | 80 if (line == -1) { |
83 return new Future.value(new DebuggerLocation.error( | 81 return new Future.value( |
84 "Line '${lineStr}' must be an integer")); | 82 new DebuggerLocation.error("Line '${lineStr}' must be an integer")); |
85 } | 83 } |
86 if (col == -1) { | 84 if (col == -1) { |
87 return new Future.value(new DebuggerLocation.error( | 85 return new Future.value( |
88 "Column '${colStr}' must be an integer")); | 86 new DebuggerLocation.error("Column '${colStr}' must be an integer")); |
89 } | 87 } |
90 | 88 |
91 if (scriptName != null) { | 89 if (scriptName != null) { |
92 // Resolve the script. | 90 // Resolve the script. |
93 var scripts = await _lookupScript(debugger.isolate, scriptName); | 91 var scripts = await _lookupScript(debugger.isolate, scriptName); |
94 if (scripts.length == 0) { | 92 if (scripts.length == 0) { |
95 return new DebuggerLocation.error("Script '${scriptName}' not found"); | 93 return new DebuggerLocation.error("Script '${scriptName}' not found"); |
96 } else if (scripts.length == 1) { | 94 } else if (scripts.length == 1) { |
97 return new DebuggerLocation.file(scripts[0], line, col); | 95 return new DebuggerLocation.file(scripts[0], line, col); |
98 } else { | 96 } else { |
99 // TODO(turnidge): Allow the user to disambiguate. | 97 // TODO(turnidge): Allow the user to disambiguate. |
100 return | 98 return new DebuggerLocation.error( |
101 new DebuggerLocation.error("Script '${scriptName}' is ambiguous"); | 99 "Script '${scriptName}' is ambiguous"); |
102 } | 100 } |
103 } else { | 101 } else { |
104 // No script provided. Default to top of stack for now. | 102 // No script provided. Default to top of stack for now. |
105 var frame = await _currentFrame(debugger); | 103 var frame = await _currentFrame(debugger); |
106 if (frame == null) { | 104 if (frame == null) { |
107 return new Future.value(new DebuggerLocation.error( | 105 return new Future.value(new DebuggerLocation.error( |
108 'A script must be provided when the stack is empty')); | 106 'A script must be provided when the stack is empty')); |
109 } | 107 } |
110 Script script = frame.location.script; | 108 Script script = frame.location.script; |
111 await script.load(); | 109 await script.load(); |
112 return new DebuggerLocation.file(script, line, col); | 110 return new DebuggerLocation.file(script, line, col); |
113 } | 111 } |
114 } | 112 } |
115 | 113 |
116 static Future<List<Script>> _lookupScript(Isolate isolate, | 114 static Future<List<Script>> _lookupScript(Isolate isolate, String name, |
117 String name, | 115 {bool allowPrefix: false}) { |
118 {bool allowPrefix: false}) { | |
119 var pending = []; | 116 var pending = []; |
120 for (var lib in isolate.libraries) { | 117 for (var lib in isolate.libraries) { |
121 if (!lib.loaded) { | 118 if (!lib.loaded) { |
122 pending.add(lib.load()); | 119 pending.add(lib.load()); |
123 } | 120 } |
124 } | 121 } |
125 return Future.wait(pending).then((_) { | 122 return Future.wait(pending).then((_) { |
126 List matches = []; | 123 List matches = []; |
127 for (var lib in isolate.libraries) { | 124 for (var lib in isolate.libraries) { |
128 for (var script in lib.scripts) { | 125 for (var script in lib.scripts) { |
129 if (allowPrefix) { | 126 if (allowPrefix) { |
130 if (script.name.startsWith(name)) { | 127 if (script.name.startsWith(name)) { |
131 matches.add(script); | 128 matches.add(script); |
132 } | 129 } |
133 } else { | 130 } else { |
134 if (name == script.name) { | 131 if (name == script.name) { |
135 matches.add(script); | 132 matches.add(script); |
136 } | 133 } |
137 } | 134 } |
138 } | 135 } |
139 } | 136 } |
140 return matches; | 137 return matches; |
141 }); | 138 }); |
142 } | 139 } |
143 | 140 |
144 static List<ServiceFunction> _lookupFunction(Isolate isolate, | 141 static List<ServiceFunction> _lookupFunction(Isolate isolate, String name, |
145 String name, | 142 {bool allowPrefix: false}) { |
146 { bool allowPrefix: false }) { | |
147 var matches = []; | 143 var matches = []; |
148 for (var lib in isolate.libraries) { | 144 for (var lib in isolate.libraries) { |
149 assert(lib.loaded); | 145 assert(lib.loaded); |
150 for (var function in lib.functions) { | 146 for (var function in lib.functions) { |
151 if (allowPrefix) { | 147 if (allowPrefix) { |
152 if (function.name.startsWith(name)) { | 148 if (function.name.startsWith(name)) { |
153 matches.add(function); | 149 matches.add(function); |
154 } | 150 } |
155 } else { | 151 } else { |
156 if (name == function.name) { | 152 if (name == function.name) { |
157 matches.add(function); | 153 matches.add(function); |
158 } | 154 } |
159 } | 155 } |
160 } | 156 } |
161 } | 157 } |
162 return matches; | 158 return matches; |
163 } | 159 } |
164 | 160 |
165 static Future<List<Class>> _lookupClass(Isolate isolate, | 161 static Future<List<Class>> _lookupClass(Isolate isolate, String name, |
166 String name, | 162 {bool allowPrefix: false}) async { |
167 { bool allowPrefix: false }) async { | |
168 if (isolate == null) { | 163 if (isolate == null) { |
169 return []; | 164 return []; |
170 } | 165 } |
171 var pending = []; | 166 var pending = []; |
172 for (var lib in isolate.libraries) { | 167 for (var lib in isolate.libraries) { |
173 assert(lib.loaded); | 168 assert(lib.loaded); |
174 for (var cls in lib.classes) { | 169 for (var cls in lib.classes) { |
175 if (!cls.loaded) { | 170 if (!cls.loaded) { |
176 pending.add(cls.load()); | 171 pending.add(cls.load()); |
177 } | 172 } |
(...skipping 22 matching lines...) Expand all Loading... |
200 assert(cls.loaded); | 195 assert(cls.loaded); |
201 if (name == function.name) { | 196 if (name == function.name) { |
202 return function; | 197 return function; |
203 } | 198 } |
204 } | 199 } |
205 return null; | 200 return null; |
206 } | 201 } |
207 | 202 |
208 // TODO(turnidge): This does not handle named functions which are | 203 // TODO(turnidge): This does not handle named functions which are |
209 // inside of named functions, e.g. foo.bar.baz. | 204 // inside of named functions, e.g. foo.bar.baz. |
210 static Future<DebuggerLocation> _parseFunction(Debugger debugger, | 205 static Future<DebuggerLocation> _parseFunction( |
211 Match match) { | 206 Debugger debugger, Match match) { |
212 Isolate isolate = debugger.isolate; | 207 Isolate isolate = debugger.isolate; |
213 var base = match.group(1); | 208 var base = match.group(1); |
214 var qualifier = match.group(2); | 209 var qualifier = match.group(2); |
215 assert(base != null); | 210 assert(base != null); |
216 | 211 |
217 return _lookupClass(isolate, base).then((classes) { | 212 return _lookupClass(isolate, base).then((classes) { |
218 var functions = []; | 213 var functions = []; |
219 if (qualifier == null) { | 214 if (qualifier == null) { |
220 // Unqualified name is either a function or a constructor. | 215 // Unqualified name is either a function or a constructor. |
221 functions.addAll(_lookupFunction(isolate, base)); | 216 functions.addAll(_lookupFunction(isolate, base)); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 | 273 |
279 return Future.wait(pending).then((List<List<String>> responses) { | 274 return Future.wait(pending).then((List<List<String>> responses) { |
280 var completions = []; | 275 var completions = []; |
281 for (var response in responses) { | 276 for (var response in responses) { |
282 completions.addAll(response); | 277 completions.addAll(response); |
283 } | 278 } |
284 return completions; | 279 return completions; |
285 }); | 280 }); |
286 } | 281 } |
287 | 282 |
288 static Future<List<String>> _completeFunction(Debugger debugger, | 283 static Future<List<String>> _completeFunction( |
289 Match match) { | 284 Debugger debugger, Match match) { |
290 Isolate isolate = debugger.isolate; | 285 Isolate isolate = debugger.isolate; |
291 var base = match.group(1); | 286 var base = match.group(1); |
292 var qualifier = match.group(2); | 287 var qualifier = match.group(2); |
293 base = (base == null ? '' : base); | 288 base = (base == null ? '' : base); |
294 | 289 |
295 if (qualifier == null) { | 290 if (qualifier == null) { |
296 return _lookupClass(isolate, base, allowPrefix:true).then((classes) { | 291 return _lookupClass(isolate, base, allowPrefix: true).then((classes) { |
297 var completions = []; | 292 var completions = []; |
298 | 293 |
299 // Complete top-level function names. | 294 // Complete top-level function names. |
300 var functions = _lookupFunction(isolate, base, allowPrefix:true); | 295 var functions = _lookupFunction(isolate, base, allowPrefix: true); |
301 var funcNames = functions.map((f) => f.name).toList(); | 296 var funcNames = functions.map((f) => f.name).toList(); |
302 funcNames.sort(); | 297 funcNames.sort(); |
303 completions.addAll(funcNames); | 298 completions.addAll(funcNames); |
304 | 299 |
305 // Complete class names. | 300 // Complete class names. |
306 var classNames = classes.map((f) => f.name).toList(); | 301 var classNames = classes.map((f) => f.name).toList(); |
307 classNames.sort(); | 302 classNames.sort(); |
308 completions.addAll(classNames); | 303 completions.addAll(classNames); |
309 | 304 |
310 return completions; | 305 return completions; |
311 }); | 306 }); |
312 } else { | 307 } else { |
313 return _lookupClass(isolate, base, allowPrefix:false).then((classes) { | 308 return _lookupClass(isolate, base, allowPrefix: false).then((classes) { |
314 var completions = []; | 309 var completions = []; |
315 for (var cls in classes) { | 310 for (var cls in classes) { |
316 for (var function in cls.functions) { | 311 for (var function in cls.functions) { |
317 if (function.kind == M.FunctionKind.constructor) { | 312 if (function.kind == M.FunctionKind.constructor) { |
318 if (function.name.startsWith(match.group(0))) { | 313 if (function.name.startsWith(match.group(0))) { |
319 completions.add(function.name); | 314 completions.add(function.name); |
320 } | 315 } |
321 } else { | 316 } else { |
322 if (function.qualifiedName.startsWith(match.group(0))) { | 317 if (function.qualifiedName.startsWith(match.group(0))) { |
323 completions.add(function.qualifiedName); | 318 completions.add(function.qualifiedName); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 colStr = (colStr == null ? '' : colStr); | 367 colStr = (colStr == null ? '' : colStr); |
373 if (colStr.startsWith(':')) { | 368 if (colStr.startsWith(':')) { |
374 lineStrComplete = true; | 369 lineStrComplete = true; |
375 colStr = colStr.substring(1); | 370 colStr = colStr.substring(1); |
376 } | 371 } |
377 } | 372 } |
378 | 373 |
379 if (!scriptNameComplete) { | 374 if (!scriptNameComplete) { |
380 // The script name is incomplete. Complete it. | 375 // The script name is incomplete. Complete it. |
381 var scripts = | 376 var scripts = |
382 await _lookupScript(debugger.isolate, scriptName, allowPrefix:true); | 377 await _lookupScript(debugger.isolate, scriptName, allowPrefix: true); |
383 List completions = []; | 378 List completions = []; |
384 for (var script in scripts) { | 379 for (var script in scripts) { |
385 completions.add(script.name + ':'); | 380 completions.add(script.name + ':'); |
386 } | 381 } |
387 completions.sort(); | 382 completions.sort(); |
388 return completions; | 383 return completions; |
389 | |
390 } else { | 384 } else { |
391 // The script name is complete. Look it up. | 385 // The script name is complete. Look it up. |
392 var scripts = | 386 var scripts = |
393 await _lookupScript(debugger.isolate, scriptName, allowPrefix:false); | 387 await _lookupScript(debugger.isolate, scriptName, allowPrefix: false); |
394 if (scripts.isEmpty) { | 388 if (scripts.isEmpty) { |
395 return []; | 389 return []; |
396 } | 390 } |
397 var script = scripts[0]; | 391 var script = scripts[0]; |
398 await script.load(); | 392 await script.load(); |
399 if (!lineStrComplete) { | 393 if (!lineStrComplete) { |
400 // Complete the line. | 394 // Complete the line. |
401 var sharedPrefix = '${script.name}:'; | 395 var sharedPrefix = '${script.name}:'; |
402 List completions = []; | 396 List completions = []; |
403 var report = await script.isolate.getSourceReport( | 397 var report = await script.isolate |
404 [Isolate.kPossibleBreakpointsReport], script); | 398 .getSourceReport([Isolate.kPossibleBreakpointsReport], script); |
405 Set<int> possibleBpts = getPossibleBreakpointLines(report, script); | 399 Set<int> possibleBpts = getPossibleBreakpointLines(report, script); |
406 for (var line in possibleBpts) { | 400 for (var line in possibleBpts) { |
407 var currentLineStr = line.toString(); | 401 var currentLineStr = line.toString(); |
408 if (currentLineStr.startsWith(lineStr)) { | 402 if (currentLineStr.startsWith(lineStr)) { |
409 completions.add('${sharedPrefix}${currentLineStr} '); | 403 completions.add('${sharedPrefix}${currentLineStr} '); |
410 completions.add('${sharedPrefix}${currentLineStr}:'); | 404 completions.add('${sharedPrefix}${currentLineStr}:'); |
411 } | 405 } |
412 } | 406 } |
413 return completions; | 407 return completions; |
414 | |
415 } else { | 408 } else { |
416 // Complete the column. | 409 // Complete the column. |
417 int lineNum = int.parse(lineStr); | 410 int lineNum = int.parse(lineStr); |
418 var scriptLine = script.getLine(lineNum); | 411 var scriptLine = script.getLine(lineNum); |
419 var sharedPrefix = '${script.name}:${lineStr}:'; | 412 var sharedPrefix = '${script.name}:${lineStr}:'; |
420 List completions = []; | 413 List completions = []; |
421 int maxCol = scriptLine.text.trimRight().runes.length; | 414 int maxCol = scriptLine.text.trimRight().runes.length; |
422 for (int i = 1; i <= maxCol; i++) { | 415 for (int i = 1; i <= maxCol; i++) { |
423 var currentColStr = i.toString(); | 416 var currentColStr = i.toString(); |
424 if (currentColStr.startsWith(colStr)) { | 417 if (currentColStr.startsWith(colStr)) { |
(...skipping 18 matching lines...) Expand all Loading... |
443 return 'invalid source location (${errorMessage})'; | 436 return 'invalid source location (${errorMessage})'; |
444 } | 437 } |
445 | 438 |
446 Script script; | 439 Script script; |
447 int line; | 440 int line; |
448 int col; | 441 int col; |
449 ServiceFunction function; | 442 ServiceFunction function; |
450 String errorMessage; | 443 String errorMessage; |
451 bool get valid => (errorMessage == null); | 444 bool get valid => (errorMessage == null); |
452 } | 445 } |
OLD | NEW |