Chromium Code Reviews| 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 24 matching lines...) Expand all Loading... | |
| 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(new DebuggerLocation.error( |
| 42 "Invalid source location '${locDesc}'")); | 42 "Invalid source location '${locDesc}'")); |
| 43 } | 43 } |
| 44 | 44 |
| 45 static Future<DebuggerLocation> _currentLocation(Debugger debugger) { | 45 static Future<ServiceMap> _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 new Future.value(new DebuggerLocation.error( | 48 return null; |
| 49 'A script must be provided when the stack is empty')); | |
| 50 } | 49 } |
| 51 var frame = stack['frames'][debugger.currentFrame]; | 50 return stack['frames'][debugger.currentFrame]; |
| 51 } | |
| 52 | |
| 53 static Future<DebuggerLocation> _currentLocation(Debugger debugger) async { | |
| 54 var frame = await _currentFrame(debugger); | |
| 55 if (frame == null) { | |
| 56 return new DebuggerLocation.error( | |
| 57 'A script must be provided when the stack is empty'); | |
| 58 } | |
| 52 Script script = frame.location.script; | 59 Script script = frame.location.script; |
| 53 return script.load().then((_) { | 60 await script.load(); |
| 54 var line = script.tokenToLine(frame.location.tokenPos); | 61 var line = script.tokenToLine(frame.location.tokenPos); |
| 55 var col = script.tokenToCol(frame.location.tokenPos); | 62 var col = script.tokenToCol(frame.location.tokenPos); |
| 56 return new Future.value(new DebuggerLocation.file(script, line, col)); | 63 return new DebuggerLocation.file(script, line, col); |
| 57 }); | |
| 58 } | 64 } |
| 59 | 65 |
| 60 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger, | 66 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger, |
| 61 Match match) { | 67 Match match) async { |
| 62 var scriptName = match.group(1); | 68 var scriptName = match.group(1); |
| 63 if (scriptName != null) { | 69 if (scriptName != null) { |
| 64 scriptName = scriptName.substring(0, scriptName.length - 1); | 70 scriptName = scriptName.substring(0, scriptName.length - 1); |
| 65 } | 71 } |
| 66 var lineStr = match.group(2); | 72 var lineStr = match.group(2); |
| 67 assert(lineStr != null); | 73 assert(lineStr != null); |
| 68 var colStr = match.group(3); | 74 var colStr = match.group(3); |
| 69 if (colStr != null) { | 75 if (colStr != null) { |
| 70 colStr = colStr.substring(1); | 76 colStr = colStr.substring(1); |
| 71 } | 77 } |
| 72 var line = int.parse(lineStr, onError:(_) => -1); | 78 var line = int.parse(lineStr, onError:(_) => -1); |
| 73 var col = (colStr != null | 79 var col = (colStr != null |
| 74 ? int.parse(colStr, onError:(_) => -1) | 80 ? int.parse(colStr, onError:(_) => -1) |
| 75 : null); | 81 : null); |
| 76 if (line == -1) { | 82 if (line == -1) { |
| 77 return new Future.value(new DebuggerLocation.error( | 83 return new Future.value(new DebuggerLocation.error( |
| 78 "Line '${lineStr}' must be an integer")); | 84 "Line '${lineStr}' must be an integer")); |
| 79 } | 85 } |
| 80 if (col == -1) { | 86 if (col == -1) { |
| 81 return new Future.value(new DebuggerLocation.error( | 87 return new Future.value(new DebuggerLocation.error( |
| 82 "Column '${colStr}' must be an integer")); | 88 "Column '${colStr}' must be an integer")); |
| 83 } | 89 } |
| 84 | 90 |
| 85 if (scriptName != null) { | 91 if (scriptName != null) { |
| 86 // Resolve the script. | 92 // Resolve the script. |
| 87 return _lookupScript(debugger.isolate, scriptName).then((scripts) { | 93 var scripts = await _lookupScript(debugger.isolate, scriptName); |
| 88 if (scripts.length == 0) { | 94 if (scripts.length == 0) { |
| 89 return new DebuggerLocation.error("Script '${scriptName}' not found"); | 95 return new DebuggerLocation.error("Script '${scriptName}' not found"); |
| 90 } else if (scripts.length == 1) { | 96 } else if (scripts.length == 1) { |
| 91 return new DebuggerLocation.file(scripts[0], line, col); | 97 return new DebuggerLocation.file(scripts[0], line, col); |
| 92 } else { | 98 } else { |
| 93 // TODO(turnidge): Allow the user to disambiguate. | 99 // TODO(turnidge): Allow the user to disambiguate. |
| 94 return new DebuggerLocation.error("Script '${scriptName}' is ambigous" ); | 100 return new DebuggerLocation.error("Script '${scriptName}' is ambigous"); |
| 95 } | 101 } |
| 96 }); | |
| 97 } else { | 102 } else { |
| 98 // No script provided. Default to top of stack for now. | 103 // No script provided. Default to top of stack for now. |
| 99 ServiceMap stack = debugger.stack; | 104 var frame = await _currentFrame(debugger); |
| 100 if (stack == null || stack['frames'].length == 0) { | 105 if (frame == null) { |
| 101 return new Future.value(new DebuggerLocation.error( | 106 return new Future.value(new DebuggerLocation.error( |
| 102 'A script must be provided when the stack is empty')); | 107 'A script must be provided when the stack is empty')); |
| 103 } | 108 } |
| 104 var frame = stack['frames'][debugger.currentFrame]; | |
| 105 Script script = frame.location.script; | 109 Script script = frame.location.script; |
| 106 return script.load().then((_) { | 110 await script.load(); |
| 107 return new Future.value(new DebuggerLocation.file(script, line, col)); | 111 return new DebuggerLocation.file(script, line, col); |
| 108 }); | |
| 109 } | 112 } |
| 110 } | 113 } |
| 111 | 114 |
| 112 static Future<List<Script>> _lookupScript(Isolate isolate, | 115 static Future<List<Script>> _lookupScript(Isolate isolate, |
| 113 String name, | 116 String name, |
| 114 {bool allowPrefix: false}) { | 117 {bool allowPrefix: false}) { |
| 115 var pending = []; | 118 var pending = []; |
| 116 for (var lib in isolate.libraries) { | 119 for (var lib in isolate.libraries) { |
| 117 if (!lib.loaded) { | 120 if (!lib.loaded) { |
| 118 pending.add(lib.load()); | 121 pending.add(lib.load()); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 } | 157 } |
| 155 } | 158 } |
| 156 } | 159 } |
| 157 } | 160 } |
| 158 return matches; | 161 return matches; |
| 159 } | 162 } |
| 160 | 163 |
| 161 static Future<List<Class>> _lookupClass(Isolate isolate, | 164 static Future<List<Class>> _lookupClass(Isolate isolate, |
| 162 String name, | 165 String name, |
| 163 { bool allowPrefix: false }) { | 166 { bool allowPrefix: false }) { |
| 167 if (isolate == null) { | |
| 168 return []; | |
| 169 } | |
| 164 var pending = []; | 170 var pending = []; |
| 165 for (var lib in isolate.libraries) { | 171 for (var lib in isolate.libraries) { |
| 166 assert(lib.loaded); | 172 assert(lib.loaded); |
| 167 for (var cls in lib.classes) { | 173 for (var cls in lib.classes) { |
| 168 if (!cls.loaded) { | 174 if (!cls.loaded) { |
| 169 pending.add(cls.load()); | 175 pending.add(cls.load()); |
| 170 } | 176 } |
| 171 } | 177 } |
| 172 } | 178 } |
| 173 return Future.wait(pending).then((_) { | 179 return Future.wait(pending).then((_) { |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 } else { | 254 } else { |
| 249 // TODO(turnidge): Allow the user to disambiguate. | 255 // TODO(turnidge): Allow the user to disambiguate. |
| 250 return new DebuggerLocation.error( | 256 return new DebuggerLocation.error( |
| 251 "Function '${match.group(0)}' is ambigous"); | 257 "Function '${match.group(0)}' is ambigous"); |
| 252 } | 258 } |
| 253 return new DebuggerLocation.error('foo'); | 259 return new DebuggerLocation.error('foo'); |
| 254 }); | 260 }); |
| 255 } | 261 } |
| 256 | 262 |
| 257 static RegExp partialSourceLocMatcher = | 263 static RegExp partialSourceLocMatcher = |
| 258 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?'); | 264 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d*)?'); |
| 259 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); | 265 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); |
| 260 | 266 |
| 261 /// Completes a partial source location description. | 267 /// Completes a partial source location description. |
| 262 static Future<List<String>> complete(Debugger debugger, String locDesc) { | 268 static Future<List<String>> complete(Debugger debugger, String locDesc) { |
| 263 List<Future<List<String>>> pending = []; | 269 List<Future<List<String>>> pending = []; |
| 264 var match = partialFunctionMatcher.firstMatch(locDesc); | 270 var match = partialFunctionMatcher.firstMatch(locDesc); |
| 265 if (match != null) { | 271 if (match != null) { |
| 266 pending.add(_completeFunction(debugger, match)); | 272 pending.add(_completeFunction(debugger, match)); |
| 267 } | 273 } |
| 268 | 274 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 319 } | 325 } |
| 320 } | 326 } |
| 321 } | 327 } |
| 322 } | 328 } |
| 323 completions.sort(); | 329 completions.sort(); |
| 324 return completions; | 330 return completions; |
| 325 }); | 331 }); |
| 326 } | 332 } |
| 327 } | 333 } |
| 328 | 334 |
| 329 static Future<List<String>> _completeFile(Debugger debugger, Match match) { | 335 static bool _startsWithDigit(String s) { |
| 330 var scriptName = match.group(1); | 336 return '0'.compareTo(s[0]) <= 0 && '9'.compareTo(s[0]) >= 0; |
| 331 var lineStr = match.group(2); | 337 } |
| 332 var colStr = match.group(3); | 338 |
| 333 if (lineStr != null || colStr != null) { | 339 static Future<List<String>> _completeFile( |
| 334 // TODO(turnidge): Complete valid line and column numbers. | 340 Debugger debugger, Match match) async { |
| 335 return new Future.value([]); | 341 var scriptName; |
| 342 var scriptNameComplete = false; | |
| 343 var lineStr; | |
| 344 var lineStrComplete = false; | |
| 345 var colStr; | |
| 346 if (_startsWithDigit(match.group(1))) { | |
| 347 // CASE 1: We have matched a prefix of (lineStr:)(colStr) | |
| 348 var frame = await _currentFrame(debugger); | |
| 349 if (frame == null) { | |
| 350 return []; | |
| 351 } | |
| 352 scriptName = frame.location.script.name; | |
| 353 scriptNameComplete = true; | |
| 354 lineStr = match.group(1); | |
| 355 lineStr = (lineStr == null ? '' : lineStr); | |
|
Cutch
2015/10/06 18:08:16
lineStr ?= '';
turnidge
2015/10/06 18:33:37
Done.
| |
| 356 if (lineStr.endsWith(':')) { | |
| 357 lineStr = lineStr.substring(0, lineStr.length - 1); | |
| 358 lineStrComplete = true; | |
| 359 } | |
| 360 colStr = match.group(2); | |
| 361 colStr = (colStr == null ? '' : colStr); | |
|
Cutch
2015/10/06 18:08:16
colStr ?= '';
turnidge
2015/10/06 18:33:37
Done.
| |
| 362 } else { | |
| 363 // CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr) | |
| 364 scriptName = match.group(1); | |
| 365 scriptName = (scriptName == null ? '' : scriptName); | |
| 366 if (scriptName.endsWith(':')) { | |
|
Cutch
2015/10/06 18:08:16
Not necessarily an issue for the current CL but co
turnidge
2015/10/06 18:33:37
True. Currently the script name that we use is ju
| |
| 367 scriptName = scriptName.substring(0, scriptName.length - 1); | |
| 368 scriptNameComplete = true; | |
| 369 } | |
| 370 lineStr = match.group(2); | |
| 371 lineStr = (lineStr == null ? '' : lineStr); | |
| 372 colStr = match.group(3); | |
| 373 colStr = (colStr == null ? '' : colStr); | |
| 374 if (colStr.startsWith(':')) { | |
| 375 lineStrComplete = true; | |
| 376 colStr = colStr.substring(1); | |
| 377 } | |
| 336 } | 378 } |
| 337 scriptName = (scriptName == null ? '' : scriptName); | |
| 338 | 379 |
| 339 return _lookupScript(debugger.isolate, scriptName, allowPrefix:true) | 380 if (!scriptNameComplete) { |
| 340 .then((scripts) { | 381 // The script name is incomplete. Complete it. |
| 382 var scripts = | |
| 383 await _lookupScript(debugger.isolate, scriptName, allowPrefix:true); | |
| 384 List completions = []; | |
| 385 for (var script in scripts) { | |
| 386 completions.add(script.name + ':'); | |
| 387 } | |
| 388 completions.sort(); | |
| 389 return completions; | |
| 390 | |
| 391 } else { | |
| 392 // The script name is complete. Look it up. | |
| 393 var scripts = | |
| 394 await _lookupScript(debugger.isolate, scriptName, allowPrefix:false); | |
| 395 if (scripts.isEmpty) { | |
| 396 return []; | |
| 397 } | |
| 398 var script = scripts[0]; | |
| 399 await script.load(); | |
| 400 if (!lineStrComplete) { | |
| 401 // Complete the line. | |
| 402 var sharedPrefix = '${script.name}:'; | |
| 341 List completions = []; | 403 List completions = []; |
| 342 for (var script in scripts) { | 404 for (var line in script.lines) { |
| 343 completions.add(script.name + ':'); | 405 if (line.possibleBpt) { |
| 406 var currentLineStr = line.line.toString(); | |
| 407 if (currentLineStr.startsWith(lineStr)) { | |
| 408 completions.add('${sharedPrefix}${currentLineStr} '); | |
| 409 completions.add('${sharedPrefix}${currentLineStr}:'); | |
| 410 } | |
| 411 } | |
| 344 } | 412 } |
| 345 completions.sort(); | |
| 346 return completions; | 413 return completions; |
| 347 }); | 414 |
| 415 } else { | |
| 416 // Complete the column. | |
| 417 int lineNum = int.parse(lineStr); | |
| 418 var scriptLine = script.getLine(lineNum); | |
| 419 if (!scriptLine.possibleBpt) { | |
| 420 return []; | |
| 421 } | |
| 422 var sharedPrefix = '${script.name}:${lineStr}:'; | |
| 423 List completions = []; | |
| 424 int maxCol = scriptLine.text.runes.length; | |
| 425 for (int i = 1; i <= maxCol; i++) { | |
| 426 var currentColStr = i.toString(); | |
| 427 if (currentColStr.startsWith(colStr)) { | |
| 428 completions.add('${sharedPrefix}${currentColStr} '); | |
| 429 } | |
| 430 } | |
| 431 return completions; | |
| 432 } | |
| 433 } | |
| 348 } | 434 } |
| 349 | 435 |
| 350 String toString() { | 436 String toString() { |
| 351 if (valid) { | 437 if (valid) { |
| 352 if (function != null) { | 438 if (function != null) { |
| 353 return '${function.qualifiedName}'; | 439 return '${function.qualifiedName}'; |
| 354 } else if (col != null) { | 440 } else if (col != null) { |
| 355 return '${script.name}:${line}:${col}'; | 441 return '${script.name}:${line}:${col}'; |
| 356 } else { | 442 } else { |
| 357 return '${script.name}:${line}'; | 443 return '${script.name}:${line}'; |
| 358 } | 444 } |
| 359 } | 445 } |
| 360 return 'invalid source location (${errorMessage})'; | 446 return 'invalid source location (${errorMessage})'; |
| 361 } | 447 } |
| 362 | 448 |
| 363 Script script; | 449 Script script; |
| 364 int line; | 450 int line; |
| 365 int col; | 451 int col; |
| 366 ServiceFunction function; | 452 ServiceFunction function; |
| 367 String errorMessage; | 453 String errorMessage; |
| 368 bool get valid => (errorMessage == null); | 454 bool get valid => (errorMessage == null); |
| 369 } | 455 } |
| OLD | NEW |