| 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 |