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

Side by Side Diff: pkg/fletchc/lib/input_handler.dart

Issue 1659163007: Rename fletch -> dartino (Closed) Base URL: https://github.com/dartino/sdk.git@master
Patch Set: address comments Created 4 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
« no previous file with comments | « pkg/fletchc/lib/incremental_backend.dart ('k') | pkg/fletchc/lib/program_info.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dartino 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.md file.
4
5 part of fletch.vm_session;
6
7 const String BANNER = """
8 Starting session. Type 'help' for a list of commands.
9 """;
10
11 const String HELP = """
12 Commands:
13 'help' show list of commands
14 'r'/'run' start program
15 'b [method name] [bytecode index]' set breakpoint
16 'bf <file> [line] [column]' set breakpoint
17 'bf <file> [line] [pattern]' set breakpoint on first occurrence of
18 the string pattern on the indicated line
19 'd <breakpoint id>' delete breakpoint
20 'lb' list breakpoints
21 's' step until next expression,
22 enters method invocations
23 'n' step until next expression,
24 does not enter method invocations
25 'fibers', 'lf' list all process fibers
26 'finish' finish current method (step out)
27 'restart' restart the selected frame
28 'sb' step bytecode, enters method invocations
29 'nb' step over bytecode, does not enter metho d invocations
30 'c' continue execution
31 'bt' backtrace
32 'f <n>' select frame
33 'l' list source for frame
34 'p <name>' print the value of local variable
35 'p *<name>' print the structure of local variable
36 'p' print the values of all locals
37 'processes', 'lp' list all processes
38 'disasm' disassemble code for frame
39 't <flag>' toggle one of the flags:
40 - 'internal' : show internal frames
41 'q'/'quit' quit the session
42 """;
43
44 class InputHandler {
45 final Session session;
46 final Stream<String> stream;
47 final bool echo;
48 final Uri base;
49
50 String previousLine = '';
51
52 int processPagingCount = 10;
53 int processPagingCurrent = 0;
54
55 InputHandler(this.session, this.stream, this.echo, this.base);
56
57 void printPrompt() => session.writeStdout('> ');
58
59 writeStdout(String s) => session.writeStdout(s);
60
61 writeStdoutLine(String s) => session.writeStdout("$s\n");
62
63 Future handleLine(StreamIterator stream, SessionState state) async {
64 String line = stream.current;
65 if (line.isEmpty) line = previousLine;
66 if (line.isEmpty) {
67 printPrompt();
68 return;
69 }
70 if (echo) writeStdoutLine(line);
71 List<String> commandComponents =
72 line.split(' ').where((s) => s.isNotEmpty).toList();
73 String command = commandComponents[0];
74 switch (command) {
75 case 'help':
76 writeStdoutLine(HELP);
77 break;
78 case 'b':
79 var method =
80 (commandComponents.length > 1) ? commandComponents[1] : 'main';
81 var bci =
82 (commandComponents.length > 2) ? commandComponents[2] : '0';
83 bci = int.parse(bci, onError: (_) => null);
84 if (bci == null) {
85 writeStdoutLine('### invalid bytecode index: $bci');
86 break;
87 }
88 List<Breakpoint> breakpoints =
89 await session.setBreakpoint(methodName: method, bytecodeIndex: bci);
90 if (breakpoints != null) {
91 for (Breakpoint breakpoint in breakpoints) {
92 writeStdoutLine("breakpoint set: $breakpoint");
93 }
94 } else {
95 writeStdoutLine(
96 "### failed to set breakpoint at method: $method index: $bci");
97 }
98 break;
99 case 'bf':
100 var file =
101 (commandComponents.length > 1) ? commandComponents[1] : '';
102 var line =
103 (commandComponents.length > 2) ? commandComponents[2] : '1';
104 var columnOrPattern =
105 (commandComponents.length > 3) ? commandComponents[3] : '1';
106
107 List<Uri> files = <Uri>[];
108
109 if (await new File.fromUri(base.resolve(file)).exists()) {
110 // If the supplied file resolved directly to a file use it.
111 files.add(base.resolve(file));
112 } else {
113 // Otherwise search for possible matches.
114 List<Uri> matches = session.findSourceFiles(file).toList()..sort(
115 (a, b) => a.toString().compareTo(b.toString()));
116 Iterable<int> selection = await select(
117 stream,
118 "Multiple matches for file pattern $file",
119 matches.map((uri) =>
120 uri.toString().replaceFirst(base.toString(), '')));
121 for (int selected in selection) {
122 files.add(matches.elementAt(selected));
123 }
124 }
125
126 if (files.isEmpty) {
127 writeStdoutLine('### no matching file found for: $file');
128 break;
129 }
130
131 line = int.parse(line, onError: (_) => null);
132 if (line == null || line < 1) {
133 writeStdoutLine('### invalid line number: $line');
134 break;
135 }
136
137 List<Breakpoint> breakpoints = <Breakpoint>[];
138 int columnNumber = int.parse(columnOrPattern, onError: (_) => null);
139 if (columnNumber == null) {
140 for (Uri fileUri in files) {
141 Breakpoint breakpoint = await session.setFileBreakpointFromPattern(
142 fileUri, line, columnOrPattern);
143 if (breakpoint == null) {
144 writeStdoutLine(
145 '### failed to set breakpoint for pattern $columnOrPattern ' +
146 'on $fileUri:$line');
147 } else {
148 breakpoints.add(breakpoint);
149 }
150 }
151 } else if (columnNumber < 1) {
152 writeStdoutLine('### invalid column number: $columnOrPattern');
153 break;
154 } else {
155 for (Uri fileUri in files) {
156 Breakpoint breakpoint =
157 await session.setFileBreakpoint(fileUri, line, columnNumber);
158 if (breakpoint == null) {
159 writeStdoutLine(
160 '### failed to set breakpoint ' +
161 'on $fileUri:$line:$columnNumber');
162 } else {
163 breakpoints.add(breakpoint);
164 }
165 }
166 }
167 if (breakpoints.isNotEmpty) {
168 for (Breakpoint breakpoint in breakpoints) {
169 writeStdoutLine("breakpoint set: $breakpoint");
170 }
171 } else {
172 writeStdoutLine(
173 "### failed to set any breakpoints");
174 }
175 break;
176 case 'bt':
177 if (!checkLoaded('cannot print backtrace')) {
178 break;
179 }
180 BackTrace backtrace = await session.backTrace();
181 if (backtrace == null) {
182 writeStdoutLine('### failed to get backtrace for current program');
183 } else {
184 writeStdout(backtrace.format());
185 }
186 break;
187 case 'f':
188 var frame =
189 (commandComponents.length > 1) ? commandComponents[1] : "-1";
190 frame = int.parse(frame, onError: (_) => null);
191 if (frame == null || !session.selectFrame(frame)) {
192 writeStdoutLine('### invalid frame number: $frame');
193 }
194 break;
195 case 'l':
196 if (!checkLoaded('nothing to list')) {
197 break;
198 }
199 BackTrace trace = await session.backTrace();
200 String listing = trace != null ? trace.list(state) : null;
201 if (listing != null) {
202 writeStdoutLine(listing);
203 } else {
204 writeStdoutLine("### failed listing source");
205 }
206 break;
207 case 'disasm':
208 if (checkLoaded('cannot show bytecodes')) {
209 BackTrace backtrace = await session.backTrace();
210 String disassembly = backtrace != null ? backtrace.disasm() : null;
211 if (disassembly != null) {
212 writeStdout(disassembly);
213 } else {
214 writeStdoutLine(
215 "### could not disassemble source for current frame");
216 }
217 }
218 break;
219 case 'c':
220 if (checkRunning('cannot continue')) {
221 await handleProcessStopResponse(await session.cont(), state);
222 }
223 break;
224 case 'd':
225 var id = (commandComponents.length > 1) ? commandComponents[1] : null;
226 id = int.parse(id, onError: (_) => null);
227 if (id == null) {
228 writeStdoutLine('### invalid breakpoint number: $id');
229 break;
230 }
231 Breakpoint breakpoint = await session.deleteBreakpoint(id);
232 if (breakpoint == null) {
233 writeStdoutLine("### invalid breakpoint id: $id");
234 break;
235 }
236 writeStdoutLine("### deleted breakpoint: $breakpoint");
237 break;
238 case 'processes':
239 case 'lp':
240 if (checkRunning('cannot list processes')) {
241 // Reset current paging point if not continuing from an 'lp' command.
242 if (previousLine != 'lp' && previousLine != 'processes') {
243 processPagingCurrent = 0;
244 }
245
246 List<int> processes = await session.processes();
247 processes.sort();
248
249 int count = processes.length;
250 int start = processPagingCurrent;
251 int end;
252 if (start + processPagingCount < count) {
253 processPagingCurrent += processPagingCount;
254 end = processPagingCurrent;
255 } else {
256 processPagingCurrent = 0;
257 end = count;
258 }
259
260 if (processPagingCount < count) {
261 writeStdout("displaying range [$start;${end-1}] ");
262 writeStdoutLine("of $count processes");
263 }
264 for (int i = start; i < end; ++i) {
265 int processId = processes[i];
266 BackTrace stack = await session.processStack(processId);
267 writeStdoutLine('\nprocess ${processId}');
268 writeStdout(stack.format());
269 }
270 writeStdoutLine('');
271 }
272 break;
273 case 'fibers':
274 case 'lf':
275 if (checkRunning('cannot show fibers')) {
276 List<BackTrace> traces = await session.fibers();
277 for (int fiber = 0; fiber < traces.length; ++fiber) {
278 writeStdoutLine('\nfiber $fiber');
279 writeStdout(traces[fiber].format());
280 }
281 writeStdoutLine('');
282 }
283 break;
284 case 'finish':
285 if (checkRunning('cannot finish method')) {
286 await handleProcessStopResponse(await session.stepOut(), state);
287 }
288 break;
289 case 'restart':
290 if (!checkLoaded('cannot restart')) {
291 break;
292 }
293 BackTrace trace = await session.backTrace();
294 if (trace == null) {
295 writeStdoutLine("### cannot restart when nothing is executing");
296 break;
297 }
298 if (trace.length <= 1) {
299 writeStdoutLine("### cannot restart entry frame");
300 break;
301 }
302 await handleProcessStopResponse(await session.restart(), state);
303 break;
304 case 'lb':
305 List<Breakpoint> breakpoints = session.breakpoints();
306 if (breakpoints == null || breakpoints.isEmpty) {
307 writeStdoutLine('### no breakpoints');
308 } else {
309 writeStdoutLine("### breakpoints:");
310 for (var bp in breakpoints) {
311 writeStdoutLine('$bp');
312 }
313 }
314 break;
315 case 'p':
316 if (!checkLoaded('nothing to print')) {
317 break;
318 }
319 if (commandComponents.length <= 1) {
320 List<RemoteObject> variables = await session.processAllVariables();
321 if (variables.isEmpty) {
322 writeStdoutLine('### No variables in scope');
323 } else {
324 for (RemoteObject variable in variables) {
325 writeStdoutLine(session.remoteObjectToString(variable));
326 }
327 }
328 break;
329 }
330 String variableName = commandComponents[1];
331 RemoteObject variable;
332 if (variableName.startsWith('*')) {
333 variableName = variableName.substring(1);
334 variable = await session.processVariableStructure(variableName);
335 } else {
336 variable = await session.processVariable(variableName);
337 }
338 if (variable == null) {
339 writeStdoutLine('### no such variable: $variableName');
340 } else {
341 writeStdoutLine(session.remoteObjectToString(variable));
342 }
343 break;
344 case 'q':
345 case 'quit':
346 await session.terminateSession();
347 break;
348 case 'r':
349 case 'run':
350 if (checkNotLoaded("use 'restart' to run again")) {
351 await handleProcessStopResponse(await session.debugRun(), state);
352 }
353 break;
354 case 's':
355 if (checkRunning('cannot step to next expression')) {
356 await handleProcessStopResponse(await session.step(), state);
357 }
358 break;
359 case 'n':
360 if (checkRunning('cannot go to next expression')) {
361 await handleProcessStopResponse(await session.stepOver(), state);
362 }
363 break;
364 case 'sb':
365 if (checkRunning('cannot step bytecode')) {
366 await handleProcessStopResponse(await session.stepBytecode(), state);
367 }
368 break;
369 case 'nb':
370 if (checkRunning('cannot step over bytecode')) {
371 await handleProcessStopResponse(
372 await session.stepOverBytecode(), state);
373 }
374 break;
375 case 't':
376 String toggle;
377 if (commandComponents.length > 1) {
378 toggle = commandComponents[1];
379 }
380 switch (toggle) {
381 case 'internal':
382 bool internalVisible = session.toggleInternal();
383 writeStdoutLine(
384 '### internal frame visibility set to: $internalVisible');
385 break;
386 case 'verbose':
387 bool verbose = session.toggleVerbose();
388 writeStdoutLine('### verbose printing set to: $verbose');
389 break;
390 default:
391 writeStdoutLine('### invalid flag $toggle');
392 break;
393 }
394 break;
395 default:
396 writeStdoutLine('### unknown command: $command');
397 break;
398 }
399 previousLine = line;
400 if (!session.terminated) printPrompt();
401 }
402
403 // This method is used to deal with the stopped process command responses
404 // that can be returned when sending the Fletch VM a command request.
405 Future handleProcessStopResponse(
406 VmCommand response,
407 SessionState state) async {
408 String output = await session.processStopResponseToString(response, state);
409 if (output != null && output.isNotEmpty) {
410 writeStdout(output);
411 }
412 }
413
414 bool checkLoaded([String postfix]) {
415 if (!session.loaded) {
416 String prefix = '### process not loaded';
417 writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix);
418 }
419 return session.loaded;
420 }
421
422 bool checkNotLoaded([String postfix]) {
423 if (session.loaded) {
424 String prefix = '### process already loaded';
425 writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix);
426 }
427 return !session.loaded;
428 }
429
430 bool checkRunning([String postfix]) {
431 if (!session.running) {
432 String prefix = '### process not running';
433 writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix);
434 }
435 return session.running;
436 }
437
438 bool checkNotRunning([String postfix]) {
439 if (session.running) {
440 String prefix = '### process already running';
441 writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix);
442 }
443 return !session.running;
444 }
445
446 Future<int> run(SessionState state) async {
447 writeStdoutLine(BANNER);
448 printPrompt();
449 StreamIterator streamIterator = new StreamIterator(stream);
450 while (await streamIterator.moveNext()) {
451 try {
452 await handleLine(streamIterator, state);
453 } catch (e, s) {
454 Future cancel = streamIterator.cancel()?.catchError((_) {});
455 if (!session.terminated) {
456 await session.terminateSession().catchError((_) {});
457 }
458 await cancel;
459 return new Future.error(e, s);
460 }
461 if (session.terminated) {
462 await streamIterator.cancel();
463 }
464 }
465 if (!session.terminated) await session.terminateSession();
466 return 0;
467 }
468
469 // Prompt the user to select among a set of choices.
470 // Returns a set of indexes that are the chosen indexes from the input set.
471 // If the size of choices is less then two, then the result is that the full
472 // input set is selected without prompting the user. Otherwise the user is
473 // interactively prompted to choose a selection.
474 Future<Iterable<int>> select(
475 StreamIterator stream,
476 String message,
477 Iterable<String> choices) async {
478 int length = choices.length;
479 if (length == 0) return <int>[];
480 if (length == 1) return <int>[0];
481 writeStdout("$message. ");
482 writeStdoutLine("Please select from the following choices:");
483 int i = 1;
484 int pad = 2 + "$length".length;
485 for (String choice in choices) {
486 writeStdoutLine("${i++}".padLeft(pad) + ": $choice");
487 }
488 writeStdoutLine('a'.padLeft(pad) + ": all of the above");
489 writeStdoutLine('n'.padLeft(pad) + ": none of the above");
490 while (true) {
491 printPrompt();
492 bool hasNext = await stream.moveNext();
493 if (!hasNext) {
494 writeStdoutLine("### failed to read choice input");
495 return <int>[];
496 }
497 String line = stream.current;
498 if (echo) writeStdoutLine(line);
499 if (line == 'n') {
500 return <int>[];
501 }
502 if (line == 'a') {
503 return new List<int>.generate(length, (i) => i);
504 }
505 int choice = int.parse(line, onError: (_) => 0);
506 if (choice > 0 && choice <= length) {
507 return <int>[choice - 1];
508 }
509 writeStdoutLine("Invalid choice: $choice");
510 writeStdoutLine("Please select a number between 1 and $length, " +
511 "'a' for all, or 'n' for none.");
512 }
513 }
514 }
OLDNEW
« no previous file with comments | « pkg/fletchc/lib/incremental_backend.dart ('k') | pkg/fletchc/lib/program_info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698