| 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 SourceLocation { | 7 class DebuggerLocation { |
| 8 SourceLocation.file(this.script, this.line, this.col); | 8 DebuggerLocation.file(this.script, this.line, this.col); |
| 9 SourceLocation.func(this.function); | 9 DebuggerLocation.func(this.function); |
| 10 SourceLocation.error(this.errorMessage); | 10 DebuggerLocation.error(this.errorMessage); |
| 11 | 11 |
| 12 static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?'); | 12 static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?'); |
| 13 static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?'); | 13 static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?'); |
| 14 | 14 |
| 15 /// Parses a source location description. | 15 /// Parses a source location description. |
| 16 /// | 16 /// |
| 17 /// Formats: | 17 /// Formats: |
| 18 /// '' - current position | 18 /// '' - current position |
| 19 /// 13 - line 13, current script | 19 /// 13 - line 13, current script |
| 20 /// 13:20 - line 13, col 20, current script | 20 /// 13:20 - line 13, col 20, current script |
| 21 /// script.dart:13 - line 13, script.dart | 21 /// script.dart:13 - line 13, script.dart |
| 22 /// script.dart:13:20 - line 13, col 20, script.dart | 22 /// script.dart:13:20 - line 13, col 20, script.dart |
| 23 /// main - function | 23 /// main - function |
| 24 /// FormatException - constructor | 24 /// FormatException - constructor |
| 25 /// _SHA1._updateHash - method | 25 /// _SHA1._updateHash - method |
| 26 static Future<SourceLocation> parse(Debugger debugger, String locDesc) { | 26 static Future<DebuggerLocation> parse(Debugger debugger, String locDesc) { |
| 27 if (locDesc == '') { | 27 if (locDesc == '') { |
| 28 // Special case: '' means return current location. | 28 // Special case: '' means return current location. |
| 29 return _currentLocation(debugger); | 29 return _currentLocation(debugger); |
| 30 } | 30 } |
| 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 SourceLocation.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<SourceLocation> _currentLocation(Debugger debugger) { | 45 static Future<DebuggerLocation> _currentLocation(Debugger debugger) { |
| 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 SourceLocation.error( | 48 return new Future.value(new DebuggerLocation.error( |
| 49 'A script must be provided when the stack is empty')); | 49 'A script must be provided when the stack is empty')); |
| 50 } | 50 } |
| 51 var frame = stack['frames'][debugger.currentFrame]; | 51 var frame = stack['frames'][debugger.currentFrame]; |
| 52 Script script = frame['script']; | 52 Script script = frame.location.script; |
| 53 return script.load().then((_) { | 53 return script.load().then((_) { |
| 54 var line = script.tokenToLine(frame['tokenPos']); | 54 var line = script.tokenToLine(frame.location.tokenPos); |
| 55 // TODO(turnidge): Pass in the column here once the protocol supports it. | 55 // TODO(turnidge): Pass in the column here once the protocol supports it. |
| 56 return new Future.value(new SourceLocation.file(script, line, null)); | 56 return new Future.value(new DebuggerLocation.file(script, line, null)); |
| 57 }); | 57 }); |
| 58 } | 58 } |
| 59 | 59 |
| 60 static Future<SourceLocation> _parseScriptLine(Debugger debugger, | 60 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger, |
| 61 Match match) { | 61 Match match) { |
| 62 var scriptName = match.group(1); | 62 var scriptName = match.group(1); |
| 63 if (scriptName != null) { | 63 if (scriptName != null) { |
| 64 scriptName = scriptName.substring(0, scriptName.length - 1); | 64 scriptName = scriptName.substring(0, scriptName.length - 1); |
| 65 } | 65 } |
| 66 var lineStr = match.group(2); | 66 var lineStr = match.group(2); |
| 67 assert(lineStr != null); | 67 assert(lineStr != null); |
| 68 var colStr = match.group(3); | 68 var colStr = match.group(3); |
| 69 if (colStr != null) { | 69 if (colStr != null) { |
| 70 colStr = colStr.substring(1); | 70 colStr = colStr.substring(1); |
| 71 } | 71 } |
| 72 var line = int.parse(lineStr, onError:(_) => -1); | 72 var line = int.parse(lineStr, onError:(_) => -1); |
| 73 var col = (colStr != null | 73 var col = (colStr != null |
| 74 ? int.parse(colStr, onError:(_) => -1) | 74 ? int.parse(colStr, onError:(_) => -1) |
| 75 : null); | 75 : null); |
| 76 if (line == -1) { | 76 if (line == -1) { |
| 77 return new Future.value(new SourceLocation.error( | 77 return new Future.value(new DebuggerLocation.error( |
| 78 "Line '${lineStr}' must be an integer")); | 78 "Line '${lineStr}' must be an integer")); |
| 79 } | 79 } |
| 80 if (col == -1) { | 80 if (col == -1) { |
| 81 return new Future.value(new SourceLocation.error( | 81 return new Future.value(new DebuggerLocation.error( |
| 82 "Column '${colStr}' must be an integer")); | 82 "Column '${colStr}' must be an integer")); |
| 83 } | 83 } |
| 84 | 84 |
| 85 if (scriptName != null) { | 85 if (scriptName != null) { |
| 86 // Resolve the script. | 86 // Resolve the script. |
| 87 return _lookupScript(debugger.isolate, scriptName).then((scripts) { | 87 return _lookupScript(debugger.isolate, scriptName).then((scripts) { |
| 88 if (scripts.length == 0) { | 88 if (scripts.length == 0) { |
| 89 return new SourceLocation.error("Script '${scriptName}' not found"); | 89 return new DebuggerLocation.error("Script '${scriptName}' not found"); |
| 90 } else if (scripts.length == 1) { | 90 } else if (scripts.length == 1) { |
| 91 return new SourceLocation.file(scripts[0], line, col); | 91 return new DebuggerLocation.file(scripts[0], line, col); |
| 92 } else { | 92 } else { |
| 93 // TODO(turnidge): Allow the user to disambiguate. | 93 // TODO(turnidge): Allow the user to disambiguate. |
| 94 return new SourceLocation.error("Script '${scriptName}' is ambigous"); | 94 return new DebuggerLocation.error("Script '${scriptName}' is ambigous"
); |
| 95 } | 95 } |
| 96 }); | 96 }); |
| 97 } else { | 97 } else { |
| 98 // No script provided. Default to top of stack for now. | 98 // No script provided. Default to top of stack for now. |
| 99 ServiceMap stack = debugger.stack; | 99 ServiceMap stack = debugger.stack; |
| 100 if (stack == null || stack['frames'].length == 0) { | 100 if (stack == null || stack['frames'].length == 0) { |
| 101 return new Future.value(new SourceLocation.error( | 101 return new Future.value(new DebuggerLocation.error( |
| 102 'A script must be provided when the stack is empty')); | 102 'A script must be provided when the stack is empty')); |
| 103 } | 103 } |
| 104 Script script = stack['frames'][0]['script']; | 104 Script script = stack['frames'][0].location.script; |
| 105 return new Future.value(new SourceLocation.file(script, line, col)); | 105 return new Future.value(new DebuggerLocation.file(script, line, col)); |
| 106 } | 106 } |
| 107 } | 107 } |
| 108 | 108 |
| 109 static Future<List<Script>> _lookupScript(Isolate isolate, | 109 static Future<List<Script>> _lookupScript(Isolate isolate, |
| 110 String name, | 110 String name, |
| 111 {bool allowPrefix: false}) { | 111 {bool allowPrefix: false}) { |
| 112 var pending = []; | 112 var pending = []; |
| 113 for (var lib in isolate.libraries) { | 113 for (var lib in isolate.libraries) { |
| 114 if (!lib.loaded) { | 114 if (!lib.loaded) { |
| 115 pending.add(lib.load()); | 115 pending.add(lib.load()); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 assert(cls.loaded); | 191 assert(cls.loaded); |
| 192 if (name == function.name) { | 192 if (name == function.name) { |
| 193 return function; | 193 return function; |
| 194 } | 194 } |
| 195 } | 195 } |
| 196 return null; | 196 return null; |
| 197 } | 197 } |
| 198 | 198 |
| 199 // TODO(turnidge): This does not handle named functions which are | 199 // TODO(turnidge): This does not handle named functions which are |
| 200 // inside of named functions, e.g. foo.bar.baz. | 200 // inside of named functions, e.g. foo.bar.baz. |
| 201 static Future<SourceLocation> _parseFunction(Debugger debugger, | 201 static Future<DebuggerLocation> _parseFunction(Debugger debugger, |
| 202 Match match) { | 202 Match match) { |
| 203 Isolate isolate = debugger.isolate; | 203 Isolate isolate = debugger.isolate; |
| 204 var base = match.group(1); | 204 var base = match.group(1); |
| 205 var qualifier = match.group(2); | 205 var qualifier = match.group(2); |
| 206 assert(base != null); | 206 assert(base != null); |
| 207 | 207 |
| 208 return _lookupClass(isolate, base).then((classes) { | 208 return _lookupClass(isolate, base).then((classes) { |
| 209 var functions = []; | 209 var functions = []; |
| 210 if (qualifier == null) { | 210 if (qualifier == null) { |
| 211 // Unqualified name is either a function or a constructor. | 211 // Unqualified name is either a function or a constructor. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 231 } | 231 } |
| 232 } else { | 232 } else { |
| 233 if (functionName == function.name) { | 233 if (functionName == function.name) { |
| 234 functions.add(function); | 234 functions.add(function); |
| 235 } | 235 } |
| 236 } | 236 } |
| 237 } | 237 } |
| 238 } | 238 } |
| 239 } | 239 } |
| 240 if (functions.length == 0) { | 240 if (functions.length == 0) { |
| 241 return new SourceLocation.error( | 241 return new DebuggerLocation.error( |
| 242 "Function '${match.group(0)}' not found"); | 242 "Function '${match.group(0)}' not found"); |
| 243 } else if (functions.length == 1) { | 243 } else if (functions.length == 1) { |
| 244 return new SourceLocation.func(functions[0]); | 244 return new DebuggerLocation.func(functions[0]); |
| 245 } else { | 245 } else { |
| 246 // TODO(turnidge): Allow the user to disambiguate. | 246 // TODO(turnidge): Allow the user to disambiguate. |
| 247 return new SourceLocation.error( | 247 return new DebuggerLocation.error( |
| 248 "Function '${match.group(0)}' is ambigous"); | 248 "Function '${match.group(0)}' is ambigous"); |
| 249 } | 249 } |
| 250 return new SourceLocation.error('foo'); | 250 return new DebuggerLocation.error('foo'); |
| 251 }); | 251 }); |
| 252 } | 252 } |
| 253 | 253 |
| 254 static RegExp partialSourceLocMatcher = | 254 static RegExp partialSourceLocMatcher = |
| 255 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?'); | 255 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?'); |
| 256 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); | 256 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); |
| 257 | 257 |
| 258 /// Completes a partial source location description. | 258 /// Completes a partial source location description. |
| 259 static Future<List<String>> complete(Debugger debugger, String locDesc) { | 259 static Future<List<String>> complete(Debugger debugger, String locDesc) { |
| 260 List<Future<List<String>>> pending = []; | 260 List<Future<List<String>>> pending = []; |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 357 return 'invalid source location (${errorMessage})'; | 357 return 'invalid source location (${errorMessage})'; |
| 358 } | 358 } |
| 359 | 359 |
| 360 Script script; | 360 Script script; |
| 361 int line; | 361 int line; |
| 362 int col; | 362 int col; |
| 363 ServiceFunction function; | 363 ServiceFunction function; |
| 364 String errorMessage; | 364 String errorMessage; |
| 365 bool get valid => (errorMessage == null); | 365 bool get valid => (errorMessage == null); |
| 366 } | 366 } |
| OLD | NEW |