changeset 22448:1c3deda60a9e

Truffle/Debugging: the REPL debugger now remembers breakpoint requests it receives from the command line client when there have been no executions yet (which means breakpoints cannot be set in the engine/debugger). When the first execution event arrives (with a reference to the Debugger), any "pending" breakpoints are created with the Debugger. This involved a bit of refactoring on the Breakpoint class hierarchy.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Wed, 04 Nov 2015 16:33:40 -0800
parents c3e397ce5941
children 5df298d7c949
files truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java 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
diffstat 8 files changed, 41 insertions(+), 427 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Wed Nov 04 16:33:40 2015 -0800
@@ -33,13 +33,10 @@
 import com.oracle.truffle.api.source.Source;
 
 /**
- * Breakpoint in a {@link com.oracle.truffle.api.vm.PolyglotEngine} with
- * {@link com.oracle.truffle.api.debug debugging turned on}. You can ask
- * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean)} or
- * {@link Debugger#setTagBreakpoint(int, com.oracle.truffle.api.instrument.SyntaxTag, boolean)} to
- * create an instance of {@link Breakpoint}.
+ * Breakpoint in an executing {@link com.oracle.truffle.api.vm.PolyglotEngine}.
+ *
+ * @see Debugger
  */
-@SuppressWarnings("javadoc")
 public abstract class Breakpoint {
 
     /**
@@ -111,7 +108,7 @@
 
     private State state;
 
-    Breakpoint(State state, int ignoreCount, boolean isOneShot) {
+    protected Breakpoint(State state, int ignoreCount, boolean isOneShot) {
         this.state = state;
         this.isOneShot = isOneShot;
         this.ignoreCount = ignoreCount;
@@ -200,7 +197,7 @@
         assert state == s;
     }
 
-    final void setState(State state) {
+    protected final void setState(State state) {
         this.state = state;
     }
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Wed Nov 04 16:33:40 2015 -0800
@@ -152,7 +152,7 @@
      * @throws IOException if the breakpoint can not be set.
      */
     @TruffleBoundary
-    public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
+    public LineBreakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
         return lineBreaks.create(ignoreCount, lineLocation, oneShot);
     }
 
@@ -165,7 +165,7 @@
      * @throws IOException if the breakpoint already set
      */
     @TruffleBoundary
-    public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException {
+    public TagBreakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException {
         return tagBreaks.create(ignoreCount, tag, oneShot);
     }
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Wed Nov 04 16:33:40 2015 -0800
@@ -31,16 +31,25 @@
  *
  * @see Debugger
  */
-abstract class LineBreakpoint extends Breakpoint {
+public abstract class LineBreakpoint extends Breakpoint {
+
+    private final LineLocation lineLocation;
 
-    LineBreakpoint(State state, int ignoreCount, boolean isOneShot) {
+    protected LineBreakpoint(State state, LineLocation lineLocation, int ignoreCount, boolean isOneShot) {
         super(state, ignoreCount, isOneShot);
+        this.lineLocation = lineLocation;
     }
 
     /**
      * Gets the {@linkplain LineLocation source line location} that specifies where this breakpoint
      * will trigger.
      */
-    public abstract LineLocation getLineLocation();
+    public final LineLocation getLineLocation() {
+        return lineLocation;
+    }
 
+    @Override
+    public String getLocationDescription() {
+        return "Line: " + lineLocation.getShortDescription();
+    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Wed Nov 04 16:33:40 2015 -0800
@@ -272,8 +272,6 @@
 
         private static final String SHOULD_NOT_HAPPEN = "LineBreakpointImpl:  should not happen";
 
-        private final LineLocation lineLocation;
-
         // Cached assumption that the global status of line breakpoint activity has not changed.
         private Assumption breakpointsActiveAssumption;
 
@@ -290,9 +288,7 @@
         private List<ProbeInstrument> instruments = new ArrayList<>();
 
         public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
-            super(ENABLED_UNRESOLVED, ignoreCount, oneShot);
-            this.lineLocation = lineLocation;
-
+            super(ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot);
             this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
             this.isEnabled = true;
             this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged");
@@ -471,16 +467,6 @@
             warningLog.addWarning(String.format("Exception in %s:  %s", getShortDescription(), ex.getMessage()));
         }
 
-        @Override
-        public String getLocationDescription() {
-            return "Line: " + lineLocation.getShortDescription();
-        }
-
-        @Override
-        public LineLocation getLineLocation() {
-            return lineLocation;
-        }
-
         private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Wed Nov 04 16:33:40 2015 -0800
@@ -31,15 +31,24 @@
  *
  * @see Debugger
  */
-abstract class TagBreakpoint extends Breakpoint {
+public abstract class TagBreakpoint extends Breakpoint {
+
+    private final SyntaxTag tag;
 
-    TagBreakpoint(State state, int ignoreCount, boolean isOneShot) {
+    protected TagBreakpoint(State state, SyntaxTag tag, int ignoreCount, boolean isOneShot) {
         super(state, ignoreCount, isOneShot);
+        this.tag = tag;
     }
 
     /**
      * Gets the tag that specifies where this breakpoint will trigger.
      */
-    public abstract SyntaxTag getTag();
+    public final SyntaxTag getTag() {
+        return tag;
+    }
 
+    @Override
+    public String getLocationDescription() {
+        return "Tag " + tag.name();
+    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Tue Oct 27 17:44:27 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Wed Nov 04 16:33:40 2015 -0800
@@ -241,8 +241,6 @@
 
         private static final String SHOULD_NOT_HAPPEN = "TagBreakpointImpl:  should not happen";
 
-        private final SyntaxTag tag;
-
         // Cached assumption that the global status of tag breakpoint activity has not changed.
         private Assumption breakpointsActiveAssumption;
 
@@ -259,8 +257,7 @@
         private List<ProbeInstrument> instruments = new ArrayList<>();
 
         private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) {
-            super(ENABLED, ignoreCount, oneShot);
-            this.tag = tag;
+            super(ENABLED, tag, ignoreCount, oneShot);
             this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
             this.isEnabled = true;
             this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged");
@@ -359,7 +356,7 @@
 
         @TruffleBoundary
         private String getShortDescription() {
-            return BREAKPOINT_NAME + "@" + tag.name();
+            return BREAKPOINT_NAME + "@" + getTag().name();
         }
 
         private void changeState(State after) {
@@ -433,16 +430,6 @@
             warningLog.addWarning(String.format("Exception in %s:  %s", getShortDescription(), ex.getMessage()));
         }
 
-        @Override
-        public String getLocationDescription() {
-            return "Tag " + tag.name();
-        }
-
-        @Override
-        public SyntaxTag getTag() {
-            return tag;
-        }
-
         private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
@@ -450,7 +437,5 @@
                 TagBreakpointImpl.this.nodeEnter(node, vFrame);
             }
         }
-
     }
-
 }
--- a/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
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Wed Nov 04 16:33:40 2015 -0800
@@ -26,6 +26,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import com.oracle.truffle.api.debug.Breakpoint;
@@ -353,15 +354,14 @@
         @Override
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final REPLMessage reply = createReply();
-            int deleteCount = 0;
-            for (Breakpoint breakpoint : replServer.getBreakpoints()) {
-                breakpoint.dispose();
-                deleteCount++;
-            }
-            if (deleteCount == 0) {
+            final Collection<Breakpoint> breakpoints = replServer.getBreakpoints();
+            if (breakpoints.isEmpty()) {
                 return finishReplyFailed(reply, "no breakpoints to delete");
             }
-            return finishReplySucceeded(reply, Integer.toString(deleteCount) + " breakpoints deleted");
+            for (Breakpoint breakpoint : breakpoints) {
+                breakpoint.dispose();
+            }
+            return finishReplySucceeded(reply, Integer.toString(breakpoints.size()) + " breakpoints deleted");
         }
     };
 
--- a/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
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,372 +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 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);
-    }
-
-}