/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.tools.debug.shell.client;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import jline.console.ConsoleReader;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.tools.debug.shell.server.REPLServer;
import java.io.File;
/**
* A very simple line-oriented, language-agnostic debugging client shell: the first step toward a
* general, extensible debugging framework designed to be adapted for remote debugging.
* <p>
* The architecture of this debugging framework is modeled loosely on
* <a href="https://github.com/clojure/tools.nrepl">nREPL</a>, a network REPL developed by the
* Clojure community with a focus on generality:
* <ul>
* <li>Client and (possibly remote) server communicate via <em>messages</em> carried over some
* <em>transport</em>;</li>
* <li>A message is a <em>map</em> of key/value pairs;</li>
* <li>Keys and values are <em>strings</em>;</li>
* <li>The client sends messages as <em>requests</em> to a server;</li>
* <li>A server dispatches each incoming request to an appropriate <em>handler</em> that takes
* appropriate action and responds to the client with one or more messages; and</li>
* <li>Many implementations of the <em>transport</em> are possible.</li>
* </ul>
* <p>
* <strong>Compromises:</strong>
* <p>
* In order to get
* <ol>
* <li>The current startup sequence is based on method calls, not messages;</li>
* <li>Only a very few request types and keys are implemented, omitting for example request and
* session ids;</li>
* <li>Message passing is synchronous and "transported" via method calls;</li>
* <li>Asynchrony is emulated by having each call to the server pass only a message, and by having
* the server return only a list of messages.</li>
* </ol>
*
* @see REPLServer
* @see com.oracle.truffle.tools.debug.shell.com.oracle.truffle.tools.debug.shell.REPLMessage
*/
@SuppressWarnings("deprecation")
@Deprecated
public class SimpleREPLClient implements com.oracle.truffle.tools.debug.shell.REPLClient {
private static final String REPLY_PREFIX = "==> ";
private static final String FAIL_PREFIX = "**> ";
private static final String WARNING_PREFIX = "!!> ";
private static final String TRACE_PREFIX = ">>> ";
private static final String[] NULL_ARGS = new String[0];
static final String INFO_LINE_FORMAT = " %s\n";
static final String CODE_LINE_FORMAT = " %3d %s\n";
static final String CODE_LINE_BREAK_FORMAT = "--> %3d %s\n";
private static final String STACK_FRAME_FORMAT = " %3d: at %s in %s %s\n";
private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s %s\n";
public static void main(String[] args) {
final SimpleREPLClient client = new SimpleREPLClient();
final REPLServer replServer = new REPLServer(client);
replServer.start();
client.start(replServer);
}
// Top level commands
private final Map<String, REPLCommand> commandMap = new HashMap<>();
private final Collection<String> commandNames = new TreeSet<>();
// Local options
private final Map<String, LocalOption> localOptions = new HashMap<>();
private final Collection<String> optionNames = new TreeSet<>();
// Current local context
ClientContextImpl clientContext;
private final ConsoleReader reader;
private final PrintStream writer;
private REPLServer replServer;
private final LocalOption astDepthOption = new IntegerOption(9, "astdepth", "default depth for AST display");
private final LocalOption autoWhereOption = new BooleanOption(true, "autowhere", "run the \"where\" command after each navigation");
private final LocalOption autoNodeOption = new BooleanOption(false, "autonode", "run the \"truffle node\" command after each navigation");
private final LocalOption autoSubtreeOption = new BooleanOption(false, "autosubtree", "run the \"truffle subtree\" command after each navigation");
private final LocalOption autoASTOption = new BooleanOption(false, "autoast", "run the \"truffle ast\" command after each navigation");
private final LocalOption listSizeOption = new IntegerOption(25, "listsize", "default number of lines to list");
private final LocalOption traceMessagesOption = new BooleanOption(false, "tracemessages", "trace REPL messages between client and server");
private final LocalOption verboseBreakpointInfoOption = new BooleanOption(true, "verbosebreakpointinfo", "\"info breakpoint\" displays more info");
private void addOption(LocalOption localOption) {
final String optionName = localOption.getName();
localOptions.put(optionName, localOption);
optionNames.add(optionName);
}
/**
* Non-null when the user has named a file other than where halted, providing context for
* commands such as "break"; if no explicit selection, then defaults to where halted. This is
* session state, so it persists across halting contexts.
*/
private Source selectedSource = null;
private boolean quitting; // User has requested to "Quit"
public SimpleREPLClient() {
this.writer = System.out;
try {
this.reader = new ConsoleReader();
} catch (IOException e) {
throw new RuntimeException("Unable to create console " + e);
}
addCommand(backtraceCommand);
addCommand(REPLRemoteCommand.BREAK_AT_LINE_CMD);
addCommand(REPLRemoteCommand.BREAK_AT_LINE_ONCE_CMD);
addCommand(REPLRemoteCommand.CALL_CMD);
addCommand(REPLRemoteCommand.CALL_STEP_INTO_CMD);
addCommand(REPLRemoteCommand.CLEAR_BREAK_CMD);
addCommand(REPLRemoteCommand.CONDITION_BREAK_CMD);
addCommand(REPLRemoteCommand.CONTINUE_CMD);
addCommand(REPLRemoteCommand.DELETE_CMD);
addCommand(REPLRemoteCommand.DISABLE_CMD);
addCommand(REPLRemoteCommand.DOWN_CMD);
addCommand(REPLRemoteCommand.ENABLE_CMD);
addCommand(REPLRemoteCommand.EVAL_CMD);
addCommand(REPLRemoteCommand.EVAL_STEP_INTO_CMD);
addCommand(fileCommand);
addCommand(REPLRemoteCommand.FRAME_CMD);
addCommand(helpCommand);
addCommand(infoCommand);
addCommand(REPLRemoteCommand.KILL_CMD);
addCommand(listCommand);
addCommand(REPLRemoteCommand.LOAD_CMD);
addCommand(REPLRemoteCommand.LOAD_STEP_INTO_CMD);
addCommand(quitCommand);
addCommand(setCommand);
addCommand(REPLRemoteCommand.SET_LANG_CMD);
addCommand(REPLRemoteCommand.STEP_INTO_CMD);
addCommand(REPLRemoteCommand.STEP_OUT_CMD);
addCommand(REPLRemoteCommand.STEP_OVER_CMD);
addCommand(truffleCommand);
addCommand(REPLRemoteCommand.UP_CMD);
addCommand(whereCommand);
infoCommand.addCommand(infoBreakCommand);
infoCommand.addCommand(infoLanguageCommand);
infoCommand.addCommand(infoSetCommand);
truffleCommand.addCommand(truffleASTCommand);
truffleCommand.addCommand(truffleNodeCommand);
truffleCommand.addCommand(truffleSubtreeCommand);
addOption(astDepthOption);
addOption(autoASTOption);
addOption(autoNodeOption);
addOption(autoSubtreeOption);
addOption(autoWhereOption);
addOption(listSizeOption);
addOption(traceMessagesOption);
addOption(verboseBreakpointInfoOption);
}
public void start(REPLServer server) {
this.replServer = server;
clientContext = new ClientContextImpl(null, null);
showWelcome();
try {
final com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = replServer.receive(infoLanguageCommand.createRequest(clientContext, NULL_ARGS));
if (replies.length == 0) {
clientContext.displayFailReply("No languages could be loaded");
} else if (replies.length == 1) {
final String[] args = new String[]{"", replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.LANG_NAME)};
final com.oracle.truffle.tools.debug.shell.REPLMessage[] results = replServer.receive(REPLRemoteCommand.SET_LANG_CMD.createRequest(clientContext, args));
if (results[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
final String message = results[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG);
clientContext.displayFailReply(message != null ? message : results[0].toString());
} else {
clientContext.updatePrompt();
clientContext.startContextSession();
}
} else {
clientContext.displayInfo("Languages supported (type \"lang <name>\" to set default)");
displayLanguages(replies);
clientContext.updatePrompt();
clientContext.startContextSession();
}
} finally {
clientContext.displayReply("Goodbye");
}
}
private void showWelcome() {
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage(
com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.INFO);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.TOPIC, com.oracle.truffle.tools.debug.shell.REPLMessage.WELCOME_MESSAGE);
final com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = clientContext.sendToServer(request);
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayReply("Welcome");
} else {
clientContext.displayReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.INFO_VALUE));
}
}
public void addCommand(REPLCommand replCommand) {
final String commandName = replCommand.getCommand();
final String abbreviation = replCommand.getAbbreviation();
commandNames.add(commandName);
commandMap.put(commandName, replCommand);
if (abbreviation != null) {
commandMap.put(abbreviation, replCommand);
}
}
private class ClientContextImpl implements REPLClientContext {
private final ClientContextImpl predecessor;
private final int level;
// Information about where the execution is halted
/** The source where execution, if any, is halted; null if none. */
private Source haltedSource;
/** The line number where execution, if any, is halted; 0 if none. */
private int haltedLineNumber = 0;
/** The name of a source that we can't locate. */
private String unknownSourceName = "<unavailable>";
/** The stack where execution, if any, is halted; null if none. Evaluated lazily. */
private List<REPLFrame> frames = null;
/** The frame number currently selected by user. */
private int selectedFrameNumber = 0;
private String currentPrompt;
// A execution context stacked on top of this one was explicitly
// killed, and this context will receive the resulting exception.
private boolean killPending;
/**
* Create a new context on the occasion of an execution halting.
*/
ClientContextImpl(ClientContextImpl predecessor, com.oracle.truffle.tools.debug.shell.REPLMessage message) {
this.predecessor = predecessor;
this.level = predecessor == null ? 0 : predecessor.level + 1;
if (message != null) {
final String sourceName = message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_NAME);
try {
this.haltedSource = Source.newBuilder(new File(sourceName)).build();
} catch (IOException ex) {
final String code = message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_TEXT);
if (code != null) {
this.haltedSource = Source.newBuilder(code).name(sourceName).mimeType("content/unknown").build();
}
}
if (this.haltedSource != null) {
selectedSource = haltedSource;
try {
haltedLineNumber = Integer.parseInt(message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.LINE_NUMBER));
} catch (NumberFormatException e) {
haltedLineNumber = 0;
}
} else {
this.haltedSource = null;
this.haltedLineNumber = 0;
this.unknownSourceName = sourceName;
}
}
updatePrompt();
}
private void selectSource(String fileName) {
try {
selectedSource = Source.newBuilder(new File(fileName)).build();
} catch (IOException e1) {
selectedSource = null;
}
updatePrompt();
}
@Override
public void updatePrompt() {
String languageName = "???";
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.INFO);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.TOPIC, com.oracle.truffle.tools.debug.shell.REPLMessage.INFO_CURRENT_LANGUAGE);
final com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = replServer.receive(request);
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.SUCCEEDED)) {
languageName = replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.LANG_NAME);
}
final String showLang = languageName == null ? "() " : "( " + languageName + " )";
if (level == 0) {
// 0-level context; no executions halted.
if (selectedSource == null) {
currentPrompt = showLang + " ";
} else {
currentPrompt = "(" + selectedSource.getName() + ") " + showLang + " ";
}
} else if (selectedSource != null && selectedSource != haltedSource) {
// User is focusing somewhere else than the current locn; show no line number.
final StringBuilder sb = new StringBuilder();
sb.append("(<" + Integer.toString(level) + "> ");
sb.append(selectedSource.getName());
sb.append(")");
sb.append(showLang);
sb.append(" ");
currentPrompt = sb.toString();
} else {
// Prompt reveals where currently halted.
final StringBuilder sb = new StringBuilder();
sb.append("(<" + Integer.toString(level) + "> ");
sb.append(haltedSource == null ? "??" : haltedSource.getName());
if (haltedLineNumber > 0) {
sb.append(":" + Integer.toString(haltedLineNumber));
}
sb.append(")");
sb.append(showLang);
sb.append(" ");
currentPrompt = sb.toString();
}
}
public Source source() {
return haltedSource;
}
public int lineNumber() {
return haltedLineNumber;
}
public List<REPLFrame> frames() {
if (frames == null) {
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage(com.oracle.truffle.tools.debug.shell.REPLMessage.OP,
com.oracle.truffle.tools.debug.shell.REPLMessage.BACKTRACE);
final com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = sendToServer(request);
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
return null;
}
frames = new ArrayList<>();
for (com.oracle.truffle.tools.debug.shell.REPLMessage reply : replies) {
final int index = reply.getIntValue(com.oracle.truffle.tools.debug.shell.REPLMessage.FRAME_NUMBER);
final String locationFilePath = reply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.FILE_PATH);
final Integer locationLineNumber = reply.getIntValue(com.oracle.truffle.tools.debug.shell.REPLMessage.LINE_NUMBER);
final String locationDescription = reply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_LOCATION);
final String name = reply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.METHOD_NAME);
final String sourceLineText = reply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_LINE_TEXT);
frames.add(new REPLFrameImpl(index, locationFilePath, locationLineNumber, locationDescription, name, sourceLineText));
}
frames = Collections.unmodifiableList(frames);
}
return frames;
}
public int level() {
return this.level;
}
public Source getSelectedSource() {
return selectedSource == null ? haltedSource : selectedSource;
}
public int getSelectedFrameNumber() {
return selectedFrameNumber;
}
public String stringQuery(String op) {
assert op != null;
com.oracle.truffle.tools.debug.shell.REPLMessage request = null;
switch (op) {
case com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_AST:
request = truffleASTCommand.createRequest(clientContext, NULL_ARGS);
break;
case com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_SUBTREE:
request = truffleSubtreeCommand.createRequest(clientContext, NULL_ARGS);
break;
default:
request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, op);
}
if (request == null) {
return null;
}
final com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = sendToServer(request);
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
return null;
}
return replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG);
}
public void selectFrameNumber(int frameNumber) {
this.selectedFrameNumber = frameNumber;
}
void displayWhere() {
if (level == 0) {
displayFailReply("no active execution");
return;
}
Source whereSource = null;
int whereLineNumber = 0;
if (selectedFrameNumber == 0) {
whereSource = haltedSource;
whereLineNumber = haltedLineNumber;
} else {
final REPLFrame frame = frames().get(selectedFrameNumber);
final String locationFileName = frame.locationFilePath();
if (locationFileName != null) {
try {
whereSource = Source.newBuilder(new File(locationFileName)).build();
} catch (IOException e) {
}
}
whereLineNumber = frame.locationLineNumber();
}
if (whereSource == null) {
displayFailReply("Unavalable source=\"" + this.unknownSourceName + "\"");
return;
}
final int listSize = listSizeOption.getInt();
final int fileLineCount = whereSource.getLineCount();
final String code = whereSource.getCode();
writer.println("Frame " + selectedFrameNumber + " in " + whereSource.getName());
final int halfListSize = listSize / 2;
final int startLineNumber = Math.max(1, whereLineNumber - halfListSize);
final int lastLineNumber = Math.min(startLineNumber + listSize - 1, fileLineCount);
for (int line = startLineNumber; line <= lastLineNumber; line++) {
final int offset = whereSource.getLineStartOffset(line);
final String lineText = code.substring(offset, offset + whereSource.getLineLength(line));
if (line == whereLineNumber) {
writer.format(CODE_LINE_BREAK_FORMAT, line, lineText);
} else {
writer.format(CODE_LINE_FORMAT, line, lineText);
}
}
}
public void displayStack() {
final List<REPLFrame> frameList = frames();
if (frameList == null) {
writer.println("<empty stack>");
} else {
for (REPLFrame frame : frameList) {
String sourceLineText = frame.sourceLineText();
if (sourceLineText == null) {
sourceLineText = "";
} else {
sourceLineText = "line=\"" + sourceLineText + "\"";
}
if (frame.index() == selectedFrameNumber) {
writer.format(STACK_FRAME_SELECTED_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText);
} else {
writer.format(STACK_FRAME_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText);
}
}
}
}
public void displayInfo(String message) {
writer.format(INFO_LINE_FORMAT, message);
}
public void displayReply(String message) {
writer.println(REPLY_PREFIX + message);
}
public void displayFailReply(String message) {
// Suppress kill-induced failure, since kill is announced
if (!message.equals("KillException")) {
writer.println(FAIL_PREFIX + message);
}
}
public void notifyKilled() {
// A kill will not be received until after
// This context is released, so the exception
// must be handles specially by the
// context that will catch it.
predecessor.killPending = true;
writer.println(clientContext.currentPrompt + " killed");
}
public void displayWarnings(String warnings) {
for (String warning : warnings.split("\\n")) {
writer.println(WARNING_PREFIX + warning);
}
}
public void traceMessage(String message) {
writer.println(TRACE_PREFIX + message);
}
private void startContextSession() {
while (true) {
try {
REPLCommand command = null;
String[] args = NULL_ARGS;
if (quitting) {
if (level == 0) {
return;
}
command = REPLRemoteCommand.KILL_CMD;
} else {
String line = reader.readLine(currentPrompt).trim();
if (line.startsWith("eval ")) {
args = new String[]{"eval", line.substring(5)};
} else {
args = line.split("[ \t]+");
}
if (args.length == 0) {
break;
}
final String cmd = args[0];
if (cmd.isEmpty()) {
continue;
}
command = commandMap.get(cmd);
while (command instanceof REPLIndirectCommand) {
if (traceMessagesOption.getBool()) {
traceMessage("Executing indirect: " + command.getCommand());
}
command = ((REPLIndirectCommand) command).getCommand(args);
}
if (command == quitCommand) {
if (level == 0) {
return;
}
quitting = true;
command = REPLRemoteCommand.KILL_CMD;
}
if (command == null) {
clientContext.displayFailReply("Unrecognized command \"" + cmd + "\"");
continue;
}
}
if (command instanceof REPLLocalCommand) {
if (traceMessagesOption.getBool()) {
traceMessage("Executing local: " + command.getCommand());
}
((REPLLocalCommand) command).execute(args);
} else if (command instanceof REPLRemoteCommand) {
final REPLRemoteCommand remoteCommand = (REPLRemoteCommand) command;
final com.oracle.truffle.tools.debug.shell.REPLMessage request = remoteCommand.createRequest(clientContext, args);
if (request == null) {
continue;
}
com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = sendToServer(request);
remoteCommand.processReply(clientContext, replies);
final String path = replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.FILE_PATH);
if (path != null && !path.isEmpty()) {
selectSource(path);
}
} else {
assert false; // Should not happen.
}
} catch (ThreadDeath ex) {
if (ex.getClass() != ThreadDeath.class && killPending) {
// If the previous context was killed by REPL command, then
// assume this exception is the result and the REPL should
// continue debugging in this context.
killPending = false;
} else {
// A legitimate use of the exception
throw ex;
}
} catch (REPLContinueException ex) {
break;
} catch (IOException e) {
e.printStackTrace();
}
}
}
private com.oracle.truffle.tools.debug.shell.REPLMessage[] sendToServer(com.oracle.truffle.tools.debug.shell.REPLMessage request) {
if (traceMessagesOption.getBool()) {
clientContext.traceMessage("Sever request:");
request.print(writer, " ");
}
com.oracle.truffle.tools.debug.shell.REPLMessage[] replies = replServer.receive(request);
assert replies != null && replies.length > 0;
if (traceMessagesOption.getBool()) {
if (replies.length > 1) {
clientContext.traceMessage("Received " + replies.length + " server replies");
int replyCount = 0;
for (com.oracle.truffle.tools.debug.shell.REPLMessage reply : replies) {
clientContext.traceMessage("Server Reply " + replyCount++ + ":");
reply.print(writer, " ");
}
} else {
clientContext.traceMessage("Received reply:");
replies[0].print(writer, " ");
}
}
return replies;
}
private final class REPLFrameImpl implements REPLFrame {
private final int index;
private final String locationFilePath;
private final Integer locationLineNumber;
private final String locationDescription;
private final String name;
private final String sourceLineText;
REPLFrameImpl(int index, String locationFilePath, Integer locationLineNumber, String locationDescription, String name, String sourceLineText) {
this.index = index;
this.locationFilePath = locationFilePath;
this.locationLineNumber = locationLineNumber;
this.locationDescription = locationDescription;
this.name = name;
this.sourceLineText = sourceLineText;
}
public int index() {
return index;
}
public String locationFilePath() {
return locationFilePath;
}
public Integer locationLineNumber() {
return locationLineNumber;
}
public String locationDescription() {
return locationDescription;
}
public String name() {
return name;
}
public String sourceLineText() {
return sourceLineText;
}
}
}
// Cheating with synchrony: asynchronous replies should arrive here, but don't.
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage receive(com.oracle.truffle.tools.debug.shell.REPLMessage request) {
final String result = request.get("result");
clientContext.displayReply(result != null ? result : request.toString());
return null;
}
/**
* Cheating with synchrony: take a direct call from the server that execution has halted and
* we've entered a nested debugging context.
*/
public void halted(com.oracle.truffle.tools.debug.shell.REPLMessage message) {
// Push a new context for where we've stopped.
clientContext = new ClientContextImpl(clientContext, message);
final String warnings = message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.WARNINGS);
if (warnings != null) {
clientContext.displayWarnings(warnings);
}
if (autoWhereOption.getBool()) {
clientContext.displayWhere();
}
if (autoNodeOption.getBool()) {
final String result = clientContext.stringQuery(com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_NODE);
if (result != null) {
displayTruffleNode(result);
}
}
if (autoASTOption.getBool()) {
final String result = clientContext.stringQuery(com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_AST);
if (result != null) {
displayTruffleAST(result);
}
}
if (autoSubtreeOption.getBool()) {
final String result = clientContext.stringQuery(com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_SUBTREE);
if (result != null) {
displayTruffleSubtree(result);
}
}
try {
clientContext.startContextSession();
} finally {
// To continue execution, pop the context and return
this.clientContext = clientContext.predecessor;
}
}
/**
* A command that can be executed without (direct) communication with the server; it may rely on
* some other method that goes to the server for information.
*/
private abstract class REPLLocalCommand extends REPLCommand {
REPLLocalCommand(String command, String abbreviation, String description) {
super(command, abbreviation, description);
}
abstract void execute(String[] args);
}
/**
* A command that redirects to other commands, based on arguments.
*/
private abstract class REPLIndirectCommand extends REPLCommand {
REPLIndirectCommand(String command, String abbreviation, String description) {
super(command, abbreviation, description);
}
abstract void addCommand(REPLCommand command);
abstract REPLCommand getCommand(String[] args);
}
private final REPLCommand backtraceCommand = new REPLLocalCommand("backtrace", "bt", "Display current stack") {
@Override
void execute(String[] args) {
if (clientContext.level == 0) {
clientContext.displayFailReply("no active execution");
} else {
clientContext.displayStack();
}
}
};
private final REPLCommand fileCommand = new REPLRemoteCommand("file", null, "Set/display current file for viewing") {
final String[] help = {"file: display current file path", "file <filename>: Set file to be current file for viewing"};
@Override
public String[] getHelp() {
return help;
}
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
if (args.length == 1) {
final Source source = clientContext.getSelectedSource();
if (source == null) {
clientContext.displayFailReply("no file currently selected");
} else {
clientContext.displayReply(source.getPath());
}
return null;
}
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.FILE);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_NAME, args[1]);
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
com.oracle.truffle.tools.debug.shell.REPLMessage firstReply = replies[0];
if (firstReply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
final String result = firstReply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG);
clientContext.displayFailReply(result != null ? result : firstReply.toString());
return;
}
final String fileName = firstReply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.SOURCE_NAME);
final String path = firstReply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.FILE_PATH);
clientContext.selectSource(path == null ? fileName : path);
clientContext.displayReply(clientContext.getSelectedSource().getPath());
for (int i = 1; i < replies.length; i++) {
com.oracle.truffle.tools.debug.shell.REPLMessage reply = replies[i];
final String result = reply.get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG);
clientContext.displayInfo(result != null ? result : reply.toString());
}
}
};
private final REPLCommand helpCommand = new REPLLocalCommand("help", null, "Describe commands") {
final String[] help = {"help: list available commands", "help <command>: additional information about <command>"};
@Override
public String[] getHelp() {
return help;
}
@Override
public void execute(String[] args) {
if (args.length == 1) {
clientContext.displayReply("Available commands:");
for (String commandName : commandNames) {
final REPLCommand command = commandMap.get(commandName);
if (command == null) {
clientContext.displayInfo(commandName + ": Error, no implementation for command");
} else {
final String abbrev = command.getAbbreviation();
if (abbrev == null) {
clientContext.displayInfo(commandName + ": " + command.getDescription());
} else {
clientContext.displayInfo(commandName + "(" + abbrev + "): " + command.getDescription());
}
}
}
} else {
final String cmdName = args[1];
final REPLCommand cmd = commandMap.get(cmdName);
if (cmd == null) {
clientContext.displayReply("command \"" + cmdName + "\" not recognized");
} else {
final String[] helpLines = cmd.getHelp();
if (helpLines == null) {
clientContext.displayReply("\"" + cmdName + "\":");
} else if (helpLines.length == 1) {
clientContext.displayInfo(helpLines[0]);
} else {
clientContext.displayReply("\"" + cmdName + "\":");
for (String line : helpLines) {
clientContext.displayInfo(line);
}
}
}
}
}
};
private final REPLIndirectCommand infoCommand = new REPLIndirectCommand(com.oracle.truffle.tools.debug.shell.REPLMessage.INFO, null, "Additional information on topics") {
// "Info" commands
private final Map<String, REPLCommand> infoCommandMap = new HashMap<>();
private final Collection<String> infoCommandNames = new TreeSet<>();
@Override
public String[] getHelp() {
final ArrayList<String> lines = new ArrayList<>();
for (String infoCommandName : infoCommandNames) {
final REPLCommand cmd = infoCommandMap.get(infoCommandName);
if (cmd == null) {
lines.add("\"" + com.oracle.truffle.tools.debug.shell.REPLMessage.INFO + " " + infoCommandName + "\" not implemented");
} else {
lines.add("\"" + com.oracle.truffle.tools.debug.shell.REPLMessage.INFO + " " + infoCommandName + "\": " + cmd.getDescription());
}
}
return lines.toArray(new String[0]);
}
@Override
void addCommand(REPLCommand replCommand) {
final String commandName = replCommand.getCommand();
final String abbreviation = replCommand.getAbbreviation();
infoCommandNames.add(commandName);
infoCommandMap.put(commandName, replCommand);
if (abbreviation != null) {
infoCommandMap.put(abbreviation, replCommand);
}
}
@Override
REPLCommand getCommand(String[] args) {
if (args.length == 1) {
clientContext.displayFailReply("info topic not specified; try \"help info\"");
return null;
}
final String topic = args[1];
REPLCommand command = infoCommandMap.get(topic);
if (command == null) {
clientContext.displayFailReply("topic \"" + topic + "\" not recognized");
return null;
}
return command;
}
};
private final REPLCommand infoBreakCommand = new REPLRemoteCommand("breakpoint", "break", "info about breakpoints") {
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_INFO);
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayFailReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
} else {
Arrays.sort(replies, new Comparator<com.oracle.truffle.tools.debug.shell.REPLMessage>() {
public int compare(com.oracle.truffle.tools.debug.shell.REPLMessage o1, com.oracle.truffle.tools.debug.shell.REPLMessage o2) {
try {
final int n1 = Integer.parseInt(o1.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_ID));
final int n2 = Integer.parseInt(o2.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_ID));
return Integer.compare(n1, n2);
} catch (Exception ex) {
}
return 0;
}
});
clientContext.displayReply("Breakpoints set:");
for (com.oracle.truffle.tools.debug.shell.REPLMessage message : replies) {
final StringBuilder sb = new StringBuilder();
sb.append(Integer.parseInt(message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_ID)) + ": ");
sb.append("@" + message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.INFO_VALUE));
sb.append(" (state=" + message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_STATE));
if (verboseBreakpointInfoOption.getBool()) {
sb.append(", hits=" + Integer.parseInt(message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_HIT_COUNT)));
sb.append(", ignore=" + Integer.parseInt(message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_IGNORE_COUNT)));
}
final String condition = message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.BREAKPOINT_CONDITION);
if (condition != null) {
sb.append(", condition=\"" + condition + "\"");
}
sb.append(")");
clientContext.displayInfo(sb.toString());
}
}
}
};
private final REPLRemoteCommand infoLanguageCommand = new REPLRemoteCommand("languages", "lang", "languages supported") {
final String[] help = {"info language: list details about supported languages"};
@Override
public String[] getHelp() {
return help;
}
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.INFO);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.TOPIC, com.oracle.truffle.tools.debug.shell.REPLMessage.INFO_SUPPORTED_LANGUAGES);
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayFailReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
} else {
clientContext.displayReply("Languages supported:");
displayLanguages(replies);
}
}
};
private void displayLanguages(com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
for (com.oracle.truffle.tools.debug.shell.REPLMessage message : replies) {
final StringBuilder sb = new StringBuilder();
final String name = message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.LANG_NAME);
if (!name.equals("")) {
sb.append(name);
sb.append(" ver. ");
sb.append(message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.LANG_VER));
clientContext.displayInfo(sb.toString());
}
}
}
private final REPLCommand infoSetCommand = new REPLLocalCommand("set", null, "info about settings") {
final String[] help = {"info sets: list local options that can be set"};
@Override
public String[] getHelp() {
return help;
}
@Override
public void execute(String[] args) {
clientContext.displayReply("Settable options:");
for (String optionName : optionNames) {
final LocalOption localOption = localOptions.get(optionName);
if (localOption == null) {
clientContext.displayInfo(localOption + ": Error, no implementation for option");
} else {
clientContext.displayInfo(optionName + "=" + localOption.getValue() + ": " + localOption.getDescription());
}
}
}
};
private final REPLCommand listCommand = new REPLLocalCommand("list", null, "Display selected source file") {
final String[] help = {"list: list <listsize> lines of selected file (see option \"listsize\")", "list all: list all lines", "list <n>: list <listsize> lines centered around line <n>"};
private Source lastListedSource = null;
private int nextLineToList = 1;
@Override
public String[] getHelp() {
return help;
}
@Override
public void execute(String[] args) {
final Source source = clientContext.getSelectedSource();
if (source == null) {
clientContext.displayFailReply("No selected file");
reset();
return;
}
final int listSize = listSizeOption.getInt();
if (args.length == 1) {
if (!source.equals(lastListedSource)) {
reset();
} else if (nextLineToList > source.getLineCount()) {
reset();
}
final int lastListedLine = printLines(source, nextLineToList, listSize);
lastListedSource = source;
nextLineToList = lastListedLine > source.getLineCount() ? 1 : lastListedLine + 1;
} else if (args.length == 2) {
reset();
if (args[1].equals("all")) {
printLines(source, 1, source.getLineCount());
} else {
try {
final int line = Integer.parseInt(args[1]);
final int halfListSize = listSize / 2;
final int start = Math.max(1, line - halfListSize);
final int count = Math.min(source.getLineCount() + 1 - start, listSize);
printLines(source, start, count);
} catch (NumberFormatException e) {
clientContext.displayFailReply("\"" + args[1] + "\" not recognized");
}
}
}
}
private int printLines(Source printSource, int start, int listSize) {
clientContext.displayReply(printSource.getName() + ":");
final int lastLineNumber = Math.min(start + listSize - 1, printSource.getLineCount());
for (int line = start; line <= lastLineNumber; line++) {
writer.format(CODE_LINE_FORMAT, line, printSource.getCode(line));
}
return lastLineNumber;
}
/**
* Forget where we were in a sequence of list commands with no arguments
*/
private void reset() {
lastListedSource = clientContext.getSelectedSource();
nextLineToList = 1;
}
};
private final REPLCommand quitCommand = new REPLRemoteCommand("quit", "q", "Quit execution and REPL") {
@Override
protected com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
return null;
}
};
private final REPLCommand setCommand = new REPLLocalCommand("set", null, "set <option>=<value>") {
@Override
public String[] getHelp() {
return new String[]{"Sets an option \"set <option-name>=<value>\"; see also \"info set\""};
}
@Override
public void execute(String[] args) {
com.oracle.truffle.tools.debug.shell.REPLMessage request = null;
if (args.length == 1) {
clientContext.displayFailReply("No option specified, try \"help set\"");
} else if (args.length == 2) {
String[] split = new String[0];
try {
split = args[1].split("=");
} catch (Exception ex) {
}
if (split.length == 0) {
clientContext.displayFailReply("Arguments not understood, try \"help set\"");
} else if (split.length == 1) {
clientContext.displayFailReply("No option value specified, try \"help set\"");
} else if (split.length > 2) {
clientContext.displayFailReply("Arguments not understood, try \"help set\"");
} else {
final String optionName = split[0];
final String newValue = split[1];
final LocalOption localOption = localOptions.get(optionName);
if (localOption != null) {
if (!localOption.setValue(newValue)) {
clientContext.displayFailReply("Invalid option value \"" + newValue + "\"");
}
clientContext.displayInfo(localOption.name + " = " + localOption.getValue());
} else {
request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.SET);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OPTION, optionName);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.VALUE, newValue);
}
}
} else {
clientContext.displayFailReply("Arguments not understood, try \"help set\"");
}
}
};
private final REPLIndirectCommand truffleCommand = new REPLIndirectCommand(com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE, "t", "Access to Truffle internals") {
// "Truffle" commands
private final Map<String, REPLCommand> truffleCommandMap = new HashMap<>();
private final Collection<String> truffleCommandNames = new TreeSet<>();
@Override
public String[] getHelp() {
final ArrayList<String> lines = new ArrayList<>();
for (String truffleCommandName : truffleCommandNames) {
final REPLCommand cmd = truffleCommandMap.get(truffleCommandName);
if (cmd == null) {
lines.add("\"" + com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE + " " + truffleCommandName + "\" not implemented");
} else {
for (String line : cmd.getHelp()) {
lines.add(line);
}
}
}
return lines.toArray(new String[0]);
}
@Override
void addCommand(REPLCommand replCommand) {
final String commandName = replCommand.getCommand();
final String abbreviation = replCommand.getAbbreviation();
truffleCommandNames.add(commandName);
truffleCommandMap.put(commandName, replCommand);
if (abbreviation != null) {
truffleCommandMap.put(abbreviation, replCommand);
}
}
@Override
REPLCommand getCommand(String[] args) {
if (args.length == 1) {
clientContext.displayFailReply("truffle request not specified; try \"help truffle\"");
return null;
}
final String topic = args[1];
REPLCommand command = truffleCommandMap.get(topic);
if (command == null) {
clientContext.displayFailReply("truffle request \"" + topic + "\" not recognized");
return null;
}
return command;
}
};
private final REPLRemoteCommand truffleASTCommand = new REPLRemoteCommand("ast", null, "print the AST that contains the current node") {
final String[] help = {"truffle ast: print the AST subtree that contains current node (see \"set treedepth\")",
"truffle ast <n>: print the AST subtree that contains current node to a maximum depth of <n>"};
@Override
public String[] getHelp() {
return help;
}
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
if (clientContext.level() == 0) {
context.displayFailReply("no active execution");
return null;
}
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.TOPIC, com.oracle.truffle.tools.debug.shell.REPLMessage.AST);
int astDepth = astDepthOption.getInt();
if (args.length > 2) {
final String depthText = args[2];
try {
astDepth = Integer.parseInt(depthText);
} catch (NumberFormatException e) {
}
}
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.AST_DEPTH, Integer.toString(astDepth));
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayFailReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
} else {
clientContext.displayReply("AST containing the Current Node:");
for (com.oracle.truffle.tools.debug.shell.REPLMessage message : replies) {
for (String line : message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG).split("\n")) {
clientContext.displayInfo(line);
}
}
}
}
};
private void displayTruffleAST(String text) {
clientContext.displayReply("AST containing Current Node:");
for (String line : text.split("\n")) {
clientContext.displayInfo(line);
}
}
private final REPLRemoteCommand truffleNodeCommand = new REPLRemoteCommand("node", null, "describe current AST node") {
final String[] help = {"truffle node: describe the AST node at the current execution context"};
@Override
public String[] getHelp() {
return help;
}
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
if (clientContext.level() == 0) {
context.displayFailReply("no active execution");
return null;
}
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE_NODE);
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayFailReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
} else {
displayTruffleNode(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
}
}
};
private void displayTruffleNode(String nodeString) {
clientContext.displayReply("Current Node: " + nodeString);
}
private final REPLRemoteCommand truffleSubtreeCommand = new REPLRemoteCommand("subtree", "sub", "print the AST subtree rooted at the current node") {
final String[] help = {"truffle sub: print the AST subtree at the current node (see \"set treedepth\")", "truffle sub <n>: print the AST subtree at the current node to maximum depth <n>",
"truffle subtree: print the AST subtree at the current node (see \"set treedepth\")", "truffle sub <n>: print the AST subtree at the current node to maximum depth <n>"};
@Override
public String[] getHelp() {
return help;
}
@Override
public com.oracle.truffle.tools.debug.shell.REPLMessage createRequest(REPLClientContext context, String[] args) {
if (clientContext.level() == 0) {
context.displayFailReply("no active execution");
return null;
}
final com.oracle.truffle.tools.debug.shell.REPLMessage request = new com.oracle.truffle.tools.debug.shell.REPLMessage();
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.OP, com.oracle.truffle.tools.debug.shell.REPLMessage.TRUFFLE);
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.TOPIC, com.oracle.truffle.tools.debug.shell.REPLMessage.SUBTREE);
int astDepth = astDepthOption.getInt();
if (args.length > 2) {
final String depthText = args[2];
try {
astDepth = Integer.parseInt(depthText);
} catch (NumberFormatException e) {
}
}
request.put(com.oracle.truffle.tools.debug.shell.REPLMessage.AST_DEPTH, Integer.toString(astDepth));
return request;
}
@Override
void processReply(REPLClientContext context, com.oracle.truffle.tools.debug.shell.REPLMessage[] replies) {
if (replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.STATUS).equals(com.oracle.truffle.tools.debug.shell.REPLMessage.FAILED)) {
clientContext.displayFailReply(replies[0].get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG));
} else {
clientContext.displayReply("AST subtree at Current Node:");
for (com.oracle.truffle.tools.debug.shell.REPLMessage message : replies) {
for (String line : message.get(com.oracle.truffle.tools.debug.shell.REPLMessage.DISPLAY_MSG).split("\n")) {
clientContext.displayInfo(line);
}
}
}
}
};
private void displayTruffleSubtree(String text) {
clientContext.displayReply("AST subtree at Current Node:");
for (String line : text.split("\n")) {
clientContext.displayInfo(line);
}
}
private final REPLCommand whereCommand = new REPLLocalCommand("where", null, "Show code around current break location") {
@Override
public void execute(String[] args) {
clientContext.displayWhere();
}
};
private abstract static class LocalOption {
private final String name;
private final String description;
protected LocalOption(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public abstract boolean setValue(String newValue);
public boolean getBool() {
assert false;
return false;
}
public int getInt() {
assert false;
return 0;
}
public abstract String getValue();
}
private static final class BooleanOption extends LocalOption {
private Boolean value;
BooleanOption(boolean value, String name, String description) {
super(name, description);
this.value = value;
}
@Override
public boolean setValue(String newValue) {
final Boolean valueOf = Boolean.valueOf(newValue);
if (valueOf == null) {
return false;
}
value = valueOf;
return true;
}
@Override
public boolean getBool() {
return value;
}
@Override
public String getValue() {
return value.toString();
}
}
private static final class IntegerOption extends LocalOption {
private Integer value;
IntegerOption(int value, String name, String description) {
super(name, description);
this.value = value;
}
@Override
public boolean setValue(String newValue) {
Integer valueOf;
try {
valueOf = Integer.valueOf(newValue);
} catch (NumberFormatException e) {
return false;
}
value = valueOf;
return true;
}
@Override
public int getInt() {
return value;
}
@Override
public String getValue() {
return value.toString();
}
}
}