OLD | NEW |
| (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 library fletchc.verbs.debug_verb; | |
6 | |
7 import 'dart:core' hide | |
8 StackTrace; | |
9 | |
10 import 'infrastructure.dart'; | |
11 | |
12 import 'dart:async' show | |
13 Stream, | |
14 StreamController, | |
15 StreamIterator; | |
16 | |
17 import 'dart:convert' show | |
18 UTF8, | |
19 LineSplitter; | |
20 | |
21 import 'documentation.dart' show | |
22 debugDocumentation; | |
23 | |
24 import '../diagnostic.dart' show | |
25 throwInternalError; | |
26 | |
27 import '../worker/developer.dart' show | |
28 ClientEventHandler, | |
29 handleSignal, | |
30 compileAndAttachToVmThen, | |
31 setupClientInOut; | |
32 | |
33 import '../hub/client_commands.dart' show | |
34 ClientCommandCode; | |
35 | |
36 import 'package:fletchc/debug_state.dart' show | |
37 Breakpoint; | |
38 | |
39 import '../../debug_state.dart' show | |
40 RemoteObject, | |
41 RemoteValue, | |
42 BackTrace; | |
43 | |
44 import '../../vm_commands.dart' show | |
45 VmCommand; | |
46 | |
47 const Action debugAction = | |
48 const Action( | |
49 debug, | |
50 debugDocumentation, | |
51 requiresSession: true, | |
52 supportedTargets: const [ | |
53 TargetKind.APPLY, | |
54 TargetKind.BACKTRACE, | |
55 TargetKind.BREAK, | |
56 TargetKind.CONTINUE, | |
57 TargetKind.DELETE_BREAKPOINT, | |
58 TargetKind.DISASM, | |
59 TargetKind.FIBERS, | |
60 TargetKind.FILE, | |
61 TargetKind.FINISH, | |
62 TargetKind.FRAME, | |
63 TargetKind.LIST, | |
64 TargetKind.LIST_BREAKPOINTS, | |
65 TargetKind.PRINT, | |
66 TargetKind.PRINT_ALL, | |
67 TargetKind.RESTART, | |
68 TargetKind.RUN_TO_MAIN, | |
69 TargetKind.STEP, | |
70 TargetKind.STEP_BYTECODE, | |
71 TargetKind.STEP_OVER, | |
72 TargetKind.STEP_OVER_BYTECODE, | |
73 TargetKind.TOGGLE, | |
74 ]); | |
75 | |
76 const int sigQuit = 3; | |
77 | |
78 Future debug(AnalyzedSentence sentence, VerbContext context) async { | |
79 Uri base = sentence.base; | |
80 if (sentence.target == null) { | |
81 return context.performTaskInWorker( | |
82 new InteractiveDebuggerTask(base)); | |
83 } | |
84 | |
85 DebuggerTask task; | |
86 switch (sentence.target.kind) { | |
87 case TargetKind.APPLY: | |
88 task = new DebuggerTask(TargetKind.APPLY.index, base); | |
89 break; | |
90 case TargetKind.RUN_TO_MAIN: | |
91 task = new DebuggerTask(TargetKind.RUN_TO_MAIN.index, base); | |
92 break; | |
93 case TargetKind.BACKTRACE: | |
94 task = new DebuggerTask(TargetKind.BACKTRACE.index, base); | |
95 break; | |
96 case TargetKind.CONTINUE: | |
97 task = new DebuggerTask(TargetKind.CONTINUE.index, base); | |
98 break; | |
99 case TargetKind.BREAK: | |
100 task = new DebuggerTask(TargetKind.BREAK.index, base, | |
101 sentence.targetName); | |
102 break; | |
103 case TargetKind.LIST: | |
104 task = new DebuggerTask(TargetKind.LIST.index, base); | |
105 break; | |
106 case TargetKind.DISASM: | |
107 task = new DebuggerTask(TargetKind.DISASM.index, base); | |
108 break; | |
109 case TargetKind.FRAME: | |
110 task = new DebuggerTask(TargetKind.FRAME.index, base, | |
111 sentence.targetName); | |
112 break; | |
113 case TargetKind.DELETE_BREAKPOINT: | |
114 task = new DebuggerTask(TargetKind.DELETE_BREAKPOINT.index, | |
115 base, | |
116 sentence.targetName); | |
117 break; | |
118 case TargetKind.LIST_BREAKPOINTS: | |
119 task = new DebuggerTask(TargetKind.LIST_BREAKPOINTS.index, base); | |
120 break; | |
121 case TargetKind.STEP: | |
122 task = new DebuggerTask(TargetKind.STEP.index, base); | |
123 break; | |
124 case TargetKind.STEP_OVER: | |
125 task = new DebuggerTask(TargetKind.STEP_OVER.index, base); | |
126 break; | |
127 case TargetKind.FIBERS: | |
128 task = new DebuggerTask(TargetKind.FIBERS.index, base); | |
129 break; | |
130 case TargetKind.FINISH: | |
131 task = new DebuggerTask(TargetKind.FINISH.index, base); | |
132 break; | |
133 case TargetKind.RESTART: | |
134 task = new DebuggerTask(TargetKind.RESTART.index, base); | |
135 break; | |
136 case TargetKind.STEP_BYTECODE: | |
137 task = new DebuggerTask(TargetKind.STEP_BYTECODE.index, base); | |
138 break; | |
139 case TargetKind.STEP_OVER_BYTECODE: | |
140 task = new DebuggerTask(TargetKind.STEP_OVER_BYTECODE.index, base); | |
141 break; | |
142 case TargetKind.PRINT: | |
143 task = new DebuggerTask(TargetKind.PRINT.index, base, | |
144 sentence.targetName); | |
145 break; | |
146 case TargetKind.PRINT_ALL: | |
147 task = new DebuggerTask(TargetKind.PRINT_ALL.index, base); | |
148 break; | |
149 case TargetKind.TOGGLE: | |
150 task = new DebuggerTask(TargetKind.TOGGLE.index, base, | |
151 sentence.targetName); | |
152 break; | |
153 case TargetKind.FILE: | |
154 task = new DebuggerTask(TargetKind.FILE.index, base, | |
155 sentence.targetUri); | |
156 break; | |
157 default: | |
158 throwInternalError("Unimplemented ${sentence.target}"); | |
159 } | |
160 | |
161 return context.performTaskInWorker(task); | |
162 } | |
163 | |
164 // Returns a debug client event handler that is bound to the current session. | |
165 ClientEventHandler debugClientEventHandler( | |
166 SessionState state, | |
167 StreamIterator<ClientCommand> commandIterator, | |
168 StreamController stdinController) { | |
169 // TODO(zerny): Take the correct session explicitly because it will be cleared | |
170 // later to ensure against possible reuse. Restructure the code to avoid this. | |
171 return (Session session) async { | |
172 while (await commandIterator.moveNext()) { | |
173 ClientCommand command = commandIterator.current; | |
174 switch (command.code) { | |
175 case ClientCommandCode.Stdin: | |
176 if (command.data.length == 0) { | |
177 await stdinController.close(); | |
178 } else { | |
179 stdinController.add(command.data); | |
180 } | |
181 break; | |
182 | |
183 case ClientCommandCode.Signal: | |
184 int signalNumber = command.data; | |
185 if (signalNumber == sigQuit) { | |
186 await session.interrupt(); | |
187 } else { | |
188 handleSignal(state, signalNumber); | |
189 } | |
190 break; | |
191 | |
192 default: | |
193 throwInternalError("Unexpected command from client: $command"); | |
194 } | |
195 } | |
196 }; | |
197 } | |
198 | |
199 class InteractiveDebuggerTask extends SharedTask { | |
200 // Keep this class simple, see note in superclass. | |
201 | |
202 final Uri base; | |
203 | |
204 const InteractiveDebuggerTask(this.base); | |
205 | |
206 Future<int> call( | |
207 CommandSender commandSender, | |
208 StreamIterator<ClientCommand> commandIterator) { | |
209 | |
210 // Setup a more advanced client input handler for the interactive debug task | |
211 // that also handles the input and forwards it to the debug input handler. | |
212 StreamController stdinController = new StreamController(); | |
213 SessionState state = SessionState.current; | |
214 setupClientInOut( | |
215 state, | |
216 commandSender, | |
217 debugClientEventHandler(state, commandIterator, stdinController)); | |
218 | |
219 return interactiveDebuggerTask(state, base, stdinController); | |
220 } | |
221 } | |
222 | |
223 Future<int> runInteractiveDebuggerTask( | |
224 CommandSender commandSender, | |
225 StreamIterator<ClientCommand> commandIterator, | |
226 SessionState state, | |
227 Uri script, | |
228 Uri base) { | |
229 | |
230 // Setup a more advanced client input handler for the interactive debug task | |
231 // that also handles the input and forwards it to the debug input handler. | |
232 StreamController stdinController = new StreamController(); | |
233 return compileAndAttachToVmThen( | |
234 commandSender, | |
235 commandIterator, | |
236 state, | |
237 script, | |
238 base, | |
239 true, | |
240 () => interactiveDebuggerTask(state, base, stdinController), | |
241 eventHandler: | |
242 debugClientEventHandler(state, commandIterator, stdinController)); | |
243 } | |
244 | |
245 Future<int> interactiveDebuggerTask( | |
246 SessionState state, | |
247 Uri base, | |
248 StreamController stdinController) async { | |
249 Session session = state.session; | |
250 if (session == null) { | |
251 throwFatalError(DiagnosticKind.attachToVmBeforeRun); | |
252 } | |
253 List<FletchDelta> compilationResult = state.compilationResults; | |
254 if (compilationResult.isEmpty) { | |
255 throwFatalError(DiagnosticKind.compileBeforeRun); | |
256 } | |
257 | |
258 // Make sure current state's session is not reused if invoked again. | |
259 state.session = null; | |
260 | |
261 for (FletchDelta delta in compilationResult) { | |
262 await session.applyDelta(delta); | |
263 } | |
264 | |
265 Stream<String> inputStream = stdinController.stream | |
266 .transform(UTF8.decoder) | |
267 .transform(new LineSplitter()); | |
268 | |
269 return await session.debug(inputStream, base, state); | |
270 } | |
271 | |
272 class DebuggerTask extends SharedTask { | |
273 // Keep this class simple, see note in superclass. | |
274 final int kind; | |
275 final argument; | |
276 final Uri base; | |
277 | |
278 DebuggerTask(this.kind, this.base, [this.argument]); | |
279 | |
280 Future<int> call( | |
281 CommandSender commandSender, | |
282 StreamIterator<ClientCommand> commandIterator) { | |
283 switch (TargetKind.values[kind]) { | |
284 case TargetKind.APPLY: | |
285 return apply(commandSender, SessionState.current); | |
286 case TargetKind.RUN_TO_MAIN: | |
287 return runToMainDebuggerTask(commandSender, SessionState.current); | |
288 case TargetKind.BACKTRACE: | |
289 return backtraceDebuggerTask(commandSender, SessionState.current); | |
290 case TargetKind.CONTINUE: | |
291 return continueDebuggerTask(commandSender, SessionState.current); | |
292 case TargetKind.BREAK: | |
293 return breakDebuggerTask( | |
294 commandSender, SessionState.current, argument, base); | |
295 case TargetKind.LIST: | |
296 return listDebuggerTask(commandSender, SessionState.current); | |
297 case TargetKind.DISASM: | |
298 return disasmDebuggerTask(commandSender, SessionState.current); | |
299 case TargetKind.FRAME: | |
300 return frameDebuggerTask(commandSender, SessionState.current, argument); | |
301 case TargetKind.DELETE_BREAKPOINT: | |
302 return deleteBreakpointDebuggerTask( | |
303 commandSender, SessionState.current, argument); | |
304 case TargetKind.LIST_BREAKPOINTS: | |
305 return listBreakpointsDebuggerTask(commandSender, SessionState.current); | |
306 case TargetKind.STEP: | |
307 return stepDebuggerTask(commandSender, SessionState.current); | |
308 case TargetKind.STEP_OVER: | |
309 return stepOverDebuggerTask(commandSender, SessionState.current); | |
310 case TargetKind.FIBERS: | |
311 return fibersDebuggerTask(commandSender, SessionState.current); | |
312 case TargetKind.FINISH: | |
313 return finishDebuggerTask(commandSender, SessionState.current); | |
314 case TargetKind.RESTART: | |
315 return restartDebuggerTask(commandSender, SessionState.current); | |
316 case TargetKind.STEP_BYTECODE: | |
317 return stepBytecodeDebuggerTask(commandSender, SessionState.current); | |
318 case TargetKind.STEP_OVER_BYTECODE: | |
319 return stepOverBytecodeDebuggerTask( | |
320 commandSender, SessionState.current); | |
321 case TargetKind.PRINT: | |
322 return printDebuggerTask(commandSender, SessionState.current, argument); | |
323 case TargetKind.PRINT_ALL: | |
324 return printAllDebuggerTask(commandSender, SessionState.current); | |
325 case TargetKind.TOGGLE: | |
326 return toggleDebuggerTask( | |
327 commandSender, SessionState.current, argument); | |
328 case TargetKind.FILE: | |
329 return runInteractiveDebuggerTask( | |
330 commandSender, commandIterator, SessionState.current, argument, | |
331 base); | |
332 | |
333 default: | |
334 throwInternalError("Unimplemented ${TargetKind.values[kind]}"); | |
335 } | |
336 return null; | |
337 } | |
338 } | |
339 | |
340 Session attachToSession(SessionState state, CommandSender commandSender) { | |
341 Session session = state.session; | |
342 if (session == null) { | |
343 throwFatalError(DiagnosticKind.attachToVmBeforeRun); | |
344 } | |
345 state.attachCommandSender(commandSender); | |
346 return session; | |
347 } | |
348 | |
349 Future<int> runToMainDebuggerTask( | |
350 CommandSender commandSender, | |
351 SessionState state) async { | |
352 List<FletchDelta> compilationResults = state.compilationResults; | |
353 Session session = state.session; | |
354 if (session == null) { | |
355 throwFatalError(DiagnosticKind.attachToVmBeforeRun); | |
356 } | |
357 if (session.loaded) { | |
358 // We cannot reuse a session that has already been loaded. Loading | |
359 // currently implies that some of the code has been run. | |
360 throwFatalError(DiagnosticKind.sessionInvalidState, | |
361 sessionName: state.name); | |
362 } | |
363 if (compilationResults.isEmpty) { | |
364 throwFatalError(DiagnosticKind.compileBeforeRun); | |
365 } | |
366 | |
367 state.attachCommandSender(commandSender); | |
368 for (FletchDelta delta in compilationResults) { | |
369 await session.applyDelta(delta); | |
370 } | |
371 | |
372 await session.enableDebugger(); | |
373 await session.spawnProcess(); | |
374 await session.setBreakpoint(methodName: "main", bytecodeIndex: 0); | |
375 await session.debugRun(); | |
376 | |
377 return 0; | |
378 } | |
379 | |
380 Future<int> backtraceDebuggerTask( | |
381 CommandSender commandSender, | |
382 SessionState state) async { | |
383 Session session = attachToSession(state, commandSender); | |
384 | |
385 if (!session.loaded) { | |
386 throwInternalError('### process not loaded, cannot show backtrace'); | |
387 } | |
388 BackTrace trace = await session.backTrace(); | |
389 print(trace.format()); | |
390 | |
391 return 0; | |
392 } | |
393 | |
394 Future<int> continueDebuggerTask( | |
395 CommandSender commandSender, | |
396 SessionState state) async { | |
397 Session session = attachToSession(state, commandSender); | |
398 | |
399 if (!session.running) { | |
400 // TODO(ager, lukechurch): Fix error reporting. | |
401 throwInternalError('### process not running, cannot continue'); | |
402 } | |
403 VmCommand response = await session.cont(); | |
404 print(await session.processStopResponseToString(response, state)); | |
405 | |
406 if (session.terminated) state.session = null; | |
407 | |
408 return 0; | |
409 } | |
410 | |
411 Future<int> breakDebuggerTask( | |
412 CommandSender commandSender, | |
413 SessionState state, | |
414 String breakpointSpecification, | |
415 Uri base) async { | |
416 Session session = attachToSession(state, commandSender); | |
417 | |
418 if (breakpointSpecification.contains('@')) { | |
419 List<String> parts = breakpointSpecification.split('@'); | |
420 | |
421 if (parts.length > 2) { | |
422 // TODO(ager, lukechurch): Fix error reporting. | |
423 throwInternalError('Invalid breakpoint format'); | |
424 } | |
425 | |
426 String name = parts[0]; | |
427 int index = 0; | |
428 | |
429 if (parts.length == 2) { | |
430 index = int.parse(parts[1], onError: (_) => -1); | |
431 if (index == -1) { | |
432 // TODO(ager, lukechurch): Fix error reporting. | |
433 throwInternalError('Invalid bytecode index'); | |
434 } | |
435 } | |
436 | |
437 List<Breakpoint> breakpoints = | |
438 await session.setBreakpoint(methodName: name, bytecodeIndex: index); | |
439 for (Breakpoint breakpoint in breakpoints) { | |
440 print("Breakpoint set: $breakpoint"); | |
441 } | |
442 } else if (breakpointSpecification.contains(':')) { | |
443 List<String> parts = breakpointSpecification.split(':'); | |
444 | |
445 if (parts.length != 3) { | |
446 // TODO(ager, lukechurch): Fix error reporting. | |
447 throwInternalError('Invalid breakpoint format'); | |
448 } | |
449 | |
450 String file = parts[0]; | |
451 int line = int.parse(parts[1], onError: (_) => -1); | |
452 int column = int.parse(parts[2], onError: (_) => -1); | |
453 | |
454 if (line < 1 || column < 1) { | |
455 // TODO(ager, lukechurch): Fix error reporting. | |
456 throwInternalError('Invalid line or column number'); | |
457 } | |
458 | |
459 // TODO(ager): Refactor session so that setFileBreakpoint | |
460 // does not print automatically but gives us information about what | |
461 // happened. | |
462 Breakpoint breakpoint = | |
463 await session.setFileBreakpoint(base.resolve(file), line, column); | |
464 if (breakpoint != null) { | |
465 print("Breakpoint set: $breakpoint"); | |
466 } else { | |
467 // TODO(ager, lukechurch): Fix error reporting. | |
468 throwInternalError('Failed to set breakpoint'); | |
469 } | |
470 } else { | |
471 List<Breakpoint> breakpoints = | |
472 await session.setBreakpoint( | |
473 methodName: breakpointSpecification, bytecodeIndex: 0); | |
474 for (Breakpoint breakpoint in breakpoints) { | |
475 print("Breakpoint set: $breakpoint"); | |
476 } | |
477 } | |
478 | |
479 return 0; | |
480 } | |
481 | |
482 Future<int> listDebuggerTask( | |
483 CommandSender commandSender, SessionState state) async { | |
484 Session session = attachToSession(state, commandSender); | |
485 | |
486 if (!session.loaded) { | |
487 throwInternalError('### process not loaded, nothing to list'); | |
488 } | |
489 BackTrace trace = await session.backTrace(); | |
490 if (trace == null) { | |
491 // TODO(ager,lukechurch): Fix error reporting. | |
492 throwInternalError('Source listing failed'); | |
493 } | |
494 print(trace.list(state)); | |
495 | |
496 return 0; | |
497 } | |
498 | |
499 Future<int> disasmDebuggerTask( | |
500 CommandSender commandSender, SessionState state) async { | |
501 Session session = attachToSession(state, commandSender); | |
502 | |
503 if (!session.loaded) { | |
504 throwInternalError('### process not loaded, nothing to disassemble'); | |
505 } | |
506 BackTrace trace = await session.backTrace(); | |
507 if (trace == null) { | |
508 // TODO(ager,lukechurch): Fix error reporting. | |
509 throwInternalError('Bytecode disassembly failed'); | |
510 } | |
511 print(trace.disasm()); | |
512 | |
513 return 0; | |
514 } | |
515 | |
516 Future<int> frameDebuggerTask( | |
517 CommandSender commandSender, SessionState state, String frame) async { | |
518 Session session = attachToSession(state, commandSender); | |
519 | |
520 int frameNumber = int.parse(frame, onError: (_) => -1); | |
521 if (frameNumber == -1) { | |
522 // TODO(ager,lukechurch): Fix error reporting. | |
523 throwInternalError('Invalid frame number'); | |
524 } | |
525 | |
526 bool frameSelected = await session.selectFrame(frameNumber); | |
527 if (!frameSelected) { | |
528 // TODO(ager,lukechurch): Fix error reporting. | |
529 throwInternalError('Frame selection failed'); | |
530 } | |
531 | |
532 return 0; | |
533 } | |
534 | |
535 Future<int> deleteBreakpointDebuggerTask( | |
536 CommandSender commandSender, SessionState state, String breakpoint) async { | |
537 Session session = attachToSession(state, commandSender); | |
538 | |
539 int id = int.parse(breakpoint, onError: (_) => -1); | |
540 if (id == -1) { | |
541 // TODO(ager,lukechurch): Fix error reporting. | |
542 throwInternalError('Invalid breakpoint id: $breakpoint'); | |
543 } | |
544 | |
545 Breakpoint bp = await session.deleteBreakpoint(id); | |
546 if (bp == null) { | |
547 throwInternalError('Invalid breakpoint id: $id'); | |
548 } | |
549 print('Deleted breakpoint: $bp'); | |
550 return 0; | |
551 } | |
552 | |
553 Future<int> listBreakpointsDebuggerTask( | |
554 CommandSender commandSender, SessionState state) async { | |
555 Session session = attachToSession(state, commandSender); | |
556 List<Breakpoint> breakpoints = session.breakpoints(); | |
557 if (breakpoints == null || breakpoints.isEmpty) { | |
558 print('No breakpoints'); | |
559 } else { | |
560 print('Breakpoints:'); | |
561 for (Breakpoint bp in breakpoints) { | |
562 print(bp); | |
563 } | |
564 } | |
565 return 0; | |
566 } | |
567 | |
568 Future<int> stepDebuggerTask( | |
569 CommandSender commandSender, SessionState state) async { | |
570 Session session = attachToSession(state, commandSender); | |
571 if (!session.running) { | |
572 throwInternalError( | |
573 '### process not running, cannot step to next expression'); | |
574 } | |
575 VmCommand response = await session.step(); | |
576 print(await session.processStopResponseToString(response, state)); | |
577 return 0; | |
578 } | |
579 | |
580 Future<int> stepOverDebuggerTask( | |
581 CommandSender commandSender, SessionState state) async { | |
582 Session session = attachToSession(state, commandSender); | |
583 if (!session.running) { | |
584 throwInternalError('### process not running, cannot go to next expression'); | |
585 } | |
586 VmCommand response = await session.stepOver(); | |
587 print(await session.processStopResponseToString(response, state)); | |
588 return 0; | |
589 } | |
590 | |
591 Future<int> fibersDebuggerTask( | |
592 CommandSender commandSender, SessionState state) async { | |
593 Session session = attachToSession(state, commandSender); | |
594 if (!session.running) { | |
595 throwInternalError('### process not running, cannot show fibers'); | |
596 } | |
597 List<BackTrace> traces = await session.fibers(); | |
598 print(''); | |
599 for (int fiber = 0; fiber < traces.length; ++fiber) { | |
600 print('fiber $fiber'); | |
601 print(traces[fiber].format()); | |
602 } | |
603 return 0; | |
604 } | |
605 | |
606 Future<int> finishDebuggerTask( | |
607 CommandSender commandSender, SessionState state) async { | |
608 Session session = attachToSession(state, commandSender); | |
609 if (!session.running) { | |
610 throwInternalError('### process not running, cannot finish method'); | |
611 } | |
612 VmCommand response = await session.stepOut(); | |
613 print(await session.processStopResponseToString(response, state)); | |
614 return 0; | |
615 } | |
616 | |
617 Future<int> restartDebuggerTask( | |
618 CommandSender commandSender, SessionState state) async { | |
619 Session session = attachToSession(state, commandSender); | |
620 if (!session.loaded) { | |
621 throwInternalError('### process not loaded, cannot restart'); | |
622 } | |
623 BackTrace trace = await session.backTrace(); | |
624 if (trace == null) { | |
625 throwInternalError("### cannot restart when nothing is executing."); | |
626 } | |
627 if (trace.length <= 1) { | |
628 throwInternalError("### cannot restart entry frame."); | |
629 } | |
630 VmCommand response = await session.restart(); | |
631 print(await session.processStopResponseToString(response, state)); | |
632 return 0; | |
633 } | |
634 | |
635 Future<int> apply( | |
636 CommandSender commandSender, SessionState state) async { | |
637 Session session = attachToSession(state, commandSender); | |
638 await session.applyDelta(state.compilationResults.last); | |
639 return 0; | |
640 } | |
641 | |
642 Future<int> stepBytecodeDebuggerTask( | |
643 CommandSender commandSender, SessionState state) async { | |
644 Session session = attachToSession(state, commandSender); | |
645 if (!session.running) { | |
646 throwInternalError('### process not running, cannot step bytecode'); | |
647 } | |
648 VmCommand response = await session.stepBytecode(); | |
649 assert(response != null); // stepBytecode cannot return null | |
650 print(await session.processStopResponseToString(response, state)); | |
651 return 0; | |
652 } | |
653 | |
654 Future<int> stepOverBytecodeDebuggerTask( | |
655 CommandSender commandSender, SessionState state) async { | |
656 Session session = attachToSession(state, commandSender); | |
657 if (!session.running) { | |
658 throwInternalError('### process not running, cannot step over bytecode'); | |
659 } | |
660 VmCommand response = await session.stepOverBytecode(); | |
661 assert(response != null); // stepOverBytecode cannot return null | |
662 print(await session.processStopResponseToString(response, state)); | |
663 return 0; | |
664 } | |
665 | |
666 Future<int> printDebuggerTask( | |
667 CommandSender commandSender, SessionState state, String name) async { | |
668 Session session = attachToSession(state, commandSender); | |
669 | |
670 RemoteObject variable; | |
671 if (name.startsWith("*")) { | |
672 name = name.substring(1); | |
673 variable = await session.processVariableStructure(name); | |
674 } else { | |
675 variable = await session.processVariable(name); | |
676 } | |
677 if (variable == null) { | |
678 print('### No such variable: $name'); | |
679 } else { | |
680 print(session.remoteObjectToString(variable)); | |
681 } | |
682 | |
683 return 0; | |
684 } | |
685 | |
686 Future<int> printAllDebuggerTask( | |
687 CommandSender commandSender, SessionState state) async { | |
688 Session session = attachToSession(state, commandSender); | |
689 List<RemoteObject> variables = await session.processAllVariables(); | |
690 if (variables.isEmpty) { | |
691 print('### No variables in scope'); | |
692 } else { | |
693 for (RemoteObject variable in variables) { | |
694 print(session.remoteObjectToString(variable)); | |
695 } | |
696 } | |
697 return 0; | |
698 } | |
699 | |
700 Future<int> toggleDebuggerTask( | |
701 CommandSender commandSender, SessionState state, String argument) async { | |
702 Session session = attachToSession(state, commandSender); | |
703 | |
704 if (argument != 'internal') { | |
705 // TODO(ager, lukechurch): Fix error reporting. | |
706 throwInternalError("Invalid argument to toggle. " | |
707 "Valid arguments: 'internal'."); | |
708 } | |
709 await session.toggleInternal(); | |
710 | |
711 return 0; | |
712 } | |
OLD | NEW |