| 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 '';
|
| - }
|
| -}
|
|
|