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
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 27 Oct 2015 17:44:27 -0700
parents 2e5ac2d13d84
children 834c334de216 1c3deda60a9e
files mx.truffle/mx_truffle.py truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPL.java truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package.html truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java
diffstat 15 files changed, 793 insertions(+), 840 deletions(-) [+]
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();
-    }
-}