changeset 22482:e4dd15f04c7d

Merge with 825d0d0301c9a923065d5c52ecb4dd17e649ddab
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 07 Dec 2015 21:23:02 -0800
parents 07f3efb4e321 (diff) 825d0d0301c9 (current diff)
children 8d19a118c109
files truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java
diffstat 10 files changed, 315 insertions(+), 285 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Mon Dec 07 21:23:02 2015 -0800
@@ -33,7 +33,6 @@
 import java.lang.annotation.Target;
 import java.util.Map;
 import java.util.Objects;
-import java.util.WeakHashMap;
 
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.SuspendedEvent;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Mon Dec 07 21:23:02 2015 -0800
@@ -33,10 +33,13 @@
 import com.oracle.truffle.api.source.Source;
 
 /**
- * Breakpoint in an executing {@link com.oracle.truffle.api.vm.PolyglotEngine}.
- *
- * @see Debugger
+ * 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}.
  */
+@SuppressWarnings("javadoc")
 public abstract class Breakpoint {
 
     /**
@@ -108,7 +111,7 @@
 
     private State state;
 
-    protected Breakpoint(State state, int ignoreCount, boolean isOneShot) {
+    Breakpoint(State state, int ignoreCount, boolean isOneShot) {
         this.state = state;
         this.isOneShot = isOneShot;
         this.ignoreCount = ignoreCount;
@@ -197,7 +200,7 @@
         assert state == s;
     }
 
-    protected final void setState(State state) {
+    final void setState(State state) {
         this.state = state;
     }
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Mon Dec 07 21:23:02 2015 -0800
@@ -154,7 +154,7 @@
      * @throws IOException if the breakpoint can not be set.
      */
     @TruffleBoundary
-    public LineBreakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
+    public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
         return lineBreaks.create(ignoreCount, lineLocation, oneShot);
     }
 
@@ -167,7 +167,7 @@
      * @throws IOException if the breakpoint already set
      */
     @TruffleBoundary
-    public TagBreakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException {
+    public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException {
         return tagBreaks.create(ignoreCount, tag, oneShot);
     }
 
@@ -680,8 +680,11 @@
          */
         private MaterializedFrame haltedFrame;
 
-        /** Subset of the Truffle stack corresponding to the current execution. */
-        private List<FrameDebugDescription> contextStack;
+        /**
+         * Subset of the Truffle stack corresponding to the current execution, not including the
+         * current frame.
+         */
+        private List<FrameInstance> contextStack;
 
         private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) {
             this(executionSource, previousContext, -1);
@@ -759,11 +762,10 @@
             final List<String> recentWarnings = new ArrayList<>(warnings);
             warnings.clear();
 
-            final List<FrameDebugDescription> frames = new ArrayList<>();
+            final List<FrameInstance> frames = new ArrayList<>();
             // Map the Truffle stack for this execution, ignore nested executions
             // Ignore frames for which no CallNode is available.
-            // The top/current/0 frame is not produced by the iterator.
-            frames.add(new FrameDebugDescription(0, haltedNode, haltedFrame));
+            // The top/current/0 frame is not produced by the iterator; reported separately
             Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() {
                 int stackIndex = 1;
                 int frameIndex = 1;
@@ -774,8 +776,8 @@
                         final Node callNode = frameInstance.getCallNode();
                         if (callNode != null) {
                             final SourceSection sourceSection = callNode.getEncapsulatingSourceSection();
-                            if (sourceSection != null && sourceSection.getIdentifier() != SourceSection.UNKNOWN) {
-                                frames.add(new FrameDebugDescription(frameIndex, frameInstance));
+                            if (sourceSection != null && !sourceSection.getIdentifier().equals("<unknown>")) {
+                                frames.add(frameInstance);
                                 frameIndex++;
                             } else if (TRACE) {
                                 if (callNode != null) {
@@ -783,7 +785,7 @@
                                 } else {
                                     contextTrace("HIDDEN frame added");
                                 }
-                                frames.add(new FrameDebugDescription(frameIndex, frameInstance));
+                                frames.add(frameInstance);
                                 frameIndex++;
                             }
                         } else if (TRACE) {
@@ -792,7 +794,7 @@
                             } else {
                                 contextTrace("HIDDEN frame added");
                             }
-                            frames.add(new FrameDebugDescription(frameIndex, frameInstance));
+                            frames.add(frameInstance);
                             frameIndex++;
                         }
                         stackIndex++;
@@ -813,7 +815,7 @@
 
             try {
                 // Pass control to the debug client with current execution suspended
-                ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, contextStack, recentWarnings));
+                ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, haltedNode, haltedFrame, contextStack, recentWarnings));
                 // Debug client finished normally, execution resumes
                 // Presume that the client has set a new strategy (or default to Continue)
                 running = true;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Mon Dec 07 21:23:02 2015 -0800
@@ -31,25 +31,16 @@
  *
  * @see Debugger
  */
-public abstract class LineBreakpoint extends Breakpoint {
-
-    private final LineLocation lineLocation;
+abstract class LineBreakpoint extends Breakpoint {
 
-    protected LineBreakpoint(State state, LineLocation lineLocation, int ignoreCount, boolean isOneShot) {
+    LineBreakpoint(State state, 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 final LineLocation getLineLocation() {
-        return lineLocation;
-    }
+    public abstract LineLocation getLineLocation();
 
-    @Override
-    public String getLocationDescription() {
-        return "Line: " + lineLocation.getShortDescription();
-    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Mon Dec 07 21:23:02 2015 -0800
@@ -271,6 +271,8 @@
 
         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;
 
@@ -287,7 +289,9 @@
         private List<ProbeInstrument> instruments = new ArrayList<>();
 
         public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
-            super(ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot);
+            super(ENABLED_UNRESOLVED, ignoreCount, oneShot);
+            this.lineLocation = lineLocation;
+
             this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
             this.isEnabled = true;
             this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged");
@@ -464,6 +468,16 @@
             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	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Mon Dec 07 21:23:02 2015 -0800
@@ -30,6 +30,8 @@
 import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.FrameInstance;
+import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.nodes.Node;
@@ -58,15 +60,19 @@
     }
 
     private final Debugger debugger;
-    private final List<FrameDebugDescription> stack;
+    private final Node haltedNode;
+    private final MaterializedFrame haltedFrame;
+    private final List<FrameInstance> stack;
     private final List<String> warnings;
 
-    SuspendedEvent(Debugger debugger, List<FrameDebugDescription> stack, List<String> warnings) {
+    SuspendedEvent(Debugger debugger, Node haltedNode, MaterializedFrame haltedFrame, List<FrameInstance> stack, List<String> warnings) {
         this.debugger = debugger;
+        this.haltedNode = haltedNode;
+        this.haltedFrame = haltedFrame;
         this.stack = stack;
         this.warnings = warnings;
         if (TRACE) {
-            trace("Execution suspended at Node=" + stack.get(0).node());
+            trace("Execution suspended at Node=" + haltedNode);
         }
     }
 
@@ -83,11 +89,11 @@
     }
 
     public Node getNode() {
-        return stack.get(0).node();
+        return haltedNode;
     }
 
     public MaterializedFrame getFrame() {
-        return stack.get(0).frame();
+        return haltedFrame;
     }
 
     public List<String> getRecentWarnings() {
@@ -96,12 +102,13 @@
 
     /**
      * Gets the stack frames from the currently halted
-     * {@link com.oracle.truffle.api.vm.PolyglotEngine} execution.
+     * {@link com.oracle.truffle.api.vm.PolyglotEngine} execution, not counting the Node and Frame
+     * where halted.
      *
      * @return list of stack frames
      */
     @CompilerDirectives.TruffleBoundary
-    public List<FrameDebugDescription> getStack() {
+    public List<FrameInstance> getStack() {
         return stack;
     }
 
@@ -188,20 +195,20 @@
      * Evaluates given code snippet in the context of currently suspended execution.
      *
      * @param code the snippet to evaluate
-     * @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
+     * @param frameNumber specify a frame from those {@link #getStack() currently on stack} to
+     *            perform the evaluation in context of
      * @return the computed value
      * @throws IOException in case an evaluation goes wrong
      */
     public Object eval(String code, Integer frameNumber) throws IOException {
-        int n = 0;
-        if (frameNumber != null) {
-            if (frameNumber < 0 || frameNumber >= stack.size()) {
-                throw new IOException("invalid frame number");
-            }
-            n = frameNumber;
+        if (frameNumber < 0 || frameNumber >= stack.size()) {
+            throw new IOException("invalid frame number");
         }
-        return debugger.evalInContext(this, code, stack.get(n).node(), stack.get(n).frame());
+        if (frameNumber == 0) {
+            return debugger.evalInContext(this, code, haltedNode, haltedFrame);
+        }
+        final FrameInstance instance = stack.get(frameNumber - 1);
+        final MaterializedFrame frame = instance.getFrame(FrameAccess.MATERIALIZE, true).materialize();
+        return debugger.evalInContext(this, code, instance.getCallNode(), frame);
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Mon Dec 07 21:23:02 2015 -0800
@@ -31,24 +31,15 @@
  *
  * @see Debugger
  */
-public abstract class TagBreakpoint extends Breakpoint {
-
-    private final SyntaxTag tag;
+abstract class TagBreakpoint extends Breakpoint {
 
-    protected TagBreakpoint(State state, SyntaxTag tag, int ignoreCount, boolean isOneShot) {
+    TagBreakpoint(State state, int ignoreCount, boolean isOneShot) {
         super(state, ignoreCount, isOneShot);
-        this.tag = tag;
     }
 
     /**
      * Gets the tag that specifies where this breakpoint will trigger.
      */
-    public final SyntaxTag getTag() {
-        return tag;
-    }
+    public abstract SyntaxTag getTag();
 
-    @Override
-    public String getLocationDescription() {
-        return "Tag " + tag.name();
-    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Dec 07 21:23:02 2015 -0800
@@ -240,6 +240,8 @@
 
         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;
 
@@ -256,7 +258,8 @@
         private List<ProbeInstrument> instruments = new ArrayList<>();
 
         private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) {
-            super(ENABLED, tag, ignoreCount, oneShot);
+            super(ENABLED, ignoreCount, oneShot);
+            this.tag = tag;
             this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
             this.isEnabled = true;
             this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged");
@@ -353,7 +356,7 @@
 
         @TruffleBoundary
         private String getShortDescription() {
-            return BREAKPOINT_NAME + "@" + getTag().name();
+            return BREAKPOINT_NAME + "@" + tag.name();
         }
 
         private void changeState(State after) {
@@ -427,6 +430,16 @@
             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
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Mon Dec 07 21:23:02 2015 -0800
@@ -29,10 +29,10 @@
 import java.util.Collection;
 import java.util.List;
 
-import com.oracle.truffle.api.debug.Breakpoint;
-import com.oracle.truffle.api.debug.FrameDebugDescription;
-import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameInstance;
+import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.instrument.ASTPrinter;
 import com.oracle.truffle.api.instrument.KillException;
 import com.oracle.truffle.api.instrument.QuitException;
@@ -43,6 +43,7 @@
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.vm.PolyglotEngine.Language;
 import com.oracle.truffle.tools.debug.shell.REPLMessage;
+import com.oracle.truffle.tools.debug.shell.server.REPLServer.BreakpointInfo;
 import com.oracle.truffle.tools.debug.shell.server.REPLServer.Context;
 
 /**
@@ -108,15 +109,15 @@
         return replies;
     }
 
-    protected static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServer replServer) {
+    protected static final REPLMessage createBreakpointInfoMessage(BreakpointInfo info) {
         final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BREAKPOINT_INFO);
-        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()));
-        infoMessage.put(REPLMessage.INFO_VALUE, breakpoint.getLocationDescription().toString());
-        if (breakpoint.getCondition() != null) {
-            infoMessage.put(REPLMessage.BREAKPOINT_CONDITION, breakpoint.getCondition().getCode());
+        infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(info.getID()));
+        infoMessage.put(REPLMessage.BREAKPOINT_STATE, info.describeState());
+        infoMessage.put(REPLMessage.BREAKPOINT_HIT_COUNT, Integer.toString(info.getHitCount()));
+        infoMessage.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, Integer.toString(info.getIgnoreCount()));
+        infoMessage.put(REPLMessage.INFO_VALUE, info.describeLocation());
+        if (info.getCondition() != null) {
+            infoMessage.put(REPLMessage.BREAKPOINT_CONDITION, info.getCondition());
         }
         infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED);
         return infoMessage;
@@ -150,10 +151,11 @@
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final Visualizer visualizer = replServer.getVisualizer();
             final ArrayList<REPLMessage> replies = new ArrayList<>();
-            final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack();
-
-            for (int i = 0; i < stack.size(); i++) {
-                replies.add(btMessage(i, stack.get(i).node(), visualizer));
+            final Context currentContext = replServer.getCurrentContext();
+            final List<FrameInstance> stack = currentContext.getStack();
+            replies.add(btMessage(0, currentContext.getNode(), visualizer));
+            for (int i = 1; i <= stack.size(); i++) {
+                replies.add(btMessage(i, stack.get(i - 1).getCallNode(), visualizer));
             }
             if (replies.size() > 0) {
                 return replies.toArray(new REPLMessage[0]);
@@ -199,7 +201,7 @@
             if (source == null) {
                 return finishReplyFailed(reply, fileName + " not found");
             }
-            Integer lineNumber = request.getIntValue(REPLMessage.LINE_NUMBER);
+            final Integer lineNumber = request.getIntValue(REPLMessage.LINE_NUMBER);
             if (lineNumber == null) {
                 return finishReplyFailed(reply, "missing line number");
             }
@@ -207,15 +209,10 @@
             if (ignoreCount == null) {
                 ignoreCount = 0;
             }
-            Breakpoint breakpoint;
-            try {
-                breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false);
-            } catch (IOException ex) {
-                return finishReplyFailed(reply, ex);
-            }
+            final BreakpointInfo breakpointInfo = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false);
             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.BREAKPOINT_ID, Integer.toString(breakpointInfo.getID()));
             reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber));
             reply.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, ignoreCount.toString());
             return finishReplySucceeded(reply, "Breakpoint set");
@@ -239,19 +236,14 @@
             if (source == null) {
                 return finishReplyFailed(reply, fileName + " not found");
             }
-            Integer lineNumber = request.getIntValue(REPLMessage.LINE_NUMBER);
+            final Integer lineNumber = request.getIntValue(REPLMessage.LINE_NUMBER);
             if (lineNumber == null) {
                 return finishReplyFailed(reply, "missing line number");
             }
-            Breakpoint breakpoint;
-            try {
-                breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true);
-            } catch (IOException ex) {
-                return finishReplyFailed(reply, ex);
-            }
+            final BreakpointInfo breakpointInfo = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true);
             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.BREAKPOINT_ID, Integer.toString(breakpointInfo.getID()));
             reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber));
             return finishReplySucceeded(reply, "One-shot line breakpoint set");
         }
@@ -291,8 +283,8 @@
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final REPLMessage reply = createReply();
             final ArrayList<REPLMessage> infoMessages = new ArrayList<>();
-            for (Breakpoint breakpoint : replServer.getBreakpoints()) {
-                infoMessages.add(createBreakpointInfoMessage(breakpoint, replServer));
+            for (BreakpointInfo breakpointInfo : replServer.getBreakpoints()) {
+                infoMessages.add(createBreakpointInfoMessage(breakpointInfo));
             }
             if (infoMessages.size() > 0) {
                 return infoMessages.toArray(new REPLMessage[0]);
@@ -338,15 +330,15 @@
         @Override
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final REPLMessage reply = createReply();
-            Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID);
+            final Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID);
             if (breakpointNumber == null) {
                 return finishReplyFailed(reply, "missing breakpoint number");
             }
-            final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber);
-            if (breakpoint == null) {
+            final BreakpointInfo breakpointInfo = replServer.findBreakpoint(breakpointNumber);
+            if (breakpointInfo == null) {
                 return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber);
             }
-            replServer.clearBreakpoint(breakpoint);
+            breakpointInfo.dispose();
             reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber));
             return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " cleared");
         }
@@ -367,12 +359,12 @@
         @Override
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final REPLMessage reply = createReply();
-            final Collection<Breakpoint> breakpoints = replServer.getBreakpoints();
+            final Collection<BreakpointInfo> breakpoints = replServer.getBreakpoints();
             if (breakpoints.isEmpty()) {
                 return finishReplyFailed(reply, "no breakpoints to delete");
             }
-            for (Breakpoint breakpoint : breakpoints) {
-                breakpoint.dispose();
+            for (BreakpointInfo breakpointInfo : breakpoints) {
+                breakpointInfo.dispose();
             }
             return finishReplySucceeded(reply, Integer.toString(breakpoints.size()) + " breakpoints deleted");
         }
@@ -387,11 +379,11 @@
             if (breakpointNumber == null) {
                 return finishReplyFailed(reply, "missing breakpoint number");
             }
-            final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber);
-            if (breakpoint == null) {
+            final BreakpointInfo breakpointInfo = replServer.findBreakpoint(breakpointNumber);
+            if (breakpointInfo == null) {
                 return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber);
             }
-            breakpoint.setEnabled(false);
+            breakpointInfo.setEnabled(false);
             reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber));
             return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " disabled");
         }
@@ -406,11 +398,11 @@
             if (breakpointNumber == null) {
                 return finishReplyFailed(reply, "missing breakpoint number");
             }
-            final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber);
-            if (breakpoint == null) {
+            final BreakpointInfo breakpointInfo = replServer.findBreakpoint(breakpointNumber);
+            if (breakpointInfo == null) {
                 return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber);
             }
-            breakpoint.setEnabled(true);
+            breakpointInfo.setEnabled(true);
             reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber));
             return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " enabled");
         }
@@ -479,17 +471,24 @@
             if (frameNumber == null) {
                 return finishReplyFailed(createReply(), "no frame number specified");
             }
-            final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack();
-            if (frameNumber < 0 || frameNumber >= stack.size()) {
+            final Context currentContext = replServer.getCurrentContext();
+            final List<FrameInstance> stack = currentContext.getStack();
+            if (frameNumber < 0 || frameNumber > stack.size()) {
                 return finishReplyFailed(createReply(), "frame number " + frameNumber + " out of range");
             }
             final Visualizer visualizer = replServer.getVisualizer();
 
-            final FrameDebugDescription frameDescription = stack.get(frameNumber);
-            final Frame frame = frameDescription.frame();
-            final Node node = frameDescription.node();
+            MaterializedFrame frame;
+            Node node;
+            if (frameNumber == 0) {
+                frame = currentContext.getFrame();
+                node = currentContext.getNode();
+            } else {
+                final FrameInstance instance = stack.get(frameNumber - 1);
+                frame = instance.getFrame(FrameAccess.MATERIALIZE, true).materialize();
+                node = instance.getCallNode();
+            }
             List<? extends FrameSlot> slots = frame.getFrameDescriptor().getSlots();
-
             if (slots.size() == 0) {
                 final REPLMessage emptyFrameMessage = createFrameInfoMessage(replServer, frameNumber, node);
                 return finishReplySucceeded(emptyFrameMessage, "empty frame");
@@ -628,8 +627,8 @@
                 return finishReplyFailed(message, "missing breakpoint number");
             }
             message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber));
-            final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber);
-            if (breakpoint == null) {
+            final BreakpointInfo breakpointInfo = replServer.findBreakpoint(breakpointNumber);
+            if (breakpointInfo == null) {
                 return finishReplyFailed(message, "no breakpoint number " + breakpointNumber);
             }
             final String expr = request.get(REPLMessage.BREAKPOINT_CONDITION);
@@ -637,7 +636,7 @@
                 return finishReplyFailed(message, "missing condition for " + breakpointNumber);
             }
             try {
-                breakpoint.setCondition(expr);
+                breakpointInfo.setCondition(expr);
             } catch (IOException ex) {
                 return finishReplyFailed(message, "invalid condition for " + breakpointNumber);
             } catch (UnsupportedOperationException ex) {
@@ -735,12 +734,12 @@
                 return finishReplyFailed(message, "missing breakpoint number");
             }
             message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber));
-            final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber);
-            if (breakpoint == null) {
+            final BreakpointInfo breakpointInfo = replServer.findBreakpoint(breakpointNumber);
+            if (breakpointInfo == null) {
                 return finishReplyFailed(message, "no breakpoint number " + breakpointNumber);
             }
             try {
-                breakpoint.setCondition(null);
+                breakpointInfo.setCondition(null);
             } catch (Exception ex) {
                 return finishReplyFailed(message, ex);
             }
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Mon Dec 07 12:51:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Mon Dec 07 21:23:02 2015 -0800
@@ -36,12 +36,11 @@
 import java.util.WeakHashMap;
 
 import com.oracle.truffle.api.debug.Breakpoint;
+import com.oracle.truffle.api.debug.Breakpoint.State;
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.ExecutionEvent;
-import com.oracle.truffle.api.debug.FrameDebugDescription;
-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;
@@ -64,6 +63,13 @@
  */
 public final class REPLServer {
 
+    enum BreakpointKind {
+        LINE,
+        TAG
+    }
+
+    private static int nextBreakpointUID = 0;
+
     // Language-agnostic
     private final PolyglotEngine engine;
     private Debugger db;
@@ -87,19 +93,7 @@
     private PolyglotEngine.Language defaultLanguage;
     private final Visualizer visualizer;
 
-    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<>();
+    private Map<Integer, BreakpointInfo> breakpoints = new WeakHashMap<>();
 
     public REPLServer(String defaultMIMEType, Visualizer visualizer) {
         this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer;
@@ -135,27 +129,8 @@
         protected void on(ExecutionEvent event) {
             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");
-                    }
+                for (BreakpointInfo breakpointInfo : breakpoints.values()) {
+                    breakpointInfo.activate(db);
                 }
             }
             if (currentServerContext.steppingInto) {
@@ -329,7 +304,7 @@
             }
             this.steppingInto = stepInto;
             try {
-                return symbol.invoke(null, args.toArray(new Object[0])).get();
+                return symbol.execute(args.toArray(new Object[0])).get();
             } finally {
                 this.steppingInto = false;
             }
@@ -402,11 +377,25 @@
         }
 
         /**
-         * Provides access to the execution stack.
+         * @return Node where halted
+         */
+        Node getNode() {
+            return event.getNode();
+        }
+
+        /**
+         * @return Frame where halted
+         */
+        MaterializedFrame getFrame() {
+            return event.getFrame();
+        }
+
+        /**
+         * Provides access to the execution stack, not counting the node/frame where halted.
          *
          * @return immutable list of stack elements
          */
-        List<FrameDebugDescription> getStack() {
+        List<FrameInstance> getStack() {
             return event.getStack();
         }
 
@@ -500,168 +489,190 @@
         return language.getMimeTypes().iterator().next();
     }
 
-    Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
-        Breakpoint breakpoint;
-        if (db == null) {
-            breakpoint = new PendingLineBreakpoint(ignoreCount, lineLocation, oneShot);
-        } else {
-            breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot);
-        }
-        registerNewBreakpoint(breakpoint);
-        return breakpoint;
+    BreakpointInfo setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
+        return new BreakpointInfo(db, lineLocation, ignoreCount, oneShot);
     }
 
-    Breakpoint setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) throws IOException {
-        Breakpoint breakpoint;
-        if (db == null) {
-            breakpoint = new PendingTagBreakpoint(ignoreCount, tag, oneShot);
-        } else {
-            breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot);
-        }
-        registerNewBreakpoint(breakpoint);
-        return breakpoint;
+    BreakpointInfo setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) {
+        return new BreakpointInfo(db, tag, ignoreCount, oneShot);
     }
 
-    private synchronized void registerNewBreakpoint(Breakpoint breakpoint) {
-        breakpoints.put(breakpoint, nextBreakpointUID++);
-    }
-
-    synchronized Breakpoint findBreakpoint(int id) {
-        for (Map.Entry<Breakpoint, Integer> entrySet : breakpoints.entrySet()) {
-            if (id == entrySet.getValue()) {
-                return entrySet.getKey();
-            }
-        }
-        return null;
+    synchronized BreakpointInfo findBreakpoint(int id) {
+        return breakpoints.get(id);
     }
 
     /**
      * Gets a list of the currently existing breakpoints.
      */
-    Collection<Breakpoint> getBreakpoints() {
-        return new ArrayList<>(breakpoints.keySet());
-    }
-
-    synchronized int getBreakpointID(Breakpoint breakpoint) {
-        final Integer id = breakpoints.get(breakpoint);
-        return id == null ? -1 : id;
+    Collection<BreakpointInfo> getBreakpoints() {
+        return new ArrayList<>(breakpoints.values());
     }
 
-    void clearBreakpoint(Breakpoint breakpoint) {
-        breakpoint.dispose();
-        breakpoints.remove(breakpoint);
-    }
+    final class BreakpointInfo {
+
+        private final BreakpointKind kind;
+
+        /** Null before created in debugger or after disposal. */
+        private Breakpoint breakpoint;
 
-    /**
-     * The intention to create a line breakpoint.
-     */
-    private final class PendingLineBreakpoint extends LineBreakpoint {
+        /** Non-null only when breakpoint == null. */
+        private State state; // non-null iff haven't "activated" yet
+
+        private final int uid;
+
+        private boolean oneShot;
+
+        private int ignoreCount;
 
         private Source conditionSource;
 
-        PendingLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
-            super(Breakpoint.State.ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot);
+        private final LineLocation lineLocation;
+
+        private final SyntaxTag tag;
+
+        private BreakpointInfo(Debugger debugger, LineLocation lineLocation, int ignoreCount, boolean oneShot) {
+            this.kind = BreakpointKind.LINE;
+            this.lineLocation = lineLocation;
+            this.tag = null;
+            this.ignoreCount = ignoreCount;
+            this.oneShot = oneShot;
+            this.uid = nextBreakpointUID++;
+            if (debugger == null) {
+                this.state = State.ENABLED_UNRESOLVED;
+            } else {
+                activate(debugger);
+            }
+            breakpoints.put(uid, this);
         }
 
-        @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");
+        private BreakpointInfo(Debugger debugger, SyntaxTag tag, int ignoreCount, boolean oneShot) {
+            this.kind = BreakpointKind.TAG;
+            this.lineLocation = null;
+            this.tag = tag;
+            this.ignoreCount = ignoreCount;
+            this.oneShot = oneShot;
+            this.uid = nextBreakpointUID++;
+            if (debugger == null) {
+                this.state = State.ENABLED_UNRESOLVED;
+            } else {
+                activate(debugger);
+            }
+            breakpoints.put(uid, this);
+        }
+
+        private void activate(Debugger debugger) {
+            if (breakpoint != null) {
+                throw new IllegalStateException("Breakpoint already activated");
+            }
+            if (state == State.DISPOSED) {
+                throw new IllegalStateException("Breakpoint already disposed");
+            }
+            try {
+                switch (kind) {
+                    case LINE:
+                        breakpoint = debugger.setLineBreakpoint(ignoreCount, lineLocation, oneShot);
+                        break;
+                    case TAG:
+                        breakpoint = debugger.setTagBreakpoint(ignoreCount, tag, oneShot);
+                        break;
+                    default:
+                        throw new IllegalStateException("Unexpected breakpoint kind");
+                }
+                if (conditionSource != null) {
+                    breakpoint.setCondition(conditionSource.getCode());
+                    conditionSource = null;
+                }
+                if (state == State.DISABLED_UNRESOLVED) {
+                    breakpoint.setEnabled(false);
+                }
+                state = null;
+            } catch (IOException ex) {
+                throw new IllegalStateException("Failure to activate breakpoint " + uid + ":  " + ex.getMessage());
             }
         }
 
-        @Override
-        public boolean isEnabled() {
-            return getState() == State.ENABLED_UNRESOLVED;
+        int getID() {
+            return uid;
         }
 
-        @Override
-        public void setCondition(String expr) throws IOException {
-
-            this.conditionSource = expr == null ? null : Source.fromText(expr, "breakpoint condition from text: " + expr);
-        }
-
-        @Override
-        public Source getCondition() {
-            return conditionSource;
+        String describeState() {
+            return (breakpoint == null ? state : breakpoint.getState()).getName();
         }
 
-        @Override
-        public void dispose() {
-            if (getState() == State.DISPOSED) {
-                throw new IllegalStateException("Breakpoint already disposed");
+        String describeLocation() {
+            if (breakpoint == null) {
+                switch (kind) {
+                    case LINE:
+                        return "Line: " + lineLocation.getShortDescription();
+                    case TAG:
+                        return "Tag " + tag.name();
+                    default:
+                        throw new IllegalStateException("Unexpected breakpoint state");
+                }
             }
-            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);
+            return breakpoint.getLocationDescription();
         }
 
-        @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");
+        void setEnabled(boolean enabled) {
+            if (breakpoint == null) {
+                switch (state) {
+                    case ENABLED_UNRESOLVED:
+                        if (!enabled) {
+                            state = State.DISABLED_UNRESOLVED;
+                        }
+                        break;
+                    case DISABLED_UNRESOLVED:
+                        if (enabled) {
+                            state = State.ENABLED_UNRESOLVED;
+                        }
+                        break;
+                    case DISPOSED:
+                        throw new IllegalStateException("Disposed breakpoints must stay disposed");
+                    default:
+                        throw new IllegalStateException("Unexpected breakpoint state");
+                }
+            } else {
+                breakpoint.setEnabled(enabled);
             }
         }
 
-        @Override
-        public boolean isEnabled() {
-            return getState() == State.ENABLED_UNRESOLVED;
+        boolean isEnabled() {
+            return breakpoint == null ? (state == State.ENABLED_UNRESOLVED) : breakpoint.isEnabled();
         }
 
-        @Override
-        public void setCondition(String expr) throws IOException {
-            this.conditionSource = Source.fromText(expr, "breakpoint condition from text: " + expr);
+        void setCondition(String expr) throws IOException {
+            if (breakpoint == null) {
+                conditionSource = expr == null ? null : Source.fromText(expr, "breakpoint condition from text: " + expr);
+            } else {
+                breakpoint.setCondition(expr);
+            }
+        }
+
+        String getCondition() {
+            final Source source = breakpoint == null ? conditionSource : breakpoint.getCondition();
+            return source == null ? null : source.getCode();
         }
 
-        @Override
-        public Source getCondition() {
-            return conditionSource;
+        int getIgnoreCount() {
+            return breakpoint == null ? ignoreCount : breakpoint.getIgnoreCount();
+        }
+
+        int getHitCount() {
+            return breakpoint == null ? 0 : breakpoint.getHitCount();
         }
 
-        @Override
-        public void dispose() {
-            if (getState() == State.DISPOSED) {
-                throw new IllegalStateException("Breakpoint already disposed");
+        void dispose() {
+            if (breakpoint == null) {
+                if (state == State.DISPOSED) {
+                    throw new IllegalStateException("Breakpoint already disposed");
+                }
+            } else {
+                breakpoint.dispose();
+                breakpoint = null;
             }
-            setState(State.DISPOSED);
-            breakpoints.remove(this);
+            state = State.DISPOSED;
+            breakpoints.remove(uid);
+            conditionSource = null;
         }
     }
 }