Index: pkg/fletchc/lib/vm_session.dart |
diff --git a/pkg/fletchc/lib/vm_session.dart b/pkg/fletchc/lib/vm_session.dart |
deleted file mode 100644 |
index 94757fe153afe595f89fd0fb4b373275a9948c95..0000000000000000000000000000000000000000 |
--- a/pkg/fletchc/lib/vm_session.dart |
+++ /dev/null |
@@ -1,868 +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. |
- |
-library fletch.vm_session; |
- |
-import 'dart:core'; |
-import 'dart:async'; |
-import 'dart:convert'; |
-import 'dart:io' hide exit; |
- |
-import 'dart:typed_data' show |
- ByteData, |
- Uint8List; |
- |
-import 'vm_commands.dart'; |
-import 'fletch_system.dart'; |
- |
-import 'incremental/fletchc_incremental.dart' |
- show IncrementalCompiler; |
- |
-import 'src/codegen_visitor.dart'; |
-import 'src/debug_info.dart'; |
- |
-// TODO(ahe): Get rid of this import. |
-import 'src/fletch_backend.dart' show FletchBackend; |
- |
-import 'src/fletch_selector.dart' show |
- FletchSelector, |
- SelectorKind; |
- |
-import 'debug_state.dart'; |
- |
-import 'src/shared_command_infrastructure.dart' show |
- CommandTransformerBuilder, |
- toUint8ListView; |
- |
-import 'src/hub/session_manager.dart' show |
- SessionState; |
- |
-part 'vm_command_reader.dart'; |
-part 'input_handler.dart'; |
- |
-/// Encapsulates a TCP connection to a running fletch-vm and provides a |
-/// [VmCommand] based view on top of it. |
-class FletchVmSession { |
- /// The outgoing connection to the fletch-vm. |
- final StreamSink<List<int>> _outgoingSink; |
- |
- final Sink<List<int>> stdoutSink; |
- final Sink<List<int>> stderrSink; |
- |
- /// The VM command reader reads data from the vm, converts the data |
- /// into a [VmCommand], and provides a stream iterator iterating over |
- /// these commands. |
- /// If the [VmCommand] is for stdout or stderr the reader automatically |
- /// forwards them to the stdout/stderr sinks and does not add them to the |
- /// iterator. |
- final VmCommandReader _commandReader; |
- |
- /// Completes when the underlying TCP connection is terminated. |
- final Future _done; |
- |
- bool _connectionIsDead = false; |
- bool _drainedIncomingCommands = false; |
- |
- // TODO(ahe): Get rid of this. See also issue 67. |
- bool silent = false; |
- |
- /// When true, messages generated by this class (or its subclasses) will not |
- /// contain IDs. Note: we only hide these IDs for debugger tests that use |
- /// golden files. |
- bool hideRawIds = false; |
- |
- /// When true, don't use colors to highlight focus when printing code. |
- /// This is currently only true when running tests to avoid having to deal |
- /// with color control characters in the expected files. |
- bool colorsDisabled = false; |
- |
- VmCommand connectionError = new ConnectionError("Connection is closed", null); |
- |
- FletchVmSession(Socket vmSocket, |
- Sink<List<int>> stdoutSink, |
- Sink<List<int>> stderrSink) |
- : _outgoingSink = vmSocket, |
- this.stdoutSink = stdoutSink, |
- this.stderrSink = stderrSink, |
- _done = vmSocket.done, |
- _commandReader = new VmCommandReader(vmSocket, stdoutSink, stderrSink) { |
- _done.catchError((_, __) {}).then((_) { |
- _connectionIsDead = true; |
- }); |
- } |
- |
- void writeStdout(String s) { |
- if (!silent && stdoutSink != null) stdoutSink.add(UTF8.encode(s)); |
- } |
- |
- void writeStdoutLine(String s) => writeStdout("$s\n"); |
- |
- /// Convenience around [runCommands] for running just a single command. |
- Future<VmCommand> runCommand(VmCommand command) { |
- return runCommands([command]); |
- } |
- |
- /// Sends the given commands to a fletch-vm and reads response commands |
- /// (if necessary). |
- /// |
- /// If all commands have been successfully applied and responses been awaited, |
- /// this function will complete with the last received [VmCommand] from the |
- /// remote peer (or `null` if there was none). |
- Future<VmCommand> runCommands(List<VmCommand> commands) async { |
- if (commands.any((VmCommand c) => c.numberOfResponsesExpected == null)) { |
- throw new ArgumentError( |
- 'The runComands() method will read response commands and therefore ' |
- 'needs to know how many to read. One of the given commands does' |
- 'not specify how many commands the response will have.'); |
- } |
- |
- VmCommand lastResponse; |
- for (VmCommand command in commands) { |
- await sendCommand(command); |
- for (int i = 0; i < command.numberOfResponsesExpected; i++) { |
- lastResponse = await readNextCommand(); |
- } |
- } |
- return lastResponse; |
- } |
- |
- /// Sends all given [VmCommand]s to a fletch-vm. |
- Future sendCommands(List<VmCommand> commands) async { |
- for (var command in commands) { |
- await sendCommand(command); |
- } |
- } |
- |
- /// Sends a [VmCommand] to a fletch-vm. |
- Future sendCommand(VmCommand command) async { |
- if (_connectionIsDead) { |
- throw new StateError( |
- 'Trying to send command ${command} to fletch-vm, but ' |
- 'the connection is already closed.'); |
- } |
- command.addTo(_outgoingSink); |
- } |
- |
- /// Will read the next [VmCommand] the fletch-vm sends to us. |
- Future<VmCommand> readNextCommand({bool force: true}) async { |
- if (_drainedIncomingCommands) { |
- return connectionError; |
- } |
- |
- _drainedIncomingCommands = !await _commandReader.iterator.moveNext() |
- .catchError((error, StackTrace trace) { |
- connectionError = new ConnectionError(error, trace); |
- return false; |
- }); |
- |
- if (_drainedIncomingCommands && force) { |
- return connectionError; |
- } |
- |
- return _commandReader.iterator.current; |
- } |
- |
- /// Closes the connection to the fletch-vm and drains the remaining response |
- /// commands. |
- /// |
- /// If [ignoreExtraCommands] is `false` it will throw a StateError if the |
- /// fletch-vm sent any commands. |
- Future shutdown({bool ignoreExtraCommands: false}) async { |
- await _outgoingSink.close().catchError((_) {}); |
- |
- while (!_drainedIncomingCommands) { |
- VmCommand response = await readNextCommand(force: false); |
- if (!ignoreExtraCommands && response != null) { |
- await kill(); |
- throw new StateError( |
- "Got unexpected command from fletch-vm during shutdown " |
- "($response)"); |
- } |
- } |
- |
- return _done; |
- } |
- |
- Future interrupt() { |
- return sendCommand(const ProcessDebugInterrupt()); |
- } |
- |
- /// Closes the connection to the fletch-vm. It does not wait until it shuts |
- /// down. |
- /// |
- /// This method will never complete with an exception. |
- Future kill() async { |
- _connectionIsDead = true; |
- _drainedIncomingCommands = true; |
- |
- await _outgoingSink.close().catchError((_) {}); |
- var value = _commandReader.iterator.cancel(); |
- if (value != null) { |
- await value.catchError((_) {}); |
- } |
- _drainedIncomingCommands = true; |
- } |
-} |
- |
-/// Extends a bare [FletchVmSession] with debugging functionality. |
-class Session extends FletchVmSession { |
- final IncrementalCompiler compiler; |
- final Future processExitCodeFuture; |
- |
- DebugState debugState; |
- FletchSystem fletchSystem; |
- bool loaded = false; |
- bool running = false; |
- bool terminated = false; |
- |
- Session(Socket fletchVmSocket, |
- this.compiler, |
- Sink<List<int>> stdoutSink, |
- Sink<List<int>> stderrSink, |
- [this.processExitCodeFuture]) |
- : super(fletchVmSocket, stdoutSink, stderrSink) { |
- // We send many small packages, so use no-delay. |
- fletchVmSocket.setOption(SocketOption.TCP_NODELAY, true); |
- // TODO(ajohnsen): Should only be initialized on debug()/testDebugger(). |
- debugState = new DebugState(this); |
- } |
- |
- Future applyDelta(FletchDelta delta) async { |
- VmCommand response = await runCommands(delta.commands); |
- fletchSystem = delta.system; |
- return response; |
- } |
- |
- Future<HandShakeResult> handShake(String version) async { |
- VmCommand command = await runCommand(new HandShake(version)); |
- if (command != null && command is HandShakeResult) return command; |
- return null; |
- } |
- |
- Future disableVMStandardOutput() async { |
- await runCommand(const DisableStandardOutput()); |
- } |
- |
- // Returns either a [WriteSnapshotResult] or a [ConnectionError]. |
- Future<VmCommand> writeSnapshot(String snapshotPath) async { |
- VmCommand result = await runCommand(new WriteSnapshot(snapshotPath)); |
- await shutdown(); |
- return result; |
- } |
- |
- Future enableDebugger() async { |
- await runCommand(const Debugging()); |
- } |
- |
- Future spawnProcess() async { |
- await runCommand(const ProcessSpawnForMain()); |
- } |
- |
- Future run() async { |
- await spawnProcess(); |
- loaded = true; |
- await runCommand(const ProcessRun()); |
- // NOTE: The [ProcessRun] command normally results in a |
- // [ProcessTerminated] command. But if the compiler emitted a compile time |
- // error, the fletch-vm will just halt()/exit() and we therefore get no |
- // response. |
- var command = await readNextCommand(force: false); |
- if (command != null && command is! ProcessTerminated) { |
- throw new Exception('Expected process to finish complete with ' |
- '[ProcessTerminated] but got [$command]'); |
- } |
- await shutdown(); |
- } |
- |
- Future<int> debug( |
- Stream<String> inputLines, |
- Uri base, |
- SessionState state, |
- {bool echo: false}) async { |
- await enableDebugger(); |
- await spawnProcess(); |
- return new InputHandler(this, inputLines, echo, base).run(state); |
- } |
- |
- Future terminateSession() async { |
- await runCommand(const SessionEnd()); |
- if (processExitCodeFuture != null) await processExitCodeFuture; |
- await shutdown(); |
- terminated = true; |
- } |
- |
- // This method handles the various responses a command can return to indicate |
- // the process has stopped running. |
- // The session's state is updated to match the current state of the vm. |
- Future<VmCommand> handleProcessStop(VmCommand response) async { |
- debugState.reset(); |
- switch (response.code) { |
- case VmCommandCode.UncaughtException: |
- case VmCommandCode.ProcessCompileTimeError: |
- running = false; |
- break; |
- |
- case VmCommandCode.ProcessTerminated: |
- running = false; |
- loaded = false; |
- // TODO(ahe): Let the caller terminate the session. See issue 67. |
- await terminateSession(); |
- break; |
- |
- case VmCommandCode.ConnectionError: |
- running = false; |
- loaded = false; |
- await shutdown(); |
- terminated = true; |
- break; |
- |
- case VmCommandCode.ProcessBreakpoint: |
- ProcessBreakpoint command = response; |
- var function = fletchSystem.lookupFunctionById(command.functionId); |
- debugState.topFrame = new BackTraceFrame( |
- function, command.bytecodeIndex, compiler, debugState); |
- running = true; |
- break; |
- |
- default: |
- throw new StateError( |
- "Unhandled response from Fletch VM connection: ${response.code}"); |
- |
- } |
- return response; |
- } |
- |
- Future<VmCommand> debugRun() async { |
- assert(!loaded); |
- assert(!running); |
- loaded = true; |
- running = true; |
- await sendCommand(const ProcessRun()); |
- return handleProcessStop(await readNextCommand()); |
- } |
- |
- Future setBreakpointHelper(String name, |
- FletchFunction function, |
- int bytecodeIndex) async { |
- ProcessSetBreakpoint response = await runCommands([ |
- new PushFromMap(MapId.methods, function.functionId), |
- new ProcessSetBreakpoint(bytecodeIndex), |
- ]); |
- int breakpointId = response.value; |
- var breakpoint = new Breakpoint(name, bytecodeIndex, breakpointId); |
- debugState.breakpoints[breakpointId] = breakpoint; |
- return breakpoint; |
- } |
- |
- // TODO(ager): Let setBreakpoint return a stream instead and deal with |
- // error situations such as bytecode indices that are out of bounds for |
- // some of the methods with the given name. |
- Future setBreakpoint({String methodName, int bytecodeIndex}) async { |
- Iterable<FletchFunction> functions = |
- fletchSystem.functionsWhere((f) => f.name == methodName); |
- List<Breakpoint> breakpoints = []; |
- for (FletchFunction function in functions) { |
- breakpoints.add( |
- await setBreakpointHelper(methodName, function, bytecodeIndex)); |
- } |
- return breakpoints; |
- } |
- |
- Future setFileBreakpointFromPosition(String name, |
- Uri file, |
- int position) async { |
- if (position == null) { |
- return null; |
- } |
- DebugInfo debugInfo = compiler.debugInfoForPosition( |
- file, |
- position, |
- fletchSystem); |
- if (debugInfo == null) { |
- return null; |
- } |
- SourceLocation location = debugInfo.locationForPosition(position); |
- if (location == null) { |
- return null; |
- } |
- FletchFunction function = debugInfo.function; |
- int bytecodeIndex = location.bytecodeIndex; |
- return setBreakpointHelper(function.name, function, bytecodeIndex); |
- } |
- |
- Future setFileBreakpointFromPattern(Uri file, |
- int line, |
- String pattern) async { |
- assert(line > 0); |
- int position = compiler.positionInFileFromPattern(file, line - 1, pattern); |
- return setFileBreakpointFromPosition( |
- '$file:$line:$pattern', file, position); |
- } |
- |
- Future setFileBreakpoint(Uri file, int line, int column) async { |
- assert(line > 0 && column > 0); |
- int position = compiler.positionInFile(file, line - 1, column - 1); |
- return setFileBreakpointFromPosition('$file:$line:$column', file, position); |
- } |
- |
- Future doDeleteBreakpoint(int id) async { |
- ProcessDeleteBreakpoint response = |
- await runCommand(new ProcessDeleteBreakpoint(id)); |
- assert(response.id == id); |
- } |
- |
- Future<Breakpoint> deleteBreakpoint(int id) async { |
- if (!debugState.breakpoints.containsKey(id)) { |
- return null; |
- } |
- await doDeleteBreakpoint(id); |
- return debugState.breakpoints.remove(id); |
- } |
- |
- List<Breakpoint> breakpoints() { |
- assert(debugState.breakpoints != null); |
- return debugState.breakpoints.values.toList(); |
- } |
- |
- Iterable<Uri> findSourceFiles(Pattern pattern) { |
- return compiler.findSourceFiles(pattern); |
- } |
- |
- bool stepMadeProgress(BackTraceFrame frame) { |
- return frame.functionId != debugState.topFrame.functionId || |
- frame.bytecodePointer != debugState.topFrame.bytecodePointer; |
- } |
- |
- Future<VmCommand> stepTo(int functionId, int bcp) async { |
- assert(running); |
- VmCommand response = await runCommand(new ProcessStepTo(functionId, bcp)); |
- return handleProcessStop(response); |
- } |
- |
- Future<VmCommand> step() async { |
- assert(running); |
- final SourceLocation previous = debugState.currentLocation; |
- final BackTraceFrame initialFrame = debugState.topFrame; |
- VmCommand response; |
- do { |
- int bcp = debugState.topFrame.stepBytecodePointer(previous); |
- if (bcp != -1) { |
- response = await stepTo(debugState.topFrame.functionId, bcp); |
- } else { |
- response = await stepBytecode(); |
- } |
- } while (running && |
- debugState.atLocation(previous) && |
- stepMadeProgress(initialFrame)); |
- return response; |
- } |
- |
- Future<VmCommand> stepOver() async { |
- assert(running); |
- VmCommand response; |
- final SourceLocation previous = debugState.currentLocation; |
- final BackTraceFrame initialFrame = debugState.topFrame; |
- do { |
- response = await stepOverBytecode(); |
- } while (running && |
- debugState.atLocation(previous) && |
- stepMadeProgress(initialFrame)); |
- return response; |
- } |
- |
- Future<VmCommand> stepOut() async { |
- assert(running); |
- BackTrace trace = await backTrace(); |
- // If we are at the last frame, just continue. This will either terminate |
- // the process or stop at any user configured breakpoints. |
- if (trace.visibleFrames <= 1) return cont(); |
- |
- // Since we know there is at least two visible frames at this point stepping |
- // out will hit a visible frame before the process terminates, hence we can |
- // step out until we either hit another breakpoint or a visible frame, ie. |
- // we skip internal frame and stop at the next visible frame. |
- SourceLocation return_location = trace.visibleFrame(1).sourceLocation(); |
- VmCommand response; |
- do { |
- await sendCommand(const ProcessStepOut()); |
- ProcessSetBreakpoint setBreakpoint = await readNextCommand(); |
- assert(setBreakpoint.value != -1); |
- |
- // handleProcessStop resets the debugState and sets the top frame if it |
- // hits either the above setBreakpoint or another breakpoint. |
- response = await handleProcessStop(await readNextCommand()); |
- bool success = |
- response is ProcessBreakpoint && |
- response.breakpointId == setBreakpoint.value; |
- if (!success) { |
- await doDeleteBreakpoint(setBreakpoint.value); |
- return response; |
- } |
- } while (!debugState.topFrame.isVisible); |
- if (running && debugState.atLocation(return_location)) { |
- response = await step(); |
- } |
- return response; |
- } |
- |
- Future<VmCommand> restart() async { |
- assert(loaded); |
- assert(debugState.currentBackTrace != null); |
- assert(debugState.currentBackTrace.length > 1); |
- int frame = debugState.actualCurrentFrameNumber; |
- return handleProcessStop(await runCommand(new ProcessRestartFrame(frame))); |
- } |
- |
- Future<VmCommand> stepBytecode() async { |
- assert(running); |
- return handleProcessStop(await runCommand(const ProcessStep())); |
- } |
- |
- Future<VmCommand> stepOverBytecode() async { |
- assert(running); |
- await sendCommand(const ProcessStepOver()); |
- ProcessSetBreakpoint setBreakpoint = await readNextCommand(); |
- VmCommand response = await handleProcessStop(await readNextCommand()); |
- bool success = |
- response is ProcessBreakpoint && |
- response.breakpointId == setBreakpoint.value; |
- if (!success && !terminated && setBreakpoint.value != -1) { |
- // Delete the initial one-time breakpoint as it wasn't hit. |
- await doDeleteBreakpoint(setBreakpoint.value); |
- } |
- return response; |
- } |
- |
- Future<VmCommand> cont() async { |
- assert(running); |
- return handleProcessStop(await runCommand(const ProcessContinue())); |
- } |
- |
- bool selectFrame(int frame) { |
- if (debugState.currentBackTrace == null || |
- debugState.currentBackTrace.actualFrameNumber(frame) == -1) { |
- return false; |
- } |
- debugState.currentFrame = frame; |
- return true; |
- } |
- |
- BackTrace stackTraceFromBacktraceResponse( |
- ProcessBacktrace backtraceResponse) { |
- int frames = backtraceResponse.frames; |
- BackTrace stackTrace = new BackTrace(frames, debugState); |
- for (int i = 0; i < frames; ++i) { |
- int functionId = backtraceResponse.functionIds[i]; |
- FletchFunction function = fletchSystem.lookupFunctionById(functionId); |
- if (function == null) { |
- function = const FletchFunction.missing(); |
- } |
- stackTrace.addFrame( |
- compiler, |
- new BackTraceFrame(function, |
- backtraceResponse.bytecodeIndices[i], |
- compiler, |
- debugState)); |
- } |
- return stackTrace; |
- } |
- |
- Future<RemoteObject> uncaughtException() async { |
- assert(loaded); |
- assert(!terminated); |
- if (debugState.currentUncaughtException == null) { |
- await sendCommand(const ProcessUncaughtExceptionRequest()); |
- VmCommand response = await readNextCommand(); |
- if (response is DartValue) { |
- debugState.currentUncaughtException = new RemoteValue(response); |
- } else { |
- assert(response is InstanceStructure); |
- List<DartValue> fields = await readInstanceStructureFields(response); |
- debugState.currentUncaughtException = |
- new RemoteInstance(response, fields); |
- } |
- } |
- return debugState.currentUncaughtException; |
- } |
- |
- Future<BackTrace> backTrace() async { |
- assert(loaded); |
- if (debugState.currentBackTrace == null) { |
- ProcessBacktrace backtraceResponse = |
- await runCommand(const ProcessBacktraceRequest()); |
- debugState.currentBackTrace = |
- stackTraceFromBacktraceResponse(backtraceResponse); |
- } |
- return debugState.currentBackTrace; |
- } |
- |
- Future<BackTrace> backtraceForFiber(int fiber) async { |
- ProcessBacktrace backtraceResponse = |
- await runCommand(new ProcessFiberBacktraceRequest(fiber)); |
- return stackTraceFromBacktraceResponse(backtraceResponse); |
- } |
- |
- Future<List<BackTrace>> fibers() async { |
- assert(running); |
- await runCommand(const NewMap(MapId.fibers)); |
- ProcessNumberOfStacks response = |
- await runCommand(const ProcessAddFibersToMap()); |
- int numberOfFibers = response.value; |
- List<BackTrace> stacktraces = new List(numberOfFibers); |
- for (int i = 0; i < numberOfFibers; i++) { |
- stacktraces[i] = await backtraceForFiber(i); |
- } |
- await runCommand(const DeleteMap(MapId.fibers)); |
- return stacktraces; |
- } |
- |
- Future<List<int>> processes() async { |
- assert(running); |
- ProcessGetProcessIdsResult response = |
- await runCommand(const ProcessGetProcessIds()); |
- return response.ids; |
- } |
- |
- Future<BackTrace> processStack(int processId) async { |
- assert(running); |
- ProcessBacktrace backtraceResponse = |
- await runCommand(new ProcessBacktraceRequest(processId)); |
- return stackTraceFromBacktraceResponse(backtraceResponse); |
- } |
- |
- String dartValueToString(DartValue value) { |
- if (value is Instance) { |
- Instance i = value; |
- String className = fletchSystem.lookupClassById(i.classId).name; |
- return "Instance of '$className'"; |
- } else { |
- return value.dartToString(); |
- } |
- } |
- |
- String instanceStructureToString(InstanceStructure structure, |
- List<DartValue> fields) { |
- int classId = structure.classId; |
- FletchClass klass = fletchSystem.lookupClassById(classId); |
- |
- // TODO(fletchc-team): This should be more strict and a compiler bug |
- // should be reported to the user in order for us to get a bugreport. |
- if (klass == null) { |
- return 'Unknown (Fletch compiler was unable to find exception class)'; |
- } |
- |
- StringBuffer sb = new StringBuffer(); |
- sb.writeln("Instance of '${klass.name}' {"); |
- for (int i = 0; i < structure.fields; i++) { |
- DartValue value = fields[i]; |
- String fieldName = debugState.lookupFieldName(klass, i); |
- if (fieldName == null) { |
- fieldName = '<unnamed>'; |
- } |
- sb.writeln(' $fieldName: ${dartValueToString(value)}'); |
- } |
- sb.write('}'); |
- return '$sb'; |
- } |
- |
- String noSuchMethodErrorToString(List<DartValue> nsmFields) { |
- assert(nsmFields.length == 3); |
- DartValue receiver = nsmFields[0]; |
- ClassValue receiverClass = nsmFields[1]; |
- Integer receiverSelector = nsmFields[2]; |
- FletchSelector selector = new FletchSelector(receiverSelector.value); |
- |
- String method = |
- fletchSystem.lookupSymbolBySelector(selector.encodedSelector); |
- FletchClass klass = fletchSystem.lookupClassById(receiverClass.classId); |
- // TODO(ahe): If FletchClass is a synthetic closure class, improve the |
- // message. For example, "(local) function `foo` takes 3 arguments, but |
- // called with 4" instead of "Class '<internal>' has no method named 'call' |
- // that takes 4 arguments". |
- String name = klass.name; |
- String raw = hideRawIds |
- ? "" : " (${receiverClass.classId}::${selector.encodedSelector})"; |
- // TODO(ahe): Lookup the name in the receiverClass and include that as a |
- // suggestion in the error mesage. |
- switch (selector.kind) { |
- case SelectorKind.Method: |
- return "NoSuchMethodError: Class '$name' has no method named " |
- "'$method' that takes ${selector.arity} arguments$raw"; |
- |
- case SelectorKind.Getter: |
- return "NoSuchMethodError: " |
- "Class '$name' has no getter named '$method'$raw"; |
- |
- case SelectorKind.Setter: |
- return "NoSuchMethodError: " |
- "Class '$name' has no setter named '$method'$raw"; |
- } |
- } |
- |
- Future<List<DartValue>> readInstanceStructureFields( |
- InstanceStructure structure) async { |
- List<DartValue> fields = <DartValue>[]; |
- for (int i = 0; i < structure.fields; i++) { |
- fields.add(await readNextCommand()); |
- } |
- return fields; |
- } |
- |
- String exceptionToString(RemoteObject exception) { |
- String message; |
- if (exception is RemoteValue) { |
- message = dartValueToString(exception.value); |
- } else if (exception is RemoteInstance) { |
- InstanceStructure structure = exception.instance; |
- int classId = structure.classId; |
- FletchClass klass = fletchSystem.lookupClassById(classId); |
- |
- FletchBackend backend = compiler.compiler.backend; |
- var fletchNoSuchMethodErrorClass = fletchSystem.lookupClassByElement( |
- backend.fletchNoSuchMethodErrorClass); |
- |
- if (klass == fletchNoSuchMethodErrorClass) { |
- message = noSuchMethodErrorToString(exception.fields); |
- } else { |
- message = instanceStructureToString( |
- exception.instance, exception.fields); |
- } |
- } else { |
- throw new UnimplementedError(); |
- } |
- return 'Uncaught exception: $message'; |
- } |
- |
- Future<RemoteValue> processVariable(String name) async { |
- assert(loaded); |
- LocalValue local = await lookupValue(name); |
- return local != null ? await processLocal(local) : null; |
- } |
- |
- Future<RemoteObject> processVariableStructure(String name) async { |
- assert(loaded); |
- LocalValue local = await lookupValue(name); |
- return local != null ? await processLocalStructure(local) : null; |
- } |
- |
- Future<List<RemoteObject>> processAllVariables() async { |
- assert(loaded); |
- BackTrace trace = await backTrace(); |
- ScopeInfo info = trace.scopeInfoForCurrentFrame; |
- List<RemoteObject> variables = []; |
- for (ScopeInfo current = info; |
- current != ScopeInfo.sentinel; |
- current = current.previous) { |
- variables.add(await processLocal(current.local, current.name)); |
- } |
- return variables; |
- } |
- |
- Future<LocalValue> lookupValue(String name) async { |
- assert(loaded); |
- BackTrace trace = await backTrace(); |
- return trace.scopeInfoForCurrentFrame.lookup(name); |
- } |
- |
- Future<RemoteValue> processLocal(LocalValue local, [String name]) async { |
- var actualFrameNumber = debugState.actualCurrentFrameNumber; |
- VmCommand response = await runCommand( |
- new ProcessLocal(actualFrameNumber, local.slot)); |
- assert(response is DartValue); |
- return new RemoteValue(response, name: name); |
- } |
- |
- Future<RemoteObject> processLocalStructure(LocalValue local) async { |
- var frameNumber = debugState.actualCurrentFrameNumber; |
- await sendCommand(new ProcessLocalStructure(frameNumber, local.slot)); |
- VmCommand response = await readNextCommand(); |
- if (response is DartValue) { |
- return new RemoteValue(response); |
- } else { |
- assert(response is InstanceStructure); |
- List<DartValue> fields = await readInstanceStructureFields(response); |
- return new RemoteInstance(response, fields); |
- } |
- } |
- |
- String remoteObjectToString(RemoteObject object) { |
- String message; |
- if (object is RemoteValue) { |
- message = dartValueToString(object.value); |
- } else if (object is RemoteInstance) { |
- message = instanceStructureToString(object.instance, object.fields); |
- } else { |
- throw new UnimplementedError(); |
- } |
- if (object.name != null) { |
- // Prefix with name. |
- message = "${object.name}: $message"; |
- } |
- return message; |
- } |
- |
- bool toggleInternal() { |
- debugState.showInternalFrames = !debugState.showInternalFrames; |
- if (debugState.currentBackTrace != null) { |
- debugState.currentBackTrace.visibilityChanged(); |
- } |
- return debugState.showInternalFrames; |
- } |
- |
- bool toggleVerbose() { |
- debugState.verbose = !debugState.verbose; |
- return debugState.verbose; |
- } |
- |
- // This method is a helper method for computing the default output for one |
- // of the stop command results. There are currently the following stop |
- // responses: |
- // ProcessTerminated |
- // ProcessBreakpoint |
- // UncaughtException |
- // ProcessCompileError |
- // ConnectionError |
- Future<String> processStopResponseToString( |
- VmCommand response, |
- SessionState state) async { |
- if (response is UncaughtException) { |
- StringBuffer sb = new StringBuffer(); |
- // Print the exception first, followed by a stack trace. |
- RemoteObject exception = await uncaughtException(); |
- sb.writeln(exceptionToString(exception)); |
- BackTrace trace = await backTrace(); |
- assert(trace != null); |
- sb.write(trace.format()); |
- String result = '$sb'; |
- if (!result.endsWith('\n')) result = '$result\n'; |
- return result; |
- |
- } else if (response is ProcessBreakpoint) { |
- // Print the current line of source code. |
- BackTrace trace = await backTrace(); |
- assert(trace != null); |
- BackTraceFrame topFrame = trace.visibleFrame(0); |
- if (topFrame != null) { |
- String result; |
- if (debugState.verbose) { |
- result = topFrame.list(state, contextLines: 0); |
- } else { |
- result = topFrame.shortString(); |
- } |
- if (!result.endsWith('\n')) result = '$result\n'; |
- return result; |
- } |
- } else if (response is ProcessCompileTimeError) { |
- // TODO(wibling): add information to ProcessCompileTimeError about the |
- // specific error and print here. |
- return ''; |
- } else if (response is ProcessTerminated) { |
- return '### process terminated\n'; |
- |
- } else if (response is ConnectionError) { |
- return '### lost connection to the virtual machine\n'; |
- } |
- return ''; |
- } |
-} |