Index: pkg/fletchc/lib/input_handler.dart |
diff --git a/pkg/fletchc/lib/input_handler.dart b/pkg/fletchc/lib/input_handler.dart |
deleted file mode 100644 |
index c21f3fcbb665ae4d8eed7d549992ed9d7d69ea9f..0000000000000000000000000000000000000000 |
--- a/pkg/fletchc/lib/input_handler.dart |
+++ /dev/null |
@@ -1,514 +0,0 @@ |
-// Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE.md file. |
- |
-part of fletch.vm_session; |
- |
-const String BANNER = """ |
-Starting session. Type 'help' for a list of commands. |
-"""; |
- |
-const String HELP = """ |
-Commands: |
- 'help' show list of commands |
- 'r'/'run' start program |
- 'b [method name] [bytecode index]' set breakpoint |
- 'bf <file> [line] [column]' set breakpoint |
- 'bf <file> [line] [pattern]' set breakpoint on first occurrence of |
- the string pattern on the indicated line |
- 'd <breakpoint id>' delete breakpoint |
- 'lb' list breakpoints |
- 's' step until next expression, |
- enters method invocations |
- 'n' step until next expression, |
- does not enter method invocations |
- 'fibers', 'lf' list all process fibers |
- 'finish' finish current method (step out) |
- 'restart' restart the selected frame |
- 'sb' step bytecode, enters method invocations |
- 'nb' step over bytecode, does not enter method invocations |
- 'c' continue execution |
- 'bt' backtrace |
- 'f <n>' select frame |
- 'l' list source for frame |
- 'p <name>' print the value of local variable |
- 'p *<name>' print the structure of local variable |
- 'p' print the values of all locals |
- 'processes', 'lp' list all processes |
- 'disasm' disassemble code for frame |
- 't <flag>' toggle one of the flags: |
- - 'internal' : show internal frames |
- 'q'/'quit' quit the session |
-"""; |
- |
-class InputHandler { |
- final Session session; |
- final Stream<String> stream; |
- final bool echo; |
- final Uri base; |
- |
- String previousLine = ''; |
- |
- int processPagingCount = 10; |
- int processPagingCurrent = 0; |
- |
- InputHandler(this.session, this.stream, this.echo, this.base); |
- |
- void printPrompt() => session.writeStdout('> '); |
- |
- writeStdout(String s) => session.writeStdout(s); |
- |
- writeStdoutLine(String s) => session.writeStdout("$s\n"); |
- |
- Future handleLine(StreamIterator stream, SessionState state) async { |
- String line = stream.current; |
- if (line.isEmpty) line = previousLine; |
- if (line.isEmpty) { |
- printPrompt(); |
- return; |
- } |
- if (echo) writeStdoutLine(line); |
- List<String> commandComponents = |
- line.split(' ').where((s) => s.isNotEmpty).toList(); |
- String command = commandComponents[0]; |
- switch (command) { |
- case 'help': |
- writeStdoutLine(HELP); |
- break; |
- case 'b': |
- var method = |
- (commandComponents.length > 1) ? commandComponents[1] : 'main'; |
- var bci = |
- (commandComponents.length > 2) ? commandComponents[2] : '0'; |
- bci = int.parse(bci, onError: (_) => null); |
- if (bci == null) { |
- writeStdoutLine('### invalid bytecode index: $bci'); |
- break; |
- } |
- List<Breakpoint> breakpoints = |
- await session.setBreakpoint(methodName: method, bytecodeIndex: bci); |
- if (breakpoints != null) { |
- for (Breakpoint breakpoint in breakpoints) { |
- writeStdoutLine("breakpoint set: $breakpoint"); |
- } |
- } else { |
- writeStdoutLine( |
- "### failed to set breakpoint at method: $method index: $bci"); |
- } |
- break; |
- case 'bf': |
- var file = |
- (commandComponents.length > 1) ? commandComponents[1] : ''; |
- var line = |
- (commandComponents.length > 2) ? commandComponents[2] : '1'; |
- var columnOrPattern = |
- (commandComponents.length > 3) ? commandComponents[3] : '1'; |
- |
- List<Uri> files = <Uri>[]; |
- |
- if (await new File.fromUri(base.resolve(file)).exists()) { |
- // If the supplied file resolved directly to a file use it. |
- files.add(base.resolve(file)); |
- } else { |
- // Otherwise search for possible matches. |
- List<Uri> matches = session.findSourceFiles(file).toList()..sort( |
- (a, b) => a.toString().compareTo(b.toString())); |
- Iterable<int> selection = await select( |
- stream, |
- "Multiple matches for file pattern $file", |
- matches.map((uri) => |
- uri.toString().replaceFirst(base.toString(), ''))); |
- for (int selected in selection) { |
- files.add(matches.elementAt(selected)); |
- } |
- } |
- |
- if (files.isEmpty) { |
- writeStdoutLine('### no matching file found for: $file'); |
- break; |
- } |
- |
- line = int.parse(line, onError: (_) => null); |
- if (line == null || line < 1) { |
- writeStdoutLine('### invalid line number: $line'); |
- break; |
- } |
- |
- List<Breakpoint> breakpoints = <Breakpoint>[]; |
- int columnNumber = int.parse(columnOrPattern, onError: (_) => null); |
- if (columnNumber == null) { |
- for (Uri fileUri in files) { |
- Breakpoint breakpoint = await session.setFileBreakpointFromPattern( |
- fileUri, line, columnOrPattern); |
- if (breakpoint == null) { |
- writeStdoutLine( |
- '### failed to set breakpoint for pattern $columnOrPattern ' + |
- 'on $fileUri:$line'); |
- } else { |
- breakpoints.add(breakpoint); |
- } |
- } |
- } else if (columnNumber < 1) { |
- writeStdoutLine('### invalid column number: $columnOrPattern'); |
- break; |
- } else { |
- for (Uri fileUri in files) { |
- Breakpoint breakpoint = |
- await session.setFileBreakpoint(fileUri, line, columnNumber); |
- if (breakpoint == null) { |
- writeStdoutLine( |
- '### failed to set breakpoint ' + |
- 'on $fileUri:$line:$columnNumber'); |
- } else { |
- breakpoints.add(breakpoint); |
- } |
- } |
- } |
- if (breakpoints.isNotEmpty) { |
- for (Breakpoint breakpoint in breakpoints) { |
- writeStdoutLine("breakpoint set: $breakpoint"); |
- } |
- } else { |
- writeStdoutLine( |
- "### failed to set any breakpoints"); |
- } |
- break; |
- case 'bt': |
- if (!checkLoaded('cannot print backtrace')) { |
- break; |
- } |
- BackTrace backtrace = await session.backTrace(); |
- if (backtrace == null) { |
- writeStdoutLine('### failed to get backtrace for current program'); |
- } else { |
- writeStdout(backtrace.format()); |
- } |
- break; |
- case 'f': |
- var frame = |
- (commandComponents.length > 1) ? commandComponents[1] : "-1"; |
- frame = int.parse(frame, onError: (_) => null); |
- if (frame == null || !session.selectFrame(frame)) { |
- writeStdoutLine('### invalid frame number: $frame'); |
- } |
- break; |
- case 'l': |
- if (!checkLoaded('nothing to list')) { |
- break; |
- } |
- BackTrace trace = await session.backTrace(); |
- String listing = trace != null ? trace.list(state) : null; |
- if (listing != null) { |
- writeStdoutLine(listing); |
- } else { |
- writeStdoutLine("### failed listing source"); |
- } |
- break; |
- case 'disasm': |
- if (checkLoaded('cannot show bytecodes')) { |
- BackTrace backtrace = await session.backTrace(); |
- String disassembly = backtrace != null ? backtrace.disasm() : null; |
- if (disassembly != null) { |
- writeStdout(disassembly); |
- } else { |
- writeStdoutLine( |
- "### could not disassemble source for current frame"); |
- } |
- } |
- break; |
- case 'c': |
- if (checkRunning('cannot continue')) { |
- await handleProcessStopResponse(await session.cont(), state); |
- } |
- break; |
- case 'd': |
- var id = (commandComponents.length > 1) ? commandComponents[1] : null; |
- id = int.parse(id, onError: (_) => null); |
- if (id == null) { |
- writeStdoutLine('### invalid breakpoint number: $id'); |
- break; |
- } |
- Breakpoint breakpoint = await session.deleteBreakpoint(id); |
- if (breakpoint == null) { |
- writeStdoutLine("### invalid breakpoint id: $id"); |
- break; |
- } |
- writeStdoutLine("### deleted breakpoint: $breakpoint"); |
- break; |
- case 'processes': |
- case 'lp': |
- if (checkRunning('cannot list processes')) { |
- // Reset current paging point if not continuing from an 'lp' command. |
- if (previousLine != 'lp' && previousLine != 'processes') { |
- processPagingCurrent = 0; |
- } |
- |
- List<int> processes = await session.processes(); |
- processes.sort(); |
- |
- int count = processes.length; |
- int start = processPagingCurrent; |
- int end; |
- if (start + processPagingCount < count) { |
- processPagingCurrent += processPagingCount; |
- end = processPagingCurrent; |
- } else { |
- processPagingCurrent = 0; |
- end = count; |
- } |
- |
- if (processPagingCount < count) { |
- writeStdout("displaying range [$start;${end-1}] "); |
- writeStdoutLine("of $count processes"); |
- } |
- for (int i = start; i < end; ++i) { |
- int processId = processes[i]; |
- BackTrace stack = await session.processStack(processId); |
- writeStdoutLine('\nprocess ${processId}'); |
- writeStdout(stack.format()); |
- } |
- writeStdoutLine(''); |
- } |
- break; |
- case 'fibers': |
- case 'lf': |
- if (checkRunning('cannot show fibers')) { |
- List<BackTrace> traces = await session.fibers(); |
- for (int fiber = 0; fiber < traces.length; ++fiber) { |
- writeStdoutLine('\nfiber $fiber'); |
- writeStdout(traces[fiber].format()); |
- } |
- writeStdoutLine(''); |
- } |
- break; |
- case 'finish': |
- if (checkRunning('cannot finish method')) { |
- await handleProcessStopResponse(await session.stepOut(), state); |
- } |
- break; |
- case 'restart': |
- if (!checkLoaded('cannot restart')) { |
- break; |
- } |
- BackTrace trace = await session.backTrace(); |
- if (trace == null) { |
- writeStdoutLine("### cannot restart when nothing is executing"); |
- break; |
- } |
- if (trace.length <= 1) { |
- writeStdoutLine("### cannot restart entry frame"); |
- break; |
- } |
- await handleProcessStopResponse(await session.restart(), state); |
- break; |
- case 'lb': |
- List<Breakpoint> breakpoints = session.breakpoints(); |
- if (breakpoints == null || breakpoints.isEmpty) { |
- writeStdoutLine('### no breakpoints'); |
- } else { |
- writeStdoutLine("### breakpoints:"); |
- for (var bp in breakpoints) { |
- writeStdoutLine('$bp'); |
- } |
- } |
- break; |
- case 'p': |
- if (!checkLoaded('nothing to print')) { |
- break; |
- } |
- if (commandComponents.length <= 1) { |
- List<RemoteObject> variables = await session.processAllVariables(); |
- if (variables.isEmpty) { |
- writeStdoutLine('### No variables in scope'); |
- } else { |
- for (RemoteObject variable in variables) { |
- writeStdoutLine(session.remoteObjectToString(variable)); |
- } |
- } |
- break; |
- } |
- String variableName = commandComponents[1]; |
- RemoteObject variable; |
- if (variableName.startsWith('*')) { |
- variableName = variableName.substring(1); |
- variable = await session.processVariableStructure(variableName); |
- } else { |
- variable = await session.processVariable(variableName); |
- } |
- if (variable == null) { |
- writeStdoutLine('### no such variable: $variableName'); |
- } else { |
- writeStdoutLine(session.remoteObjectToString(variable)); |
- } |
- break; |
- case 'q': |
- case 'quit': |
- await session.terminateSession(); |
- break; |
- case 'r': |
- case 'run': |
- if (checkNotLoaded("use 'restart' to run again")) { |
- await handleProcessStopResponse(await session.debugRun(), state); |
- } |
- break; |
- case 's': |
- if (checkRunning('cannot step to next expression')) { |
- await handleProcessStopResponse(await session.step(), state); |
- } |
- break; |
- case 'n': |
- if (checkRunning('cannot go to next expression')) { |
- await handleProcessStopResponse(await session.stepOver(), state); |
- } |
- break; |
- case 'sb': |
- if (checkRunning('cannot step bytecode')) { |
- await handleProcessStopResponse(await session.stepBytecode(), state); |
- } |
- break; |
- case 'nb': |
- if (checkRunning('cannot step over bytecode')) { |
- await handleProcessStopResponse( |
- await session.stepOverBytecode(), state); |
- } |
- break; |
- case 't': |
- String toggle; |
- if (commandComponents.length > 1) { |
- toggle = commandComponents[1]; |
- } |
- switch (toggle) { |
- case 'internal': |
- bool internalVisible = session.toggleInternal(); |
- writeStdoutLine( |
- '### internal frame visibility set to: $internalVisible'); |
- break; |
- case 'verbose': |
- bool verbose = session.toggleVerbose(); |
- writeStdoutLine('### verbose printing set to: $verbose'); |
- break; |
- default: |
- writeStdoutLine('### invalid flag $toggle'); |
- break; |
- } |
- break; |
- default: |
- writeStdoutLine('### unknown command: $command'); |
- break; |
- } |
- previousLine = line; |
- if (!session.terminated) printPrompt(); |
- } |
- |
- // This method is used to deal with the stopped process command responses |
- // that can be returned when sending the Fletch VM a command request. |
- Future handleProcessStopResponse( |
- VmCommand response, |
- SessionState state) async { |
- String output = await session.processStopResponseToString(response, state); |
- if (output != null && output.isNotEmpty) { |
- writeStdout(output); |
- } |
- } |
- |
- bool checkLoaded([String postfix]) { |
- if (!session.loaded) { |
- String prefix = '### process not loaded'; |
- writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix); |
- } |
- return session.loaded; |
- } |
- |
- bool checkNotLoaded([String postfix]) { |
- if (session.loaded) { |
- String prefix = '### process already loaded'; |
- writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix); |
- } |
- return !session.loaded; |
- } |
- |
- bool checkRunning([String postfix]) { |
- if (!session.running) { |
- String prefix = '### process not running'; |
- writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix); |
- } |
- return session.running; |
- } |
- |
- bool checkNotRunning([String postfix]) { |
- if (session.running) { |
- String prefix = '### process already running'; |
- writeStdoutLine(postfix != null ? '$prefix, $postfix' : prefix); |
- } |
- return !session.running; |
- } |
- |
- Future<int> run(SessionState state) async { |
- writeStdoutLine(BANNER); |
- printPrompt(); |
- StreamIterator streamIterator = new StreamIterator(stream); |
- while (await streamIterator.moveNext()) { |
- try { |
- await handleLine(streamIterator, state); |
- } catch (e, s) { |
- Future cancel = streamIterator.cancel()?.catchError((_) {}); |
- if (!session.terminated) { |
- await session.terminateSession().catchError((_) {}); |
- } |
- await cancel; |
- return new Future.error(e, s); |
- } |
- if (session.terminated) { |
- await streamIterator.cancel(); |
- } |
- } |
- if (!session.terminated) await session.terminateSession(); |
- return 0; |
- } |
- |
- // Prompt the user to select among a set of choices. |
- // Returns a set of indexes that are the chosen indexes from the input set. |
- // If the size of choices is less then two, then the result is that the full |
- // input set is selected without prompting the user. Otherwise the user is |
- // interactively prompted to choose a selection. |
- Future<Iterable<int>> select( |
- StreamIterator stream, |
- String message, |
- Iterable<String> choices) async { |
- int length = choices.length; |
- if (length == 0) return <int>[]; |
- if (length == 1) return <int>[0]; |
- writeStdout("$message. "); |
- writeStdoutLine("Please select from the following choices:"); |
- int i = 1; |
- int pad = 2 + "$length".length; |
- for (String choice in choices) { |
- writeStdoutLine("${i++}".padLeft(pad) + ": $choice"); |
- } |
- writeStdoutLine('a'.padLeft(pad) + ": all of the above"); |
- writeStdoutLine('n'.padLeft(pad) + ": none of the above"); |
- while (true) { |
- printPrompt(); |
- bool hasNext = await stream.moveNext(); |
- if (!hasNext) { |
- writeStdoutLine("### failed to read choice input"); |
- return <int>[]; |
- } |
- String line = stream.current; |
- if (echo) writeStdoutLine(line); |
- if (line == 'n') { |
- return <int>[]; |
- } |
- if (line == 'a') { |
- return new List<int>.generate(length, (i) => i); |
- } |
- int choice = int.parse(line, onError: (_) => 0); |
- if (choice > 0 && choice <= length) { |
- return <int>[choice - 1]; |
- } |
- writeStdoutLine("Invalid choice: $choice"); |
- writeStdoutLine("Please select a number between 1 and $length, " + |
- "'a' for all, or 'n' for none."); |
- } |
- } |
-} |