Mercurial > hg > truffle
changeset 22444:c3e397ce5941
Truffle/Debugging/REPL: major reorganization post-PolyglotEngine
- Discarded language-specific specializations, with one temporary, minor exception
- "loadr" and "loads" commands replaced by "load", which always "steps into"
- added "call" command: it invokes an exported symbol, which is needed to run "main" in SL demo
- fixed failures to propagate KillException and QuitException properly
- fixed usability bug in the client: "load" now sets the "selected source"
- add package-info.java
line wrap: on
line diff
--- a/mx.truffle/mx_truffle.py Tue Oct 20 11:30:34 2015 -0400 +++ b/mx.truffle/mx_truffle.py Tue Oct 27 17:44:27 2015 -0700 @@ -47,7 +47,7 @@ def sldebug(args): """run a simple command line debugger for the Simple Language""" vmArgs, slArgs = mx.extract_VM_args(args, useDoubleDash=True) - mx.run_java(vmArgs + ['-cp', mx.classpath("com.oracle.truffle.sl.tools"), "com.oracle.truffle.sl.tools.debug.SLREPLServer"] + slArgs) + mx.run_java(vmArgs + ['-cp', mx.classpath("com.oracle.truffle.sl.tools"), "com.oracle.truffle.sl.tools.debug.SLREPL"] + slArgs) def _truffle_gate_runner(args, tasks): with Task('Truffle UnitTests', tasks) as t:
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Tue Oct 27 17:44:27 2015 -0700 @@ -61,7 +61,9 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.QuitException; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; @@ -814,6 +816,8 @@ target = null; } } + } catch (KillException | QuitException ex) { + throw ex; } catch (IOException ex) { res[1] = ex; } catch (RuntimeException ex) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPL.java Tue Oct 27 17:44:27 2015 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.sl.tools.debug; + +import com.oracle.truffle.sl.SLLanguage; +import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + +// TODO (mlvdv) delete when the REPL is fully multi-language +/** + * Start a single-language REPL for the Simple Language implementation. + */ +public final class SLREPL { + + @SuppressWarnings("unused") private static final Class<SLLanguage> DYNAMIC_DEPENDENCY = com.oracle.truffle.sl.SLLanguage.class; + + public static void main(String[] args) { + + // Cheating for the prototype: start from SL, rather than from the client. + final REPLServer server = new REPLServer("application/x-sl", new SLDefaultVisualizer()); + server.start(); + } + + private SLREPL() { + } +}
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java Tue Oct 20 11:30:34 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.oracle.truffle.sl.tools.debug; - -import com.oracle.truffle.api.instrument.KillException; -import com.oracle.truffle.api.instrument.QuitException; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; -import com.oracle.truffle.tools.debug.shell.server.REPLHandler; -import com.oracle.truffle.tools.debug.shell.server.REPLServerContext; -import java.io.File; -import java.util.ArrayList; - -/** - * Instantiation of the "server handler" part of the "REPL*" debugger for the simple language. - * <p> - * These handlers implement debugging commands that require language-specific support. - * - * @see SimpleREPLClient - */ -public abstract class SLREPLHandler extends REPLHandler { - - protected SLREPLHandler(String op) { - super(op); - } - - public static final SLREPLHandler INFO_HANDLER = new SLREPLHandler(REPLMessage.INFO) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - final String topic = request.get(REPLMessage.TOPIC); - - if (topic == null || topic.isEmpty()) { - final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - return finishReplyFailed(message, "No info topic specified"); - } - - switch (topic) { - - case REPLMessage.LANGUAGE: - return createLanguageInfoReply(); - - default: - final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - return finishReplyFailed(message, "No info about topic \"" + topic + "\""); - } - } - }; - - private static REPLMessage[] createLanguageInfoReply() { - final ArrayList<REPLMessage> langMessages = new ArrayList<>(); - - final REPLMessage msg1 = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - msg1.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE); - msg1.put(REPLMessage.INFO_KEY, "Language"); - msg1.put(REPLMessage.INFO_VALUE, "Simple"); - msg1.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - langMessages.add(msg1); - - return langMessages.toArray(new REPLMessage[0]); - } - - public static final SLREPLHandler LOAD_RUN_SOURCE_HANDLER = new SLREPLHandler(REPLMessage.LOAD_RUN) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - return loadHandler(request, serverContext, false); - } - }; - - public static final SLREPLHandler LOAD_STEP_SOURCE_HANDLER = new SLREPLHandler(REPLMessage.LOAD_STEP) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - return loadHandler(request, serverContext, true); - } - }; - - // TODO (mlvdv) re-implement stepInto when vm support replaced - /** - * Runs a source, optionally stepping into a specified tag. - */ - private static REPLMessage[] loadHandler(REPLMessage request, REPLServerContext serverContext, @SuppressWarnings("unused") boolean stepInto) { - final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_RUN); - final String fileName = request.get(REPLMessage.SOURCE_NAME); - try { - final File file = new File(fileName); - if (!file.canRead()) { - return finishReplyFailed(reply, "can't find file \"" + fileName + "\""); - } - final PolyglotEngine vm = serverContext.engine(); - vm.eval(Source.fromFileName(file.getPath())); - PolyglotEngine.Value main = vm.findGlobalSymbol("main"); - if (main != null) { - main.invoke(null); - } - final String path = file.getCanonicalPath(); - reply.put(REPLMessage.FILE_PATH, path); - return finishReplySucceeded(reply, fileName + " exited"); - } catch (QuitException ex) { - throw ex; - } catch (KillException ex) { - return finishReplySucceeded(reply, fileName + " killed"); - } catch (Exception ex) { - return finishReplyFailed(reply, ex); - } - } -}
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java Tue Oct 20 11:30:34 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.oracle.truffle.sl.tools.debug; - -import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.instrument.QuitException; -import com.oracle.truffle.api.instrument.Visualizer; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.vm.EventConsumer; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.api.vm.PolyglotEngine.Language; -import com.oracle.truffle.sl.SLLanguage; -import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.REPLServer; -import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; -import com.oracle.truffle.tools.debug.shell.server.REPLHandler; -import com.oracle.truffle.tools.debug.shell.server.REPLServerContext; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Instantiation of the "server" side of the "REPL*" debugger for the Simple language. - * <p> - * The SL parser is not equipped to parse program fragments, so any debugging functions that depend - * on this are not supported, for example "eval" and breakpoint conditions. - * - * @see SimpleREPLClient - */ -public final class SLREPLServer extends REPLServer { - - // TODO (mlvdv) remove when there's a better way to express this dependency - @SuppressWarnings("unused") private static final Class<SLLanguage> DYNAMIC_DEPENDENCY = com.oracle.truffle.sl.SLLanguage.class; - - public static void main(String[] args) { - // Cheating for the prototype: start from SL, rather than from the client. - final SLREPLServer server = new SLREPLServer(); - final SimpleREPLClient client = new SimpleREPLClient(getShortName(server.language), server); - - // Cheating for the prototype: allow server access to client for recursive debugging - server.setClient(client); - - try { - client.start(); - } catch (QuitException ex) { - } - } - - private static String getShortName(Language language) { - return language.getName() + "(" + language.getVersion() + ")"; - } - - private final Language language; - private final PolyglotEngine vm; - private Debugger db; - private final String statusPrefix; - private final Map<String, REPLHandler> handlerMap = new HashMap<>(); - private SLServerContext currentServerContext; - private SimpleREPLClient replClient = null; - - private void add(REPLHandler fileHandler) { - handlerMap.put(fileHandler.getOp(), fileHandler); - } - - public SLREPLServer() { - add(REPLHandler.BACKTRACE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER); - add(REPLHandler.BREAK_AT_THROW_HANDLER); - add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER); - add(REPLHandler.BREAKPOINT_INFO_HANDLER); - add(REPLHandler.CLEAR_BREAK_HANDLER); - add(REPLHandler.CONTINUE_HANDLER); - add(REPLHandler.DELETE_HANDLER); - add(REPLHandler.DISABLE_BREAK_HANDLER); - add(REPLHandler.ENABLE_BREAK_HANDLER); - add(REPLHandler.FILE_HANDLER); - add(REPLHandler.FRAME_HANDLER); - add(SLREPLHandler.INFO_HANDLER); - add(REPLHandler.KILL_HANDLER); - add(SLREPLHandler.LOAD_RUN_SOURCE_HANDLER); - add(SLREPLHandler.LOAD_STEP_SOURCE_HANDLER); - add(REPLHandler.QUIT_HANDLER); - add(REPLHandler.STEP_INTO_HANDLER); - add(REPLHandler.STEP_OUT_HANDLER); - add(REPLHandler.STEP_OVER_HANDLER); - add(REPLHandler.TRUFFLE_HANDLER); - add(REPLHandler.TRUFFLE_NODE_HANDLER); - - EventConsumer<SuspendedEvent> onHalted = new EventConsumer<SuspendedEvent>(SuspendedEvent.class) { - @Override - protected void on(SuspendedEvent ev) { - SLREPLServer.this.haltedAt(ev); - } - }; - EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) { - @Override - protected void on(ExecutionEvent event) { - event.prepareStepInto(); - db = event.getDebugger(); - } - }; - - PolyglotEngine newVM = PolyglotEngine.buildNew().onEvent(onHalted).onEvent(onExec).build(); - this.language = newVM.getLanguages().get("application/x-sl"); - assert language != null; - - this.vm = newVM; - this.statusPrefix = getShortName(language) + " REPL:"; - } - - private void setClient(SimpleREPLClient replClient) { - this.replClient = replClient; - } - - @Override - public REPLMessage start() { - - this.currentServerContext = new SLServerContext(null, null); - - // SL doesn't load modules (like other languages), so we just return a success - final REPLMessage reply = new REPLMessage(); - reply.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - reply.put(REPLMessage.DISPLAY_MSG, getShortName(language) + " started"); - return reply; - } - - @Override - public REPLMessage[] receive(REPLMessage request) { - if (currentServerContext == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, "server not started"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return currentServerContext.receive(request); - } - - /** - * Execution context of a halted SL program. - */ - public final class SLServerContext extends REPLServerContext { - - private final SLServerContext predecessor; - - public SLServerContext(SLServerContext predecessor, SuspendedEvent event) { - super(predecessor == null ? 0 : predecessor.getLevel() + 1, event); - this.predecessor = predecessor; - } - - @Override - public REPLMessage[] receive(REPLMessage request) { - final String command = request.get(REPLMessage.OP); - final REPLHandler handler = handlerMap.get(command); - - if (handler == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, command); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return handler.receive(request, currentServerContext); - } - - @Override - public Language getLanguage() { - return language; - } - - @Override - public Visualizer getVisualizer() { - return new SLDefaultVisualizer(); - } - - @Override - public PolyglotEngine engine() { - return vm; - } - - @Override - protected Debugger db() { - return db; - } - - @Override - public void registerBreakpoint(Breakpoint breakpoint) { - SLREPLServer.this.registerBreakpoint(breakpoint); - } - - @Override - public Breakpoint findBreakpoint(int id) { - return SLREPLServer.this.findBreakpoint(id); - } - - @Override - public int getBreakpointID(Breakpoint breakpoint) { - return SLREPLServer.this.getBreakpointID(breakpoint); - } - - } - - void haltedAt(SuspendedEvent event) { - // Create and push a new debug context where execution is halted - currentServerContext = new SLServerContext(currentServerContext, event); - - // Message the client that execution is halted and is in a new debugging context - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, REPLMessage.STOPPED); - final SourceSection src = event.getNode().getSourceSection(); - final Source source = src.getSource(); - message.put(REPLMessage.SOURCE_NAME, source.getName()); - message.put(REPLMessage.FILE_PATH, source.getPath()); - message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); - message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); - List<String> warnings = event.getRecentWarnings(); - if (!warnings.isEmpty()) { - final StringBuilder sb = new StringBuilder(); - for (String warning : warnings) { - sb.append(warning + "\n"); - } - message.put(REPLMessage.WARNINGS, sb.toString()); - } - try { - // Cheat with synchrony: call client directly about entering a nested debugging - // context. - replClient.halted(message); - } finally { - // Returns when "continue" is called in the new debugging context - - // Pop the debug context, and return so that the old context will continue - currentServerContext = currentServerContext.predecessor; - } - } -}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Tue Oct 27 17:44:27 2015 -0700 @@ -30,6 +30,8 @@ import java.util.Set; import java.util.TreeMap; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + /** * A message for communication between a Read-Eval-Print-Loop server associated with a language * implementation and a possibly remote client. @@ -53,6 +55,8 @@ public static final String BREAKPOINT_IGNORE_COUNT = "breakpoint-ignore-count"; public static final String BREAKPOINT_INFO = "breakpoint-info"; public static final String BREAKPOINT_STATE = "breakpoint-state"; + public static final String CALL = "call"; + public static final String CALL_NAME = "call-name"; public static final String CLEAR_BREAK = "clear-breakpoint"; public static final String CODE = "code"; public static final String CONTINUE = "continue"; @@ -76,8 +80,7 @@ public static final String LANGUAGE = "language"; public static final String LINE_NUMBER = "line-number"; public static final String LIST = "list"; - public static final String LOAD_RUN = "load-run-source"; - public static final String LOAD_STEP = "load-step-into-source"; + public static final String LOAD_SOURCE = "load-source"; public static final String METHOD_NAME = "method-name"; public static final String OP = "op"; public static final String OPTION = "option";
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java Tue Oct 20 11:30:34 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015, 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; - -import com.oracle.truffle.api.debug.Breakpoint; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * The server side of a simple message-based protocol for a possibly remote language - * Read-Eval-Print-Loop. - */ -public abstract class REPLServer { - - /** - * Starts up a server; status returned in a message. - */ - public abstract REPLMessage start(); - - /** - * Ask the server to handle a request. Return a non-empty array of messages to simulate remote - * operation where the protocol has possibly multiple messages being returned asynchronously in - * response to each request. - */ - public abstract REPLMessage[] receive(REPLMessage request); - - private int breakpointCounter; - private Map<Breakpoint, Integer> breakpoints = new WeakHashMap<>(); - - protected final synchronized void registerBreakpoint(Breakpoint breakpoint) { - breakpoints.put(breakpoint, breakpointCounter++); - } - - protected final synchronized Breakpoint findBreakpoint(int id) { - for (Map.Entry<Breakpoint, Integer> entrySet : breakpoints.entrySet()) { - if (id == entrySet.getValue()) { - return entrySet.getKey(); - } - } - return null; - } - - protected final synchronized int getBreakpointID(Breakpoint breakpoint) { - final Integer id = breakpoints.get(breakpoint); - return id == null ? -1 : id; - } - -}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Tue Oct 27 17:44:27 2015 -0700 @@ -25,13 +25,14 @@ package com.oracle.truffle.tools.debug.shell.client; import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.tools.debug.shell.REPLServer; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + import java.util.List; /** * Client context for interaction with a program halted by the {@link REPLServer}. */ -public interface REPLClientContext { +interface REPLClientContext { /** * The source code halted in this context.
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Tue Oct 27 17:44:27 2015 -0700 @@ -200,6 +200,31 @@ } }; + public static final REPLRemoteCommand CALL_CMD = new REPLRemoteCommand("call", null, "call a method/function") { + + private final String[] help = {"call <name>: calls a function by name, arguments not yet supported"}; + + @Override + public String[] getHelp() { + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + if (args.length == 1) { + context.displayFailReply("name to call not speciified"); + } else if (args.length > 2) { + context.displayFailReply("call arguments not yet supported"); + } else { + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.CALL); + request.put(REPLMessage.CALL_NAME, args[1]); + return request; + } + return null; + } + }; + public static final REPLRemoteCommand CLEAR_BREAK_CMD = new REPLRemoteCommand("clear", null, "Clear a breakpoint") { private final String[] help = {"clear <n>: clear breakpoint number <n>"}; @@ -510,7 +535,7 @@ } }; - public static final REPLRemoteCommand LOAD_RUN_CMD = new REPLRemoteCommand("load-run", "loadr", "Load and run a source") { + public static final REPLRemoteCommand LOAD_CMD = new REPLRemoteCommand("load", null, "Load source") { @Override public REPLMessage createRequest(REPLClientContext context, String[] args) { @@ -530,33 +555,7 @@ } } final REPLMessage request = new REPLMessage(); - request.put(REPLMessage.OP, REPLMessage.LOAD_RUN); - request.put(REPLMessage.SOURCE_NAME, runSource.getPath()); - return request; - } - }; - - public static final REPLRemoteCommand LOAD_STEP_CMD = new REPLRemoteCommand("load-step", "loads", "Load and step into a source") { - - @Override - public REPLMessage createRequest(REPLClientContext context, String[] args) { - Source runSource = null; - if (args.length == 1) { - runSource = context.getSelectedSource(); - if (runSource == null) { - context.displayFailReply("No file selected"); - return null; - } - } else { - try { - runSource = Source.fromFileName(args[1]); - } catch (IOException e) { - context.displayFailReply("Can't find file: " + args[1]); - return null; - } - } - final REPLMessage request = new REPLMessage(); - request.put(REPLMessage.OP, REPLMessage.LOAD_STEP); + request.put(REPLMessage.OP, REPLMessage.LOAD_SOURCE); request.put(REPLMessage.SOURCE_NAME, runSource.getPath()); return request; }
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Tue Oct 27 17:44:27 2015 -0700 @@ -24,10 +24,12 @@ */ package com.oracle.truffle.tools.debug.shell.client; +import com.oracle.truffle.api.instrument.QuitException; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.tools.debug.shell.REPLClient; import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.REPLServer; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; @@ -39,6 +41,7 @@ import java.util.List; import java.util.Map; import java.util.TreeSet; + import jline.console.ConsoleReader; /** @@ -108,7 +111,7 @@ // Cheating for the prototype; prototype startup now happens from the language server. // So this isn't used. public static void main(String[] args) { - final SimpleREPLClient repl = new SimpleREPLClient(null, null); + final SimpleREPLClient repl = new SimpleREPLClient(null); repl.start(); } @@ -147,9 +150,9 @@ */ private Source selectedSource = null; - public SimpleREPLClient(String languageName, REPLServer replServer) { - this.languageName = languageName; + public SimpleREPLClient(REPLServer replServer) { this.replServer = replServer; + this.languageName = replServer.getLanguageName(); this.writer = System.out; try { this.reader = new ConsoleReader(); @@ -162,6 +165,7 @@ addCommand(REPLRemoteCommand.BREAK_AT_LINE_ONCE_CMD); addCommand(REPLRemoteCommand.BREAK_AT_THROW_CMD); addCommand(REPLRemoteCommand.BREAK_AT_THROW_ONCE_CMD); + addCommand(REPLRemoteCommand.CALL_CMD); addCommand(REPLRemoteCommand.CLEAR_BREAK_CMD); addCommand(REPLRemoteCommand.CONDITION_BREAK_CMD); addCommand(REPLRemoteCommand.CONTINUE_CMD); @@ -176,8 +180,7 @@ addCommand(infoCommand); addCommand(REPLRemoteCommand.KILL_CMD); addCommand(listCommand); - addCommand(REPLRemoteCommand.LOAD_RUN_CMD); - addCommand(REPLRemoteCommand.LOAD_STEP_CMD); + addCommand(REPLRemoteCommand.LOAD_CMD); addCommand(quitCommand); addCommand(setCommand); addCommand(REPLRemoteCommand.STEP_INTO_CMD); @@ -207,18 +210,10 @@ public void start() { - REPLMessage startReply = replServer.start(); - - if (startReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) { - clientContext.displayFailReply(startReply.get(REPLMessage.DISPLAY_MSG)); - throw new RuntimeException("Can't start REPL server"); - } - this.clientContext = new ClientContextImpl(null, null); - try { - clientContext.startSession(); - } finally { + clientContext.startContextSession(); + } catch (QuitException ex) { clientContext.displayReply("Goodbye from " + languageName + "/REPL"); } @@ -476,7 +471,7 @@ writer.println(TRACE_PREFIX + message); } - public void startSession() { + private void startContextSession() { while (true) { try { @@ -515,15 +510,19 @@ } else if (command instanceof REPLRemoteCommand) { final REPLRemoteCommand remoteCommand = (REPLRemoteCommand) command; - final REPLMessage request = remoteCommand.createRequest(clientContext, args); if (request == null) { continue; } REPLMessage[] replies = sendToServer(request); + remoteCommand.processReply(clientContext, replies); - remoteCommand.processReply(clientContext, replies); + final String path = replies[0].get(REPLMessage.FILE_PATH); + if (path != null && !path.isEmpty()) { + selectSource(path); + } + } else { assert false; // Should not happen. } @@ -651,7 +650,7 @@ } try { - clientContext.startSession(); + clientContext.startContextSession(); } finally { // To continue execution, pop the context and return
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java Tue Oct 27 17:44:27 2015 -0700 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + @ApiInfo( + group="To Review" + ) + */ + +/** + * This package contains <strong>REPL*</strong>: an experimental framework for + * building a <em>language-agnostic</em> command-line oriented debugger that: + * <ul> + * <li>works with every Truffle-implemented language "out of the box", + * i.e. requiring minimal additional support by language implementors;</li> + * <li>works simultaneously, without special configuration, for all Truffle language implementations + * available to it; and</li> + * <li>demonstrates Truffle language-interopability by debugging seamlessly + * across Truffle <em>cross-language</em> calls.</li> + * </ul> + * <p> + * <h4>Goals for building <strong>REPL*</strong></h4> + * <ol> + * <li>Exercise and test <em>in action</em> the built-in + * {@linkplain com.oracle.truffle.api.debug.Debugger} debugging support services + * provided as part of the Truffle API, which in turn relies on Truffle + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumentation}.</li> + * <li>Emulates a client/server architecture to demonstrate that + * language-agnostic debugging can be implemented over wire protocols. Wire + * protocol communication between client and server is <em>partially</em> emulated + * by passing messages expressed as textual key-value pairs. The emulation is + * <em>partial</em> because both run interleaved on a single JVM thread, with some + * sharing of resources.</li> + * <li>Provide a working debugger that is always available during development + * of new Truffle language implementations.</li> + * <li>Provide a working debugger with extra support for Truffle language + * development, in particular the ability to inspect the current structure + * of the Truffle AST around a halted location.</li> + * </ol> + * <h4>Command Set</h4> + * The Command Line Interface (CLI) for <strong>REPL*</strong> is based as + * much as possible on the CLI for the + * <a href="http://www.gnu.org/software/gdb/documentation/">GDB Debugger.</a> + * <h4>REPL* Functionality</h4> + * Basic navigation: + * <ul> + * <li>StepIn (n times)</li> + * <li>StepOut (n times)</li> + * <li>StepOver (n times)</li> + * </ul> + * Execution: + * <ul> + * <li>Load a file source</li> + * <li>Call a defined symbol</li> + * </ul> + * Stack: + * <ul> + * <li>List frames in current execution stack</li> + * <li>Select a frame</li> + * <li>Display selected frame contents</li> + * <li>Move frame selection up/down</li> + * </ul> + * Evaluate: + * <ul> + * <li>Evaluate a Language string in halted context</li> + * <li>Evaluate a Language string in selected frame</li> + * </ul> + * Breakpoints: + * <ul> + * <li>Set/create on a specified line</li> + * <li>Set/create on any throw (before exception created)</li> + * <li>Enable / Disable</li> + * <li>One-shot (once only)</li> + * <li>Get <em>Hit</em> count</li> + * <li>Set <em>Ignore</em> count</li> + * <li>Unset/dispose</li> + * <li>Get all breakpoints</li> + * <li>Find a breakpoint by UID</li> + * <li>Set/clear the condition on a breakpoint</li> + * </ul> + * Others: + * <ul> + * <li>Display halted location in source</li> + * <li>Nested execution + * <li>Help</li> + * <li>Info displays</li> + * <li>Set/display options</li> + * <li>Display Truffle AST structure</li> + * </ul> + */ +package com.oracle.truffle.tools.debug.shell; +
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package.html Tue Oct 20 11:30:34 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -<!DOCTYPE html> -<!-- - -Copyright (c) 2015, 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. ---> -<html> -<body> - This package contains an experimental framework for building simple command-line oriented - debuggers that work with Truffle-implemented languages; it is used mainly for testing Truffle's - built-in , which actually provides the - debugging services. - <p> - Truffle debugging is made possible by the general purpose Instrumentation Framework built into - the Truffle platform. Some online documentation for the Instrumentation Framework is available - online: - <q> <a href="https://wiki.openjdk.java.net/display/Graal/Instrumentation+API">https://wiki. - openjdk.java.net/display/Graal/Instrumentation+API</a> </q> - <p> - Building one of these command line debuggers requires creating language-specific instances of: - <ol> - <li>DebugEngine, noting that this instance also - depends on related services provided by the language implementation,</li> - <li>{@link com.oracle.truffle.tools.debug.shell.REPLServer}, best accomplished by copying the - implementation for Truffle's demonstration language "Simple" (a.k.a. "SL").</li> - </ol> - - <strong>Disclaimer: </strong> although these command line debuggers are useful, they are not - intended, and will not be maintained as, fully functioning debuggers. They should be considered - valuable tools for the maintainers of the - DebugEngine, as well as for Truffle language - implementors for whom concurrent access to any kind debugging services can be quite helpful. - <p> - <strong>Note:</strong> Both the functionality and API for this package are under active - development. - <p> - </body> -</html>
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Tue Oct 20 11:30:34 2015 -0400 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Tue Oct 27 17:44:27 2015 -0700 @@ -24,8 +24,11 @@ */ package com.oracle.truffle.tools.debug.shell.server; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameInstance; @@ -38,12 +41,9 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.vm.PolyglotEngine.Language; import com.oracle.truffle.tools.debug.shell.REPLMessage; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - /** * Server-side REPL implementation of an {@linkplain REPLMessage "op"}. * <p> @@ -63,30 +63,23 @@ /** * Gets the "op" implemented by this handler. */ - public final String getOp() { + final String getOp() { return op; } /** * Passes a request to this handler. */ - public abstract REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext); + abstract REPLMessage[] receive(REPLMessage request, REPLServer replServer); /** * Creates skeleton for a reply message that identifies the operation currently being handled. */ - protected final REPLMessage createReply() { + REPLMessage createReply() { return new REPLMessage(REPLMessage.OP, op); } /** - * Creates skeleton for a reply message that identifies a specified operation. - */ - protected static final REPLMessage createReply(String opString) { - return new REPLMessage(REPLMessage.OP, opString); - } - - /** * Completes a reply, reporting and explaining successful handling. */ protected static final REPLMessage[] finishReplySucceeded(REPLMessage reply, String explanation) { @@ -114,9 +107,26 @@ return replies; } - static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServerContext serverContext) { + protected static final REPLMessage[] createLanguageInfoReply(REPLServer replServer) { + final Language language = replServer.getLanguage(); + final ArrayList<REPLMessage> langMessages = new ArrayList<>(); + langMessages.add(createLanguageInfoMessage("Language Name", language.getName())); + langMessages.add(createLanguageInfoMessage("Language version", language.getVersion())); + return langMessages.toArray(new REPLMessage[0]); + } + + protected static final REPLMessage createLanguageInfoMessage(String key, String value) { + final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + infoMessage.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE); + infoMessage.put(REPLMessage.INFO_KEY, key); + infoMessage.put(REPLMessage.INFO_VALUE, value); + infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + return infoMessage; + } + + protected static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServer replServer) { final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BREAKPOINT_INFO); - infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(serverContext.getBreakpointID(breakpoint))); + infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); infoMessage.put(REPLMessage.BREAKPOINT_STATE, breakpoint.getState().getName()); infoMessage.put(REPLMessage.BREAKPOINT_HIT_COUNT, Integer.toString(breakpoint.getHitCount())); infoMessage.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, Integer.toString(breakpoint.getIgnoreCount())); @@ -128,8 +138,8 @@ return infoMessage; } - protected static REPLMessage createFrameInfoMessage(final REPLServerContext serverContext, FrameDebugDescription frame) { - final Visualizer visualizer = serverContext.getVisualizer(); + protected static final REPLMessage createFrameInfoMessage(final REPLServer replServer, FrameDebugDescription frame) { + final Visualizer visualizer = replServer.getVisualizer(); final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.FRAME_INFO); infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(frame.index())); final Node node = frame.node(); @@ -155,11 +165,11 @@ public static final REPLHandler BACKTRACE_HANDLER = new REPLHandler(REPLMessage.BACKTRACE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final ArrayList<REPLMessage> frameMessages = new ArrayList<>(); - for (FrameDebugDescription frame : serverContext.getStack()) { - frameMessages.add(createFrameInfoMessage(serverContext, frame)); + for (FrameDebugDescription frame : replServer.getCurrentContext().getStack()) { + frameMessages.add(createFrameInfoMessage(replServer, frame)); } if (frameMessages.size() > 0) { return frameMessages.toArray(new REPLMessage[0]); @@ -171,7 +181,7 @@ public static final REPLHandler BREAK_AT_LINE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_LINE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String path = request.get(REPLMessage.FILE_PATH); final String fileName = request.get(REPLMessage.SOURCE_NAME); @@ -193,20 +203,15 @@ if (ignoreCount == null) { ignoreCount = 0; } - final Debugger db = serverContext.db(); - if (db == null) { - return finishReplyFailed(reply, "debugger not initialized"); - } Breakpoint breakpoint; try { - breakpoint = db.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); - serverContext.registerBreakpoint(breakpoint); - } catch (Exception ex) { + breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); + } catch (IOException ex) { return finishReplyFailed(reply, ex); } reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); - reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(serverContext.getBreakpointID(breakpoint))); + reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber)); reply.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, ignoreCount.toString()); return finishReplySucceeded(reply, "Breakpoint set"); @@ -216,7 +221,7 @@ public static final REPLHandler BREAK_AT_LINE_ONCE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_LINE_ONCE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String path = request.get(REPLMessage.FILE_PATH); final String fileName = request.get(REPLMessage.SOURCE_NAME); @@ -234,14 +239,15 @@ if (lineNumber == null) { return finishReplyFailed(reply, "missing line number"); } + Breakpoint breakpoint; try { - Breakpoint b = serverContext.db().setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); - serverContext.registerBreakpoint(b); - } catch (Exception ex) { + breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); + } catch (IOException ex) { return finishReplyFailed(reply, ex); } reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); + reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber)); return finishReplySucceeded(reply, "One-shot line breakpoint set"); } @@ -250,11 +256,10 @@ public static final REPLHandler BREAK_AT_THROW_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_THROW) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); try { - Breakpoint b = serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); - serverContext.registerBreakpoint(b); + replServer.setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); return finishReplySucceeded(reply, "Breakpoint at any throw set"); } catch (Exception ex) { return finishReplyFailed(reply, ex); @@ -265,10 +270,10 @@ public static final REPLHandler BREAK_AT_THROW_ONCE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_THROW_ONCE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); try { - serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); + replServer.setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); return finishReplySucceeded(reply, "One-shot breakpoint at any throw set"); } catch (Exception ex) { return finishReplyFailed(reply, ex); @@ -279,11 +284,11 @@ public static final REPLHandler BREAKPOINT_INFO_HANDLER = new REPLHandler(REPLMessage.BREAKPOINT_INFO) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final ArrayList<REPLMessage> infoMessages = new ArrayList<>(); - for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { - infoMessages.add(createBreakpointInfoMessage(breakpoint, serverContext)); + for (Breakpoint breakpoint : replServer.getBreakpoints()) { + infoMessages.add(createBreakpointInfoMessage(breakpoint, replServer)); } if (infoMessages.size() > 0) { return infoMessages.toArray(new REPLMessage[0]); @@ -292,16 +297,38 @@ } }; + public static final REPLHandler CALL_HANDLER = new REPLHandler(REPLMessage.CALL) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.CALL); + final String callName = request.get(REPLMessage.CALL_NAME); + if (callName == null) { + return finishReplyFailed(reply, "no name specified"); + } + try { + replServer.call(callName); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, callName + " killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + return finishReplySucceeded(reply, callName + " returned"); + } + }; + public static final REPLHandler CLEAR_BREAK_HANDLER = new REPLHandler(REPLMessage.CLEAR_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -314,9 +341,9 @@ public static final REPLHandler CONTINUE_HANDLER = new REPLHandler(REPLMessage.CONTINUE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - serverContext.prepareContinue(); + replServer.getCurrentContext().prepareContinue(); return finishReplySucceeded(reply, "Continue mode entered"); } }; @@ -324,10 +351,10 @@ public static final REPLHandler DELETE_HANDLER = new REPLHandler(REPLMessage.DELETE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); int deleteCount = 0; - for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { + for (Breakpoint breakpoint : replServer.getBreakpoints()) { breakpoint.dispose(); deleteCount++; } @@ -341,13 +368,13 @@ public static final REPLHandler DISABLE_BREAK_HANDLER = new REPLHandler(REPLMessage.DISABLE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -360,13 +387,13 @@ public static final REPLHandler ENABLE_BREAK_HANDLER = new REPLHandler(REPLMessage.ENABLE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -375,10 +402,44 @@ return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " enabled"); } }; + public static final REPLHandler EVAL_HANDLER = new REPLHandler(REPLMessage.EVAL) { + @SuppressWarnings("unused") + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = createReply(); + final String sourceName = request.get(REPLMessage.SOURCE_NAME); + reply.put(REPLMessage.SOURCE_NAME, sourceName); + reply.put(REPLMessage.DEBUG_LEVEL, Integer.toString(replServer.getCurrentContext().getLevel())); + final String source = request.get(REPLMessage.CODE); + final Visualizer visualizer = replServer.getVisualizer(); + + final Integer frameNumber = request.getIntValue(REPLMessage.FRAME_NUMBER); + FrameInstance frameInstance = null; + if (frameNumber != null) { + final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack(); + if (frameNumber < 0 || frameNumber >= stack.size()) { + return finishReplyFailed(reply, "invalid frame number"); + } + final FrameDebugDescription frameDescription = stack.get(frameNumber); + // frameInstance = frameDescription == null ? null : + // frameDescription.frameInstance(); + } + try { + Object returnValue = replServer.getCurrentContext().eval(source, frameInstance); + return finishReplySucceeded(reply, visualizer.displayValue(returnValue, 0)); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, "eval (" + sourceName + ") killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + } + }; public static final REPLHandler FILE_HANDLER = new REPLHandler(REPLMessage.FILE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String fileName = request.get(REPLMessage.SOURCE_NAME); if (fileName == null) { @@ -410,20 +471,20 @@ public static final REPLHandler FRAME_HANDLER = new REPLHandler(REPLMessage.FRAME) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final Integer frameNumber = request.getIntValue(REPLMessage.FRAME_NUMBER); if (frameNumber == null) { return finishReplyFailed(reply, "no frame number specified"); } - final List<FrameDebugDescription> stack = serverContext.getStack(); + final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack(); if (frameNumber < 0 || frameNumber >= stack.size()) { return finishReplyFailed(reply, "frame number " + frameNumber + " out of range"); } final FrameDebugDescription frameDescription = stack.get(frameNumber); - final REPLMessage frameMessage = createFrameInfoMessage(serverContext, frameDescription); + final REPLMessage frameMessage = createFrameInfoMessage(replServer, frameDescription); final Frame frame = frameDescription.frameInstance().getFrame(FrameInstance.FrameAccess.READ_ONLY, true); - final Visualizer visualizer = serverContext.getVisualizer(); + final Visualizer visualizer = replServer.getVisualizer(); final FrameDescriptor frameDescriptor = frame.getFrameDescriptor(); try { final StringBuilder sb = new StringBuilder(); @@ -444,20 +505,65 @@ } }; + public static final REPLHandler INFO_HANDLER = new REPLHandler(REPLMessage.INFO) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final String topic = request.get(REPLMessage.TOPIC); + + if (topic == null || topic.isEmpty()) { + final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + return finishReplyFailed(message, "No info topic specified"); + } + + switch (topic) { + + case REPLMessage.LANGUAGE: + return createLanguageInfoReply(replServer); + + default: + final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + return finishReplyFailed(message, "No info about topic \"" + topic + "\""); + } + } + }; public static final REPLHandler KILL_HANDLER = new REPLHandler(REPLMessage.KILL) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - if (serverContext.getLevel() == 0) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + if (replServer.getCurrentContext().getLevel() == 0) { return finishReplyFailed(createReply(), "nothing to kill"); } throw new KillException(); } }; + public static final REPLHandler LOAD_HANDLER = new REPLHandler(REPLMessage.LOAD_SOURCE) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_SOURCE); + final String fileName = request.get(REPLMessage.SOURCE_NAME); + try { + final Source fileSource = Source.fromFileName(fileName); + // TODO (mlvdv) language-specific + assert replServer.getLanguage().getMimeTypes().contains(fileSource.getMimeType()); + replServer.eval(fileSource); + reply.put(REPLMessage.FILE_PATH, fileName); + return finishReplySucceeded(reply, fileName + " loaded"); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, fileName + " killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + } + }; + public static final REPLHandler QUIT_HANDLER = new REPLHandler(REPLMessage.QUIT) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { throw new QuitException(); } }; @@ -465,14 +571,14 @@ public static final REPLHandler SET_BREAK_CONDITION_HANDLER = new REPLHandler(REPLMessage.SET_BREAK_CONDITION) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.SET_BREAK_CONDITION); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } @@ -497,13 +603,13 @@ public static final REPLHandler STEP_INTO_HANDLER = new REPLHandler(REPLMessage.STEP_INTO) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer repeat = request.getIntValue(REPLMessage.REPEAT); if (repeat == null) { repeat = 1; } - serverContext.prepareStepInto(repeat); + replServer.getCurrentContext().prepareStepInto(repeat); return finishReplySucceeded(reply, "StepInto <" + repeat + "> enabled"); } }; @@ -511,8 +617,8 @@ public static final REPLHandler STEP_OUT_HANDLER = new REPLHandler(REPLMessage.STEP_OUT) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - serverContext.prepareStepOut(); + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + replServer.getCurrentContext().prepareStepOut(); return finishReplySucceeded(createReply(), "StepOut enabled"); } }; @@ -520,13 +626,13 @@ public static final REPLHandler STEP_OVER_HANDLER = new REPLHandler(REPLMessage.STEP_OVER) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext debugServerContextFrame) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer repeat = request.getIntValue(REPLMessage.REPEAT); if (repeat == null) { repeat = 1; } - debugServerContextFrame.prepareStepOver(repeat); + replServer.getCurrentContext().prepareStepOver(repeat); return finishReplySucceeded(reply, "StepOver <" + repeat + "> enabled"); } }; @@ -534,12 +640,12 @@ public static final REPLHandler TRUFFLE_HANDLER = new REPLHandler(REPLMessage.TRUFFLE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); + final ASTPrinter astPrinter = replServer.getVisualizer().getASTPrinter(); final String topic = request.get(REPLMessage.TOPIC); reply.put(REPLMessage.TOPIC, topic); - Node node = serverContext.getNodeAtHalt(); + Node node = replServer.getCurrentContext().getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); } @@ -553,7 +659,7 @@ while (node.getParent() != null) { node = node.getParent(); } - final String astText = astPrinter.printTreeToString(node, depth, serverContext.getNodeAtHalt()); + final String astText = astPrinter.printTreeToString(node, depth, replServer.getCurrentContext().getNodeAtHalt()); return finishReplySucceeded(reply, astText); case REPLMessage.SUBTREE: case REPLMessage.SUB: @@ -572,14 +678,14 @@ public static final REPLHandler UNSET_BREAK_CONDITION_HANDLER = new REPLHandler(REPLMessage.UNSET_BREAK_CONDITION) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.UNSET_BREAK_CONDITION); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } @@ -595,10 +701,10 @@ public static final REPLHandler TRUFFLE_NODE_HANDLER = new REPLHandler(REPLMessage.TRUFFLE_NODE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); - final Node node = serverContext.getNodeAtHalt(); + final ASTPrinter astPrinter = replServer.getVisualizer().getASTPrinter(); + final Node node = replServer.getCurrentContext().getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java Tue Oct 27 17:44:27 2015 -0700 @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2015, 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.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.StandardSyntaxTag; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.vm.EventConsumer; +import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.api.vm.PolyglotEngine.Language; +import com.oracle.truffle.api.vm.PolyglotEngine.Value; +import com.oracle.truffle.tools.debug.shell.REPLMessage; +import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; + +/** + * The server side of a simple message-based protocol for a possibly remote language + * Read-Eval-Print-Loop. + */ +public final class REPLServer { + + // Language-agnostic + private final PolyglotEngine engine; + private Debugger db; + private Context currentServerContext; + private SimpleREPLClient replClient = null; + private String statusPrefix; + private final Map<String, REPLHandler> handlerMap = new HashMap<>(); + + // TODO (mlvdv) Language-specific + private final PolyglotEngine.Language language; + private final Visualizer visualizer; + + // Breakpoints registered with the debugger + private int breakpointCounter; + private Map<Breakpoint, Integer> breakpoints = new WeakHashMap<>(); + + /** + * Create a single-language server. + */ + public REPLServer(String mimeType, Visualizer visualizer) { + this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer; + EventConsumer<SuspendedEvent> onHalted = new EventConsumer<SuspendedEvent>(SuspendedEvent.class) { + @Override + protected void on(SuspendedEvent ev) { + REPLServer.this.haltedAt(ev); + } + }; + EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) { + @Override + protected void on(ExecutionEvent event) { + db = event.getDebugger(); + event.prepareStepInto(); + } + }; + engine = PolyglotEngine.buildNew().onEvent(onHalted).onEvent(onExec).build(); + this.language = engine.getLanguages().get(mimeType); + if (language == null) { + throw new RuntimeException("Implementation not found for \"" + mimeType + "\""); + } + statusPrefix = languageName(language); + } + + public void add(REPLHandler handler) { + handlerMap.put(handler.getOp(), handler); + } + + /** + * Starts up a server; status returned in a message. + */ + public void start() { + + addHandlers(); + this.replClient = new SimpleREPLClient(this); + this.currentServerContext = new Context(null, null); + replClient.start(); + } + + protected void addHandlers() { + add(REPLHandler.BACKTRACE_HANDLER); + add(REPLHandler.BREAK_AT_LINE_HANDLER); + add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER); + add(REPLHandler.BREAK_AT_THROW_HANDLER); + add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER); + add(REPLHandler.BREAKPOINT_INFO_HANDLER); + add(REPLHandler.CALL_HANDLER); + add(REPLHandler.CLEAR_BREAK_HANDLER); + add(REPLHandler.CONTINUE_HANDLER); + add(REPLHandler.DELETE_HANDLER); + add(REPLHandler.DISABLE_BREAK_HANDLER); + add(REPLHandler.ENABLE_BREAK_HANDLER); + add(REPLHandler.EVAL_HANDLER); + add(REPLHandler.FILE_HANDLER); + add(REPLHandler.FRAME_HANDLER); + add(REPLHandler.INFO_HANDLER); + add(REPLHandler.KILL_HANDLER); + add(REPLHandler.LOAD_HANDLER); + add(REPLHandler.QUIT_HANDLER); + add(REPLHandler.SET_BREAK_CONDITION_HANDLER); + add(REPLHandler.STEP_INTO_HANDLER); + add(REPLHandler.STEP_OUT_HANDLER); + add(REPLHandler.STEP_OVER_HANDLER); + add(REPLHandler.TRUFFLE_HANDLER); + add(REPLHandler.TRUFFLE_NODE_HANDLER); + add(REPLHandler.UNSET_BREAK_CONDITION_HANDLER); + } + + void haltedAt(SuspendedEvent event) { + // Create and push a new debug context where execution is halted + currentServerContext = new Context(currentServerContext, event); + + // Message the client that execution is halted and is in a new debugging context + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.OP, REPLMessage.STOPPED); + final SourceSection src = event.getNode().getSourceSection(); + final Source source = src.getSource(); + message.put(REPLMessage.SOURCE_NAME, source.getName()); + message.put(REPLMessage.FILE_PATH, source.getPath()); + message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); + message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); + List<String> warnings = event.getRecentWarnings(); + if (!warnings.isEmpty()) { + final StringBuilder sb = new StringBuilder(); + for (String warning : warnings) { + sb.append(warning + "\n"); + } + message.put(REPLMessage.WARNINGS, sb.toString()); + } + try { + // Cheat with synchrony: call client directly about entering a nested debugging + // context. + replClient.halted(message); + } finally { + // Returns when "continue" or "kill" is called in the new debugging context + + // Pop the debug context, and return so that the old context will continue + currentServerContext = currentServerContext.predecessor; + } + } + + /** + * Execution context of a halted program, possibly nested. + */ + public final class Context { + + private final Context predecessor; + private final int level; + private final SuspendedEvent event; + + Context(Context predecessor, SuspendedEvent event) { + this.level = predecessor == null ? 0 : predecessor.getLevel() + 1; + this.predecessor = predecessor; + this.event = event; + } + + /** + * The nesting depth of this context in the current session. + */ + int getLevel() { + return level; + } + + /** + * The AST node where execution is halted in this context. + */ + Node getNodeAtHalt() { + return event.getNode(); + } + + /** + * Evaluates given code snippet in the context of currently suspended execution. + * + * @param code the snippet to evaluate + * @param frame <code>null</code> in case the evaluation should happen in top most frame, + * non-null value + * @return result of the evaluation + * @throws IOException if something goes wrong + */ + Object eval(String code, FrameInstance frame) throws IOException { + if (event == null) { + throw new IOException("top level \"eval\" not yet supported"); + } + return event.eval(code, frame); + } + + /** + * The frame where execution is halted in this context. + */ + MaterializedFrame getFrameAtHalt() { + return event.getFrame(); + } + + /** + * Dispatches a REPL request to the appropriate handler. + */ + REPLMessage[] receive(REPLMessage request) { + final String command = request.get(REPLMessage.OP); + final REPLHandler handler = handlerMap.get(command); + + if (handler == null) { + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.OP, command); + message.put(REPLMessage.STATUS, REPLMessage.FAILED); + message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported"); + final REPLMessage[] reply = new REPLMessage[]{message}; + return reply; + } + return handler.receive(request, REPLServer.this); + } + + /** + * Provides access to the execution stack. + * + * @return immutable list of stack elements + */ + List<FrameDebugDescription> getStack() { + List<FrameDebugDescription> frames = new ArrayList<>(); + int frameCount = 1; + for (FrameInstance frameInstance : event.getStack()) { + if (frameCount == 1) { + frames.add(new FrameDebugDescription(frameCount, event.getNode(), frameInstance)); + } else { + frames.add(new FrameDebugDescription(frameCount, frameInstance.getCallNode(), frameInstance)); + } + frameCount++; + } + return Collections.unmodifiableList(frames); + } + + void prepareStepOut() { + event.prepareStepOut(); + } + + void prepareStepInto(int repeat) { + event.prepareStepInto(repeat); + } + + void prepareStepOver(int repeat) { + event.prepareStepOver(repeat); + } + + void prepareContinue() { + event.prepareContinue(); + } + } + + /** + * Ask the server to handle a request. Return a non-empty array of messages to simulate remote + * operation where the protocol has possibly multiple messages being returned asynchronously in + * response to each request. + */ + public REPLMessage[] receive(REPLMessage request) { + if (currentServerContext == null) { + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.STATUS, REPLMessage.FAILED); + message.put(REPLMessage.DISPLAY_MSG, "server not started"); + final REPLMessage[] reply = new REPLMessage[]{message}; + return reply; + } + return currentServerContext.receive(request); + } + + Context getCurrentContext() { + return currentServerContext; + } + + Visualizer getVisualizer() { + return visualizer; + } + + // TODO (mlvdv) language-specific + Language getLanguage() { + return language; + } + + // TODO (mlvdv) language-specific + public String getLanguageName() { + return languageName(this.language); + } + + private static String languageName(Language lang) { + return lang.getName() + "(" + lang.getVersion() + ")"; + } + + void eval(Source source) throws IOException { + engine.eval(source); + } + + Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + final Breakpoint breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot); + registerBreakpoint(breakpoint); + return breakpoint; + } + + Breakpoint setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) throws IOException { + final Breakpoint breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot); + registerBreakpoint(breakpoint); + return breakpoint; + } + + private synchronized void registerBreakpoint(Breakpoint breakpoint) { + breakpoints.put(breakpoint, breakpointCounter++); + } + + synchronized Breakpoint findBreakpoint(int id) { + for (Map.Entry<Breakpoint, Integer> entrySet : breakpoints.entrySet()) { + if (id == entrySet.getValue()) { + return entrySet.getKey(); + } + } + return null; + } + + Collection<Breakpoint> getBreakpoints() { + return db.getBreakpoints(); + } + + synchronized int getBreakpointID(Breakpoint breakpoint) { + final Integer id = breakpoints.get(breakpoint); + return id == null ? -1 : id; + } + + void call(String name) throws IOException { + Value symbol = engine.findGlobalSymbol(name); + if (symbol == null) { + throw new IOException("symboleval f \"" + name + "\" not found"); + } + symbol.invoke(null); + } + +}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Tue Oct 20 11:30:34 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2015, 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.server; - -import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.instrument.Visualizer; -import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.api.vm.PolyglotEngine.Language; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class REPLServerContext { - - private final int level; - private final SuspendedEvent event; - - protected REPLServerContext(int level, SuspendedEvent event) { - this.level = level; - this.event = event; - } - - /** - * The nesting depth of this context in the current session. - */ - public int getLevel() { - return level; - } - - /** - * The AST node where execution is halted in this context. - */ - public Node getNodeAtHalt() { - return event.getNode(); - } - - /** - * Evaluates given code snippet in the context of currently suspended execution. - * - * @param code the snippet to evaluate - * @param frame <code>null</code> in case the evaluation should happen in top most frame, - * non-null value - * @return result of the evaluation - * @throws IOException if something goes wrong - */ - public Object eval(String code, FrameInstance frame) throws IOException { - return event.eval(code, frame); - } - - /** - * The frame where execution is halted in this context. - */ - public MaterializedFrame getFrameAtHalt() { - return event.getFrame(); - } - - public abstract Language getLanguage(); - - public Visualizer getVisualizer() { - return new DefaultVisualizer(); - } - - public PolyglotEngine engine() { - return vm(); - } - - /** - * @deprecated use {@link #engine()}. - */ - @Deprecated - public com.oracle.truffle.api.vm.PolyglotEngine vm() { - return engine(); - } - - protected abstract Debugger db(); - - /** - * Dispatches a REPL request to the appropriate handler. - */ - public abstract REPLMessage[] receive(REPLMessage request); - - public abstract void registerBreakpoint(Breakpoint breakpoint); - - public abstract Breakpoint findBreakpoint(int id); - - public abstract int getBreakpointID(Breakpoint breakpoint); - - /** - * Provides access to the execution stack. - * - * @return immutable list of stack elements - */ - public List<FrameDebugDescription> getStack() { - List<FrameDebugDescription> frames = new ArrayList<>(); - int frameCount = 1; - for (FrameInstance frameInstance : event.getStack()) { - if (frameCount == 1) { - frames.add(new FrameDebugDescription(frameCount, event.getNode(), frameInstance)); - } else { - frames.add(new FrameDebugDescription(frameCount, frameInstance.getCallNode(), frameInstance)); - } - frameCount++; - } - return Collections.unmodifiableList(frames); - } - - void prepareStepOut() { - event.prepareStepOut(); - } - - void prepareStepInto(int repeat) { - event.prepareStepInto(repeat); - } - - void prepareStepOver(int repeat) { - event.prepareStepOver(repeat); - } - - void prepareContinue() { - event.prepareContinue(); - } -}