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

Side by Side Diff: runtime/observatory/lib/src/debugger/source_location.dart

Issue 1150303004: Switch to using SourceLocation universally in service protocol. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: post merge Created 5 years, 6 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
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'][debugger.currentFrame];
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 for (var function in cls.functions) {
191 assert(cls.loaded);
192 if (name == function.name) {
193 return function;
194 }
195 }
196 return null;
197 }
198
199 // TODO(turnidge): This does not handle named functions which are
200 // inside of named functions, e.g. foo.bar.baz.
201 static Future<SourceLocation> _parseFunction(Debugger debugger,
202 Match match) {
203 Isolate isolate = debugger.isolate;
204 var base = match.group(1);
205 var qualifier = match.group(2);
206 assert(base != null);
207
208 return _lookupClass(isolate, base).then((classes) {
209 var functions = [];
210 if (qualifier == null) {
211 // Unqualified name is either a function or a constructor.
212 functions.addAll(_lookupFunction(isolate, base));
213
214 for (var cls in classes) {
215 // Look for a self-named constructor.
216 var constructor = _getConstructor(cls, cls.name);
217 if (constructor != null) {
218 functions.add(constructor);
219 }
220 }
221 } else {
222 // Qualified name.
223 var functionName = qualifier.substring(1);
224 for (var cls in classes) {
225 assert(cls.loaded);
226 for (var function in cls.functions) {
227 if (function.kind == FunctionKind.kConstructor) {
228 // Constructor names are class-qualified.
229 if (match.group(0) == function.name) {
230 functions.add(function);
231 }
232 } else {
233 if (functionName == function.name) {
234 functions.add(function);
235 }
236 }
237 }
238 }
239 }
240 if (functions.length == 0) {
241 return new SourceLocation.error(
242 "Function '${match.group(0)}' not found");
243 } else if (functions.length == 1) {
244 return new SourceLocation.func(functions[0]);
245 } else {
246 // TODO(turnidge): Allow the user to disambiguate.
247 return new SourceLocation.error(
248 "Function '${match.group(0)}' is ambigous");
249 }
250 return new SourceLocation.error('foo');
251 });
252 }
253
254 static RegExp partialSourceLocMatcher =
255 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?');
256 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
257
258 /// Completes a partial source location description.
259 static Future<List<String>> complete(Debugger debugger, String locDesc) {
260 List<Future<List<String>>> pending = [];
261 var match = partialFunctionMatcher.firstMatch(locDesc);
262 if (match != null) {
263 pending.add(_completeFunction(debugger, match));
264 }
265
266 match = partialSourceLocMatcher.firstMatch(locDesc);
267 if (match != null) {
268 pending.add(_completeFile(debugger, match));
269 }
270
271 return Future.wait(pending).then((List<List<String>> responses) {
272 var completions = [];
273 for (var response in responses) {
274 completions.addAll(response);
275 }
276 return completions;
277 });
278 }
279
280 static Future<List<String>> _completeFunction(Debugger debugger,
281 Match match) {
282 Isolate isolate = debugger.isolate;
283 var base = match.group(1);
284 var qualifier = match.group(2);
285 base = (base == null ? '' : base);
286
287 if (qualifier == null) {
288 return _lookupClass(isolate, base, allowPrefix:true).then((classes) {
289 var completions = [];
290
291 // Complete top-level function names.
292 var functions = _lookupFunction(isolate, base, allowPrefix:true);
293 var funcNames = functions.map((f) => f.name).toList();
294 funcNames.sort();
295 completions.addAll(funcNames);
296
297 // Complete class names.
298 var classNames = classes.map((f) => f.name).toList();
299 classNames.sort();
300 completions.addAll(classNames);
301
302 return completions;
303 });
304 } else {
305 return _lookupClass(isolate, base, allowPrefix:false).then((classes) {
306 var completions = [];
307 for (var cls in classes) {
308 for (var function in cls.functions) {
309 if (function.kind == FunctionKind.kConstructor) {
310 if (function.name.startsWith(match.group(0))) {
311 completions.add(function.name);
312 }
313 } else {
314 if (function.qualifiedName.startsWith(match.group(0))) {
315 completions.add(function.qualifiedName);
316 }
317 }
318 }
319 }
320 completions.sort();
321 return completions;
322 });
323 }
324 }
325
326 static Future<List<String>> _completeFile(Debugger debugger, Match match) {
327 var scriptName = match.group(1);
328 var lineStr = match.group(2);
329 var colStr = match.group(3);
330 if (lineStr != null || colStr != null) {
331 // TODO(turnidge): Complete valid line and column numbers.
332 return new Future.value([]);
333 }
334 scriptName = (scriptName == null ? '' : scriptName);
335
336 return _lookupScript(debugger.isolate, scriptName, allowPrefix:true)
337 .then((scripts) {
338 List completions = [];
339 for (var script in scripts) {
340 completions.add(script.name + ':');
341 }
342 completions.sort();
343 return completions;
344 });
345 }
346
347 String toString() {
348 if (valid) {
349 if (function != null) {
350 return '${function.qualifiedName}';
351 } else if (col != null) {
352 return '${script.name}:${line}:${col}';
353 } else {
354 return '${script.name}:${line}';
355 }
356 }
357 return 'invalid source location (${errorMessage})';
358 }
359
360 Script script;
361 int line;
362 int col;
363 ServiceFunction function;
364 String errorMessage;
365 bool get valid => (errorMessage == null);
366 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/debugger/debugger_location.dart ('k') | runtime/observatory/lib/src/elements/class_view.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698