Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(546)

Unified Diff: runtime/observatory/lib/src/debugger/source_location.dart

Issue 918003002: Add 'break' and 'clear' commands to Observatory debugger. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/observatory/lib/src/debugger/source_location.dart
diff --git a/runtime/observatory/lib/src/debugger/source_location.dart b/runtime/observatory/lib/src/debugger/source_location.dart
new file mode 100644
index 0000000000000000000000000000000000000000..07ab3f6c00e3903c730a46e2351028b065138cf7
--- /dev/null
+++ b/runtime/observatory/lib/src/debugger/source_location.dart
@@ -0,0 +1,367 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of debugger;
+
+class SourceLocation {
+ SourceLocation.file(this.script, this.line, this.col);
+ SourceLocation.func(this.function);
+ SourceLocation.error(this.errorMessage);
+
+ static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?');
+ static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?');
+
+ /// Parses a source location description.
+ ///
+ /// Formats:
+ /// '' - current position
+ /// 13 - line 13, current script
+ /// 13:20 - line 13, col 20, current script
+ /// script.dart:13 - line 13, script.dart
+ /// script.dart:13:20 - line 13, col 20, script.dart
+ /// main - function
+ /// FormatException - constructor
+ /// _SHA1._updateHash - method
+ static Future<SourceLocation> parse(Debugger debugger, String locDesc) {
+ if (locDesc == '') {
+ // Special case: '' means return current location.
+ return _currentLocation(debugger);
+ }
+
+ // Parse the location description.
+ var match = sourceLocMatcher.firstMatch(locDesc);
+ if (match != null) {
+ return _parseScriptLine(debugger, match);
+ }
+ match = functionMatcher.firstMatch(locDesc);
+ if (match != null) {
+ return _parseFunction(debugger, match);
+ }
+ return new Future.value(new SourceLocation.error(
+ "Invalid source location '${locDesc}'"));
+ }
+
+ static Future<SourceLocation> _currentLocation(Debugger debugger) {
+ ServiceMap stack = debugger.stack;
+ if (stack == null || stack['frames'].length == 0) {
+ return new Future.value(new SourceLocation.error(
+ 'A script must be provided when the stack is empty'));
+ }
+ var frame = stack['frames'][0];
+ Script script = frame['script'];
+ return script.load().then((_) {
+ var line = script.tokenToLine(frame['tokenPos']);
+ // TODO(turnidge): Pass in the column here once the protocol supports it.
+ return new Future.value(new SourceLocation.file(script, line, null));
+ });
+ }
+
+ static Future<SourceLocation> _parseScriptLine(Debugger debugger,
+ Match match) {
+ var scriptName = match.group(1);
+ if (scriptName != null) {
+ scriptName = scriptName.substring(0, scriptName.length - 1);
+ }
+ var lineStr = match.group(2);
+ assert(lineStr != null);
+ var colStr = match.group(3);
+ if (colStr != null) {
+ colStr = colStr.substring(1);
+ }
+ var line = int.parse(lineStr, onError:(_) => -1);
+ var col = (colStr != null
+ ? int.parse(colStr, onError:(_) => -1)
+ : null);
+ if (line == -1) {
+ return new Future.value(new SourceLocation.error(
+ "Line '${lineStr}' must be an integer"));
+ }
+ if (col == -1) {
+ return new Future.value(new SourceLocation.error(
+ "Column '${colStr}' must be an integer"));
+ }
+
+ if (scriptName != null) {
+ // Resolve the script.
+ return _lookupScript(debugger.isolate, scriptName).then((scripts) {
+ if (scripts.length == 0) {
+ return new SourceLocation.error("Script '${scriptName}' not found");
+ } else if (scripts.length == 1) {
+ return new SourceLocation.file(scripts[0], line, col);
+ } else {
+ // TODO(turnidge): Allow the user to disambiguate.
+ return new SourceLocation.error("Script '${scriptName}' is ambigous");
+ }
+ });
+ } else {
+ // No script provided. Default to top of stack for now.
+ ServiceMap stack = debugger.stack;
+ if (stack == null || stack['frames'].length == 0) {
+ return new Future.value(new SourceLocation.error(
+ 'A script must be provided when the stack is empty'));
+ }
+ Script script = stack['frames'][0]['script'];
+ return new Future.value(new SourceLocation.file(script, line, col));
+ }
+ }
+
+ static Future<List<Script>> _lookupScript(Isolate isolate,
+ String name,
+ {bool allowPrefix: false}) {
+ var pending = [];
+ for (var lib in isolate.libraries) {
+ if (!lib.loaded) {
+ pending.add(lib.load());
+ }
+ }
+ return Future.wait(pending).then((_) {
+ List matches = [];
+ for (var lib in isolate.libraries) {
+ for (var script in lib.scripts) {
+ if (allowPrefix) {
+ if (script.name.startsWith(name)) {
+ matches.add(script);
+ }
+ } else {
+ if (name == script.name) {
+ matches.add(script);
+ }
+ }
+ }
+ }
+ return matches;
+ });
+ }
+
+ static List<ServiceFunction> _lookupFunction(Isolate isolate,
+ String name,
+ { bool allowPrefix: false }) {
+ var matches = [];
+ for (var lib in isolate.libraries) {
+ assert(lib.loaded);
+ for (var function in lib.functions) {
+ if (allowPrefix) {
+ if (function.name.startsWith(name)) {
+ matches.add(function);
+ }
+ } else {
+ if (name == function.name) {
+ matches.add(function);
+ }
+ }
+ }
+ }
+ return matches;
+ }
+
+ static Future<List<Class>> _lookupClass(Isolate isolate,
+ String name,
+ { bool allowPrefix: false }) {
+ var pending = [];
+ for (var lib in isolate.libraries) {
+ assert(lib.loaded);
+ for (var cls in lib.classes) {
+ if (!cls.loaded) {
+ pending.add(cls.load());
+ }
+ }
+ }
+ return Future.wait(pending).then((_) {
+ var matches = [];
+ for (var lib in isolate.libraries) {
+ for (var cls in lib.classes) {
+ if (allowPrefix) {
+ if (cls.name.startsWith(name)) {
+ matches.add(cls);
+ }
+ } else {
+ if (name == cls.name) {
+ matches.add(cls);
+ }
+ }
+ }
+ }
+ return matches;
+ });
+ }
+
+ static ServiceFunction _getConstructor(Class cls, String name) {
+ var matches = [];
+ for (var function in cls.functions) {
+ assert(cls.loaded);
+ if (name == function.name) {
+ return function;
+ }
+ }
+ return null;
+ }
+
+ // TODO(turnidge): This does not handle named functions which are
+ // inside of named functions, e.g. foo.bar.baz.
+ static Future<SourceLocation> _parseFunction(Debugger debugger,
+ Match match) {
+ Isolate isolate = debugger.isolate;
+ var base = match.group(1);
+ var qualifier = match.group(2);
+ assert(base != null);
+
+ return _lookupClass(isolate, base).then((classes) {
+ var functions = [];
+ if (qualifier == null) {
+ // Unqualified name is either a function or a constructor.
+ functions.addAll(_lookupFunction(isolate, base));
+
+ for (var cls in classes) {
+ // Look for a self-named constructor.
+ var constructor = _getConstructor(cls, cls.name);
+ if (constructor != null) {
+ functions.add(constructor);
+ }
+ }
+ } else {
+ // Qualified name.
+ var functionName = qualifier.substring(1);
+ for (var cls in classes) {
+ assert(cls.loaded);
+ for (var function in cls.functions) {
+ if (function.kind == FunctionKind.kConstructor) {
+ // Constructor names are class-qualified.
+ if (match.group(0) == function.name) {
+ functions.add(function);
+ }
+ } else {
+ if (functionName == function.name) {
+ functions.add(function);
+ }
+ }
+ }
+ }
+ }
+ if (functions.length == 0) {
+ return new SourceLocation.error(
+ "Function '${match.group(0)}' not found");
+ } else if (functions.length == 1) {
+ return new SourceLocation.func(functions[0]);
+ } else {
+ // TODO(turnidge): Allow the user to disambiguate.
+ return new SourceLocation.error(
+ "Function '${match.group(0)}' is ambigous");
+ }
+ return new SourceLocation.error('foo');
+ });
+ }
+
+ static RegExp partialSourceLocMatcher =
+ new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?');
+ static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
+
+ /// Completes a partial source location description.
+ static Future<List<String>> complete(Debugger debugger, String locDesc) {
+ List<Future<List<String>>> pending = [];
+ var match = partialFunctionMatcher.firstMatch(locDesc);
+ if (match != null) {
+ pending.add(_completeFunction(debugger, match));
+ }
+
+ match = partialSourceLocMatcher.firstMatch(locDesc);
+ if (match != null) {
+ pending.add(_completeFile(debugger, match));
+ }
+
+ return Future.wait(pending).then((List<List<String>> responses) {
+ var completions = [];
+ for (var response in responses) {
+ completions.addAll(response);
+ }
+ return completions;
+ });
+ }
+
+ static Future<List<String>> _completeFunction(Debugger debugger,
+ Match match) {
+ Isolate isolate = debugger.isolate;
+ var base = match.group(1);
+ var qualifier = match.group(2);
+ base = (base == null ? '' : base);
+
+ if (qualifier == null) {
+ return _lookupClass(isolate, base, allowPrefix:true).then((classes) {
+ var completions = [];
+
+ // Complete top-level function names.
+ var functions = _lookupFunction(isolate, base, allowPrefix:true);
+ var funcNames = functions.map((f) => f.name).toList();
+ funcNames.sort();
+ completions.addAll(funcNames);
+
+ // Complete class names.
+ var classNames = classes.map((f) => f.name).toList();
+ classNames.sort();
+ completions.addAll(classNames);
+
+ return completions;
+ });
+ } else {
+ return _lookupClass(isolate, base, allowPrefix:false).then((classes) {
+ var completions = [];
+ for (var cls in classes) {
+ for (var function in cls.functions) {
+ if (function.kind == FunctionKind.kConstructor) {
+ if (function.name.startsWith(match.group(0))) {
+ completions.add(function.name);
+ }
+ } else {
+ if (function.qualifiedName.startsWith(match.group(0))) {
+ completions.add(function.qualifiedName);
+ }
+ }
+ }
+ }
+ completions.sort();
+ return completions;
+ });
+ }
+ }
+
+ static Future<List<String>> _completeFile(Debugger debugger, Match match) {
+ var scriptName = match.group(1);
+ var lineStr = match.group(2);
+ var colStr = match.group(3);
+ if (lineStr != null || colStr != null) {
+ // TODO(turnidge): Complete valid line and column numbers.
+ return new Future.value([]);
+ }
+ scriptName = (scriptName == null ? '' : scriptName);
+
+ return _lookupScript(debugger.isolate, scriptName, allowPrefix:true)
+ .then((scripts) {
+ List completions = [];
+ for (var script in scripts) {
+ completions.add(script.name + ':');
+ }
+ completions.sort();
+ return completions;
+ });
+ }
+
+ String toString() {
+ if (valid) {
+ if (function != null) {
+ return '${function.qualifiedName}';
+ } else if (col != null) {
+ return '${script.name}:${line}:${col}';
+ } else {
+ return '${script.name}:${line}';
+ }
+ }
+ return 'invalid source location (${errorMessage})';
+ }
+
+ Script script;
+ int line;
+ int col;
+ ServiceFunction function;
+ String errorMessage;
+ bool get valid => (errorMessage == null);
+}
« no previous file with comments | « runtime/observatory/lib/src/debugger/debugger.dart ('k') | runtime/observatory/lib/src/elements/debugger.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698