| 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 |