changeset 22451:ad180d3d4bd7

Merge with fa1061fb21fa5e69d4108201e9544d550a0fa036
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 10 Nov 2015 19:42:37 -0800
parents a8b796ac350d (current diff) fa1061fb21fa (diff)
children 163fdf64587d
files truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.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 14 files changed, 219 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Tue Nov 10 19:42:37 2015 -0800
@@ -26,7 +26,6 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.EventHandlerNode;
 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.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
@@ -81,8 +80,6 @@
             try {
                 result = child.execute(vFrame);
                 eventHandlerNode.returnValue(child, vFrame, result);
-            } catch (KillException e) {
-                throw (e);
             } catch (Exception e) {
                 eventHandlerNode.returnExceptional(child, vFrame, e);
                 throw (e);
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Tue Nov 10 19:42:37 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 Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Tue Nov 10 19:42:37 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 Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Tue Nov 10 19:42:37 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 Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Tue Nov 10 19:42:37 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/SuspendedEvent.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Tue Nov 10 19:42:37 2015 -0800
@@ -200,7 +200,7 @@
      * 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,
+     * @param frameNumber <code>null</code> in case the evaluation should happen in top most frame,
      *            non-null value to specify a frame from those {@link #getStack() currently on
      *            stack} to perform the evaluation in context of
      * @return the computed value
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Tue Nov 10 19:42:37 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 Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Tue Nov 10 19:42:37 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.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Tue Nov 10 19:42:37 2015 -0800
@@ -111,6 +111,12 @@
 
     @Override
     public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+        if (exception instanceof KillException) {
+            throw (KillException) exception;
+        }
+        if (exception instanceof QuitException) {
+            throw (QuitException) exception;
+        }
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
             firstInstrumentNode.returnExceptional(node, vFrame, exception);
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Tue Nov 10 19:42:37 2015 -0800
@@ -43,7 +43,6 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.EventHandlerNode;
 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.ProbeInstrument;
 import com.oracle.truffle.api.instrument.WrapperNode;
@@ -107,8 +106,6 @@
         try {
             result = child.executeGeneric(vFrame);
             eventHandlerNode.returnValue(child, vFrame, result);
-        } catch (KillException e) {
-            throw (e);
         } catch (Exception e) {
             eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Tue Nov 10 19:42:37 2015 -0800
@@ -43,7 +43,6 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.EventHandlerNode;
 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.ProbeInstrument;
 import com.oracle.truffle.api.instrument.WrapperNode;
@@ -100,8 +99,6 @@
         try {
             child.executeVoid(vFrame);
             eventHandlerNode.returnVoid(child, vFrame);
-        } catch (KillException e) {
-            throw (e);
         } catch (Exception e) {
             eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Tue Nov 10 19:42:37 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;
@@ -354,15 +355,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 Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Tue Nov 10 19:42:37 2015 -0800
@@ -36,10 +36,13 @@
 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.LineBreakpoint;
 import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.debug.TagBreakpoint;
 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.SyntaxTag;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.instrument.impl.DefaultVisualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -72,8 +75,18 @@
     private final PolyglotEngine.Language language;
     private final Visualizer visualizer;
 
-    // Breakpoints registered with the debugger
-    private int breakpointCounter;
+    private int nextBreakpointUID = 0;
+
+    /**
+     * Map: breakpoints => breakpoint UID.
+     * <ul>
+     * <li>Contains only pending breakpoints when the Debugger not yet available.</li>
+     * <li>When the Debugger becomes available, each pending breakpoint is replaced with a Debugger
+     * breakpoint, keeping same UID.</li>
+     * <li>Contains no disposed breakpoints.</li>
+     * <li>UIDs for disposed breakpoints are never reused.</li>
+     * </ul>
+     */
     private Map<Breakpoint, Integer> breakpoints = new WeakHashMap<>();
 
     /**
@@ -91,7 +104,31 @@
         EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) {
             @Override
             protected void on(ExecutionEvent event) {
-                db = event.getDebugger();
+                if (db == null) {
+                    db = event.getDebugger();
+                    if (!breakpoints.isEmpty()) {
+                        ArrayList<? extends Breakpoint> pendingBreakpoints = new ArrayList<>(breakpoints.keySet());
+                        try {
+                            for (Breakpoint pending : pendingBreakpoints) {
+
+                                Integer uid = breakpoints.get(pending);
+                                pending.dispose();
+                                Breakpoint replacement = null;
+                                if (pending instanceof PendingLineBreakpoint) {
+                                    final PendingLineBreakpoint lineBreak = (PendingLineBreakpoint) pending;
+                                    replacement = db.setLineBreakpoint(lineBreak.getIgnoreCount(), lineBreak.getLineLocation(), lineBreak.isOneShot());
+
+                                } else if (pending instanceof PendingTagBreakpoint) {
+                                    final PendingTagBreakpoint tagBreak = (PendingTagBreakpoint) pending;
+                                    replacement = db.setTagBreakpoint(tagBreak.getIgnoreCount(), tagBreak.getTag(), tagBreak.isOneShot());
+                                }
+                                breakpoints.put(replacement, uid);
+                            }
+                        } catch (IOException e) {
+                            throw new IllegalStateException("pending breakpoints should all be valid");
+                        }
+                    }
+                }
                 if (!currentServerContext.isEval) {
                     event.prepareStepInto();
                 }
@@ -341,19 +378,29 @@
     }
 
     Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
-        final Breakpoint breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot);
-        registerBreakpoint(breakpoint);
+        Breakpoint breakpoint;
+        if (db == null) {
+            breakpoint = new PendingLineBreakpoint(ignoreCount, lineLocation, oneShot);
+        } else {
+            breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot);
+        }
+        registerNewBreakpoint(breakpoint);
         return breakpoint;
     }
 
     Breakpoint setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) throws IOException {
-        final Breakpoint breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot);
-        registerBreakpoint(breakpoint);
+        Breakpoint breakpoint;
+        if (db == null) {
+            breakpoint = new PendingTagBreakpoint(ignoreCount, tag, oneShot);
+        } else {
+            breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot);
+        }
+        registerNewBreakpoint(breakpoint);
         return breakpoint;
     }
 
-    private synchronized void registerBreakpoint(Breakpoint breakpoint) {
-        breakpoints.put(breakpoint, breakpointCounter++);
+    private synchronized void registerNewBreakpoint(Breakpoint breakpoint) {
+        breakpoints.put(breakpoint, nextBreakpointUID++);
     }
 
     synchronized Breakpoint findBreakpoint(int id) {
@@ -365,8 +412,11 @@
         return null;
     }
 
+    /**
+     * Gets a list of the currently existing breakpoints.
+     */
     Collection<Breakpoint> getBreakpoints() {
-        return db.getBreakpoints();
+        return new ArrayList<>(breakpoints.keySet());
     }
 
     synchronized int getBreakpointID(Breakpoint breakpoint) {
@@ -382,4 +432,115 @@
         symbol.invoke(null);
     }
 
+    /**
+     * The intention to create a line breakpoint.
+     */
+    private final class PendingLineBreakpoint extends LineBreakpoint {
+
+        private Source conditionSource;
+
+        PendingLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
+            super(Breakpoint.State.ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot);
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            switch (getState()) {
+                case ENABLED_UNRESOLVED:
+                    if (!enabled) {
+                        setState(State.DISABLED_UNRESOLVED);
+                    }
+                    break;
+                case DISABLED_UNRESOLVED:
+                    if (enabled) {
+                        setState(State.ENABLED_UNRESOLVED);
+                    }
+                    break;
+                case DISPOSED:
+                    throw new IllegalStateException("Disposed breakpoints must stay disposed");
+                default:
+                    throw new IllegalStateException("Unexpected breakpoint state");
+            }
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return getState() == State.ENABLED_UNRESOLVED;
+        }
+
+        @Override
+        public void setCondition(String expr) throws IOException {
+            this.conditionSource = Source.fromText(expr, "breakpoint condition from text: " + expr);
+        }
+
+        @Override
+        public Source getCondition() {
+            return conditionSource;
+        }
+
+        @Override
+        public void dispose() {
+            if (getState() == State.DISPOSED) {
+                throw new IllegalStateException("Breakpoint already disposed");
+            }
+            setState(State.DISPOSED);
+            breakpoints.remove(this);
+        }
+    }
+
+    /**
+     * The intention to create a line breakpoint.
+     */
+    private final class PendingTagBreakpoint extends TagBreakpoint {
+
+        private Source conditionSource;
+
+        PendingTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) {
+            super(Breakpoint.State.ENABLED_UNRESOLVED, tag, ignoreCount, oneShot);
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            switch (getState()) {
+                case ENABLED_UNRESOLVED:
+                    if (!enabled) {
+                        setState(State.DISABLED_UNRESOLVED);
+                    }
+                    break;
+                case DISABLED_UNRESOLVED:
+                    if (enabled) {
+                        setState(State.ENABLED_UNRESOLVED);
+                    }
+                    break;
+                case DISPOSED:
+                    throw new IllegalStateException("Disposed breakpoints must stay disposed");
+                default:
+                    throw new IllegalStateException("Unexpected breakpoint state");
+            }
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return getState() == State.ENABLED_UNRESOLVED;
+        }
+
+        @Override
+        public void setCondition(String expr) throws IOException {
+            this.conditionSource = Source.fromText(expr, "breakpoint condition from text: " + expr);
+        }
+
+        @Override
+        public Source getCondition() {
+            return conditionSource;
+        }
+
+        @Override
+        public void dispose() {
+            if (getState() == State.DISPOSED) {
+                throw new IllegalStateException("Breakpoint already disposed");
+            }
+            setState(State.DISPOSED);
+            breakpoints.remove(this);
+        }
+    }
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java	Tue Nov 10 18:25:52 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java	Tue Nov 10 19:42:37 2015 -0800
@@ -35,7 +35,6 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.EventHandlerNode;
 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.SyntaxTag;
 import com.oracle.truffle.api.instrument.Visualizer;
@@ -212,8 +211,6 @@
             try {
                 result = child.execute(vFrame);
                 eventHandlerNode.returnValue(child, vFrame, result);
-            } catch (KillException e) {
-                throw (e);
             } catch (Exception e) {
                 eventHandlerNode.returnExceptional(child, vFrame, e);
                 throw (e);