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<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 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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 153 matches.add(function); | 156 matches.add(function); |
| 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 }) async { |
| 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 await Future.wait(pending); |
| 174 var matches = []; | 180 var matches = []; |
| 175 for (var lib in isolate.libraries) { | 181 for (var lib in isolate.libraries) { |
| 176 for (var cls in lib.classes) { | 182 for (var cls in lib.classes) { |
| 177 if (allowPrefix) { | 183 if (allowPrefix) { |
| 178 if (cls.name.startsWith(name)) { | 184 if (cls.name.startsWith(name)) { |
| 179 matches.add(cls); | 185 matches.add(cls); |
| 180 } | 186 } |
| 181 } else { | 187 } else { |
| 182 if (name == cls.name) { | 188 if (name == cls.name) { |
| 183 matches.add(cls); | 189 matches.add(cls); |
| 184 } | |
| 185 } | 190 } |
| 186 } | 191 } |
| 187 } | 192 } |
| 188 return matches; | 193 } |
| 189 }); | 194 return matches; |
| 190 } | 195 } |
| 191 | 196 |
| 192 static ServiceFunction _getConstructor(Class cls, String name) { | 197 static ServiceFunction _getConstructor(Class cls, String name) { |
| 193 for (var function in cls.functions) { | 198 for (var function in cls.functions) { |
| 194 assert(cls.loaded); | 199 assert(cls.loaded); |
| 195 if (name == function.name) { | 200 if (name == function.name) { |
| 196 return function; | 201 return function; |
| 197 } | 202 } |
| 198 } | 203 } |
| 199 return null; | 204 return null; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 } else { | 253 } else { |
| 249 // TODO(turnidge): Allow the user to disambiguate. | 254 // TODO(turnidge): Allow the user to disambiguate. |
| 250 return new DebuggerLocation.error( | 255 return new DebuggerLocation.error( |
| 251 "Function '${match.group(0)}' is ambigous"); | 256 "Function '${match.group(0)}' is ambigous"); |
| 252 } | 257 } |
| 253 return new DebuggerLocation.error('foo'); | 258 return new DebuggerLocation.error('foo'); |
| 254 }); | 259 }); |
| 255 } | 260 } |
| 256 | 261 |
| 257 static RegExp partialSourceLocMatcher = | 262 static RegExp partialSourceLocMatcher = |
| 258 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?'); | 263 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d*)?'); |
| 259 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); | 264 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); |
| 260 | 265 |
| 261 /// Completes a partial source location description. | 266 /// Completes a partial source location description. |
| 262 static Future<List<String>> complete(Debugger debugger, String locDesc) { | 267 static Future<List<String>> complete(Debugger debugger, String locDesc) { |
| 263 List<Future<List<String>>> pending = []; | 268 List<Future<List<String>>> pending = []; |
| 264 var match = partialFunctionMatcher.firstMatch(locDesc); | 269 var match = partialFunctionMatcher.firstMatch(locDesc); |
| 265 if (match != null) { | 270 if (match != null) { |
| 266 pending.add(_completeFunction(debugger, match)); | 271 pending.add(_completeFunction(debugger, match)); |
| 267 } | 272 } |
| 268 | 273 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 319 } | 324 } |
| 320 } | 325 } |
| 321 } | 326 } |
| 322 } | 327 } |
| 323 completions.sort(); | 328 completions.sort(); |
| 324 return completions; | 329 return completions; |
| 325 }); | 330 }); |
| 326 } | 331 } |
| 327 } | 332 } |
| 328 | 333 |
| 329 static Future<List<String>> _completeFile(Debugger debugger, Match match) { | 334 static bool _startsWithDigit(String s) { |
| 330 var scriptName = match.group(1); | 335 return '0'.compareTo(s[0]) <= 0 && '9'.compareTo(s[0]) >= 0; |
| 331 var lineStr = match.group(2); | 336 } |
| 332 var colStr = match.group(3); | 337 |
| 333 if (lineStr != null || colStr != null) { | 338 static Future<List<String>> _completeFile( |
| 334 // TODO(turnidge): Complete valid line and column numbers. | 339 Debugger debugger, Match match) async { |
| 335 return new Future.value([]); | 340 var scriptName; |
| 341 var scriptNameComplete = false; | |
| 342 var lineStr; | |
| 343 var lineStrComplete = false; | |
| 344 var colStr; | |
| 345 if (_startsWithDigit(match.group(1))) { | |
| 346 // CASE 1: We have matched a prefix of (lineStr:)(colStr) | |
| 347 var frame = await _currentFrame(debugger); | |
| 348 if (frame == null) { | |
| 349 return []; | |
| 350 } | |
| 351 scriptName = frame.location.script.name; | |
| 352 scriptNameComplete = true; | |
| 353 lineStr = match.group(1); | |
| 354 lineStr = (lineStr == null ? '' : lineStr); | |
| 355 if (lineStr.endsWith(':')) { | |
| 356 lineStr = lineStr.substring(0, lineStr.length - 1); | |
| 357 lineStrComplete = true; | |
| 358 } | |
| 359 colStr = match.group(2); | |
| 360 colStr = (colStr == null ? '' : colStr); | |
| 361 } else { | |
| 362 // CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr) | |
| 363 scriptName = match.group(1); | |
| 364 scriptName = (scriptName == null ? '' : scriptName); | |
| 365 if (scriptName.endsWith(':')) { | |
| 366 scriptName = scriptName.substring(0, scriptName.length - 1); | |
| 367 scriptNameComplete = true; | |
| 368 } | |
| 369 lineStr = match.group(2); | |
| 370 lineStr = (lineStr == null ? '' : lineStr); | |
| 371 colStr = match.group(3); | |
| 372 colStr = (colStr == null ? '' : colStr); | |
| 373 if (colStr.startsWith(':')) { | |
| 374 lineStrComplete = true; | |
| 375 colStr = colStr.substring(1); | |
| 376 } | |
| 336 } | 377 } |
| 337 scriptName = (scriptName == null ? '' : scriptName); | |
| 338 | 378 |
| 339 return _lookupScript(debugger.isolate, scriptName, allowPrefix:true) | 379 if (!scriptNameComplete) { |
| 340 .then((scripts) { | 380 // The script name is incomplete. Complete it. |
| 381 var scripts = | |
| 382 await _lookupScript(debugger.isolate, scriptName, allowPrefix:true); | |
| 383 List completions = []; | |
| 384 for (var script in scripts) { | |
| 385 completions.add(script.name + ':'); | |
| 386 } | |
| 387 completions.sort(); | |
| 388 return completions; | |
| 389 | |
| 390 } else { | |
| 391 // The script name is complete. Look it up. | |
| 392 var scripts = | |
| 393 await _lookupScript(debugger.isolate, scriptName, allowPrefix:false); | |
| 394 if (scripts.isEmpty) { | |
| 395 return []; | |
| 396 } | |
| 397 var script = scripts[0]; | |
| 398 await script.load(); | |
| 399 if (!lineStrComplete) { | |
| 400 // Complete the line. | |
| 401 var sharedPrefix = '${script.name}:'; | |
| 341 List completions = []; | 402 List completions = []; |
| 342 for (var script in scripts) { | 403 for (var line in script.lines) { |
| 343 completions.add(script.name + ':'); | 404 if (line.possibleBpt) { |
| 405 var currentLineStr = line.line.toString(); | |
| 406 if (currentLineStr.startsWith(lineStr)) { | |
| 407 completions.add('${sharedPrefix}${currentLineStr} '); | |
| 408 completions.add('${sharedPrefix}${currentLineStr}:'); | |
| 409 } | |
| 410 } | |
| 344 } | 411 } |
| 345 completions.sort(); | |
| 346 return completions; | 412 return completions; |
| 347 }); | 413 |
| 414 } else { | |
| 415 // Complete the column. | |
| 416 int lineNum = int.parse(lineStr); | |
| 417 var scriptLine = script.getLine(lineNum); | |
| 418 if (!scriptLine.possibleBpt) { | |
| 419 return []; | |
| 420 } | |
| 421 var sharedPrefix = '${script.name}:${lineStr}:'; | |
| 422 List completions = []; | |
| 423 int maxCol = scriptLine.text.runes.length; | |
|
rmacnak
2015/10/07 18:03:09
DBC: It would be good to have a test with characte
| |
| 424 for (int i = 1; i <= maxCol; i++) { | |
| 425 var currentColStr = i.toString(); | |
| 426 if (currentColStr.startsWith(colStr)) { | |
| 427 completions.add('${sharedPrefix}${currentColStr} '); | |
| 428 } | |
| 429 } | |
| 430 return completions; | |
| 431 } | |
| 432 } | |
| 348 } | 433 } |
| 349 | 434 |
| 350 String toString() { | 435 String toString() { |
| 351 if (valid) { | 436 if (valid) { |
| 352 if (function != null) { | 437 if (function != null) { |
| 353 return '${function.qualifiedName}'; | 438 return '${function.qualifiedName}'; |
| 354 } else if (col != null) { | 439 } else if (col != null) { |
| 355 return '${script.name}:${line}:${col}'; | 440 return '${script.name}:${line}:${col}'; |
| 356 } else { | 441 } else { |
| 357 return '${script.name}:${line}'; | 442 return '${script.name}:${line}'; |
| 358 } | 443 } |
| 359 } | 444 } |
| 360 return 'invalid source location (${errorMessage})'; | 445 return 'invalid source location (${errorMessage})'; |
| 361 } | 446 } |
| 362 | 447 |
| 363 Script script; | 448 Script script; |
| 364 int line; | 449 int line; |
| 365 int col; | 450 int col; |
| 366 ServiceFunction function; | 451 ServiceFunction function; |
| 367 String errorMessage; | 452 String errorMessage; |
| 368 bool get valid => (errorMessage == null); | 453 bool get valid => (errorMessage == null); |
| 369 } | 454 } |
| OLD | NEW |