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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of debugger;
6
7 class SourceLocation {
8 SourceLocation.file(this.script, this.line, this.col);
9 SourceLocation.func(this.function);
10 SourceLocation.error(this.errorMessage);
11
12 static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?');
13 static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?');
14
15 /// Parses a source location description.
16 ///
17 /// Formats:
18 /// '' - current position
19 /// 13 - line 13, current script
20 /// 13:20 - line 13, col 20, current script
21 /// script.dart:13 - line 13, script.dart
22 /// script.dart:13:20 - line 13, col 20, script.dart
23 /// main - function
24 /// FormatException - constructor
25 /// _SHA1._updateHash - method
26 static Future<SourceLocation> parse(Debugger debugger, String locDesc) {
27 if (locDesc == '') {
28 // Special case: '' means return current location.
29 return _currentLocation(debugger);
30 }
31
32 // Parse the location description.
33 var match = sourceLocMatcher.firstMatch(locDesc);
34 if (match != null) {
35 return _parseScriptLine(debugger, match);
36 }
37 match = functionMatcher.firstMatch(locDesc);
38 if (match != null) {
39 return _parseFunction(debugger, match);
40 }
41 return new Future.value(new SourceLocation.error(
42 "Invalid source location '${locDesc}'"));
43 }
44
45 static Future<SourceLocation> _currentLocation(Debugger debugger) {
46 ServiceMap stack = debugger.stack;
47 if (stack == null || stack['frames'].length == 0) {
48 return new Future.value(new SourceLocation.error(
49 'A script must be provided when the stack is empty'));
50 }
51 var frame = stack['frames'][0];
52 Script script = frame['script'];
53 return script.load().then((_) {
54 var line = script.tokenToLine(frame['tokenPos']);
55 // TODO(turnidge): Pass in the column here once the protocol supports it.
56 return new Future.value(new SourceLocation.file(script, line, null));
57 });
58 }
59
60 static Future<SourceLocation> _parseScriptLine(Debugger debugger,
61 Match match) {
62 var scriptName = match.group(1);
63 if (scriptName != null) {
64 scriptName = scriptName.substring(0, scriptName.length - 1);
65 }
66 var lineStr = match.group(2);
67 assert(lineStr != null);
68 var colStr = match.group(3);
69 if (colStr != null) {
70 colStr = colStr.substring(1);
71 }
72 var line = int.parse(lineStr, onError:(_) => -1);
73 var col = (colStr != null
74 ? int.parse(colStr, onError:(_) => -1)
75 : null);
76 if (line == -1) {
77 return new Future.value(new SourceLocation.error(
78 "Line '${lineStr}' must be an integer"));
79 }
80 if (col == -1) {
81 return new Future.value(new SourceLocation.error(
82 "Column '${colStr}' must be an integer"));
83 }
84
85 if (scriptName != null) {
86 // Resolve the script.
87 return _lookupScript(debugger.isolate, scriptName).then((scripts) {
88 if (scripts.length == 0) {
89 return new SourceLocation.error("Script '${scriptName}' not found");
90 } else if (scripts.length == 1) {
91 return new SourceLocation.file(scripts[0], line, col);
92 } else {
93 // TODO(turnidge): Allow the user to disambiguate.
94 return new SourceLocation.error("Script '${scriptName}' is ambigous");
95 }
96 });
97 } else {
98 // No script provided. Default to top of stack for now.
99 ServiceMap stack = debugger.stack;
100 if (stack == null || stack['frames'].length == 0) {
101 return new Future.value(new SourceLocation.error(
102 'A script must be provided when the stack is empty'));
103 }
104 Script script = stack['frames'][0]['script'];
105 return new Future.value(new SourceLocation.file(script, line, col));
106 }
107 }
108
109 static Future<List<Script>> _lookupScript(Isolate isolate,
110 String name,
111 {bool allowPrefix: false}) {
112 var pending = [];
113 for (var lib in isolate.libraries) {
114 if (!lib.loaded) {
115 pending.add(lib.load());
116 }
117 }
118 return Future.wait(pending).then((_) {
119 List matches = [];
120 for (var lib in isolate.libraries) {
121 for (var script in lib.scripts) {
122 if (allowPrefix) {
123 if (script.name.startsWith(name)) {
124 matches.add(script);
125 }
126 } else {
127 if (name == script.name) {
128 matches.add(script);
129 }
130 }
131 }
132 }
133 return matches;
134 });
135 }
136
137 static List<ServiceFunction> _lookupFunction(Isolate isolate,
138 String name,
139 { bool allowPrefix: false }) {
140 var matches = [];
141 for (var lib in isolate.libraries) {
142 assert(lib.loaded);
143 for (var function in lib.functions) {
144 if (allowPrefix) {
145 if (function.name.startsWith(name)) {
146 matches.add(function);
147 }
148 } else {
149 if (name == function.name) {
150 matches.add(function);
151 }
152 }
153 }
154 }
155 return matches;
156 }
157
158 static Future<List<Class>> _lookupClass(Isolate isolate,
159 String name,
160 { bool allowPrefix: false }) {
161 var pending = [];
162 for (var lib in isolate.libraries) {
163 assert(lib.loaded);
164 for (var cls in lib.classes) {
165 if (!cls.loaded) {
166 pending.add(cls.load());
167 }
168 }
169 }
170 return Future.wait(pending).then((_) {
171 var matches = [];
172 for (var lib in isolate.libraries) {
173 for (var cls in lib.classes) {
174 if (allowPrefix) {
175 if (cls.name.startsWith(name)) {
176 matches.add(cls);
177 }
178 } else {
179 if (name == cls.name) {
180 matches.add(cls);
181 }
182 }
183 }
184 }
185 return matches;
186 });
187 }
188
189 static ServiceFunction _getConstructor(Class cls, String name) {
190 var matches = [];
191 for (var function in cls.functions) {
192 assert(cls.loaded);
193 if (name == function.name) {
194 return function;
195 }
196 }
197 return null;
198 }
199
200 // TODO(turnidge): This does not handle named functions which are
201 // inside of named functions, e.g. foo.bar.baz.
202 static Future<SourceLocation> _parseFunction(Debugger debugger,
203 Match match) {
204 Isolate isolate = debugger.isolate;
205 var base = match.group(1);
206 var qualifier = match.group(2);
207 assert(base != null);
208
209 return _lookupClass(isolate, base).then((classes) {
210 var functions = [];
211 if (qualifier == null) {
212 // Unqualified name is either a function or a constructor.
213 functions.addAll(_lookupFunction(isolate, base));
214
215 for (var cls in classes) {
216 // Look for a self-named constructor.
217 var constructor = _getConstructor(cls, cls.name);
218 if (constructor != null) {
219 functions.add(constructor);
220 }
221 }
222 } else {
223 // Qualified name.
224 var functionName = qualifier.substring(1);
225 for (var cls in classes) {
226 assert(cls.loaded);
227 for (var function in cls.functions) {
228 if (function.kind == FunctionKind.kConstructor) {
229 // Constructor names are class-qualified.
230 if (match.group(0) == function.name) {
231 functions.add(function);
232 }
233 } else {
234 if (functionName == function.name) {
235 functions.add(function);
236 }
237 }
238 }
239 }
240 }
241 if (functions.length == 0) {
242 return new SourceLocation.error(
243 "Function '${match.group(0)}' not found");
244 } else if (functions.length == 1) {
245 return new SourceLocation.func(functions[0]);
246 } else {
247 // TODO(turnidge): Allow the user to disambiguate.
248 return new SourceLocation.error(
249 "Function '${match.group(0)}' is ambigous");
250 }
251 return new SourceLocation.error('foo');
252 });
253 }
254
255 static RegExp partialSourceLocMatcher =
256 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?');
257 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
258
259 /// Completes a partial source location description.
260 static Future<List<String>> complete(Debugger debugger, String locDesc) {
261 List<Future<List<String>>> pending = [];
262 var match = partialFunctionMatcher.firstMatch(locDesc);
263 if (match != null) {
264 pending.add(_completeFunction(debugger, match));
265 }
266
267 match = partialSourceLocMatcher.firstMatch(locDesc);
268 if (match != null) {
269 pending.add(_completeFile(debugger, match));
270 }
271
272 return Future.wait(pending).then((List<List<String>> responses) {
273 var completions = [];
274 for (var response in responses) {
275 completions.addAll(response);
276 }
277 return completions;
278 });
279 }
280
281 static Future<List<String>> _completeFunction(Debugger debugger,
282 Match match) {
283 Isolate isolate = debugger.isolate;
284 var base = match.group(1);
285 var qualifier = match.group(2);
286 base = (base == null ? '' : base);
287
288 if (qualifier == null) {
289 return _lookupClass(isolate, base, allowPrefix:true).then((classes) {
290 var completions = [];
291
292 // Complete top-level function names.
293 var functions = _lookupFunction(isolate, base, allowPrefix:true);
294 var funcNames = functions.map((f) => f.name).toList();
295 funcNames.sort();
296 completions.addAll(funcNames);
297
298 // Complete class names.
299 var classNames = classes.map((f) => f.name).toList();
300 classNames.sort();
301 completions.addAll(classNames);
302
303 return completions;
304 });
305 } else {
306 return _lookupClass(isolate, base, allowPrefix:false).then((classes) {
307 var completions = [];
308 for (var cls in classes) {
309 for (var function in cls.functions) {
310 if (function.kind == FunctionKind.kConstructor) {
311 if (function.name.startsWith(match.group(0))) {
312 completions.add(function.name);
313 }
314 } else {
315 if (function.qualifiedName.startsWith(match.group(0))) {
316 completions.add(function.qualifiedName);
317 }
318 }
319 }
320 }
321 completions.sort();
322 return completions;
323 });
324 }
325 }
326
327 static Future<List<String>> _completeFile(Debugger debugger, Match match) {
328 var scriptName = match.group(1);
329 var lineStr = match.group(2);
330 var colStr = match.group(3);
331 if (lineStr != null || colStr != null) {
332 // TODO(turnidge): Complete valid line and column numbers.
333 return new Future.value([]);
334 }
335 scriptName = (scriptName == null ? '' : scriptName);
336
337 return _lookupScript(debugger.isolate, scriptName, allowPrefix:true)
338 .then((scripts) {
339 List completions = [];
340 for (var script in scripts) {
341 completions.add(script.name + ':');
342 }
343 completions.sort();
344 return completions;
345 });
346 }
347
348 String toString() {
349 if (valid) {
350 if (function != null) {
351 return '${function.qualifiedName}';
352 } else if (col != null) {
353 return '${script.name}:${line}:${col}';
354 } else {
355 return '${script.name}:${line}';
356 }
357 }
358 return 'invalid source location (${errorMessage})';
359 }
360
361 Script script;
362 int line;
363 int col;
364 ServiceFunction function;
365 String errorMessage;
366 bool get valid => (errorMessage == null);
367 }
OLDNEW
« 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