changeset 22471:a6c162686d81

Truffle/Language&Debugger: reimplement completely how the execution stack is gathered and communicated; change the signature of TruffelLanguage.evalInContext() to comply.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sun, 06 Dec 2015 20:13:49 -0800
parents 64d5619ebbce
children 1ea1e410ed3d 92e99d8e236c
files truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.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/FrameDebugDescription.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.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 10 files changed, 283 insertions(+), 195 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Sun Dec 06 20:13:49 2015 -0800
@@ -33,12 +33,11 @@
 import java.lang.annotation.Target;
 import java.util.Collections;
 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;
-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.impl.Accessor;
 import com.oracle.truffle.api.impl.FindContextNode;
@@ -53,8 +52,6 @@
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
 
-import java.util.Objects;
-
 /**
  * An entry point for everyone who wants to implement a Truffle based language. By providing an
  * implementation of this type and registering it using {@link Registration} annotation, your
@@ -395,7 +392,7 @@
          * . The names of arguments are parameters for the resulting {#link CallTarget} that allow
          * the <code>source</code> to reference the actual parameters passed to
          * {@link CallTarget#call(java.lang.Object...)}.
-         * 
+         *
          * @param source the source to evaluate
          * @param argumentNames the names of {@link CallTarget#call(java.lang.Object...)} arguments
          *            that can be referenced from the source
@@ -482,20 +479,13 @@
         }
 
         @Override
-        protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException {
-            Node n = ev.getNode();
-            if (n == null && frame != null) {
-                n = frame.getCallNode();
-            }
-            if (n == null) {
-                throw new IOException("Can't determine language for text \"" + code + "\"");
-            }
-            RootNode rootNode = n.getRootNode();
+        protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException {
+            RootNode rootNode = node.getRootNode();
             Class<? extends TruffleLanguage> languageType = findLanguage(rootNode);
             final Env env = findLanguage(vm, languageType);
             final TruffleLanguage<?> lang = findLanguage(env);
             final Source source = Source.fromText(code, "eval in context");
-            return lang.evalInContext(source, n, frame.getFrame(FrameAccess.READ_ONLY, true).materialize());
+            return lang.evalInContext(source, node, frame);
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Sun Dec 06 20:13:49 2015 -0800
@@ -29,6 +29,7 @@
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -50,6 +51,7 @@
 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;
 
 /**
  * Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}.
@@ -678,6 +680,9 @@
          */
         private MaterializedFrame haltedFrame;
 
+        /** Subset of the Truffle stack corresponding to the current execution. */
+        private List<FrameDebugDescription> contextStack;
+
         private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) {
             this(executionSource, previousContext, -1);
         }
@@ -748,7 +753,56 @@
             // Clean up, just in cased the one-shot breakpoints got confused
             lineBreaks.disposeOneShots();
 
-            final int contextStackDepth = currentStackDepth() - contextStackBase;
+            // Includes the "caller" frame (not iterated)
+            final int contextStackDepth = (currentStackDepth() - contextStackBase) + 1;
+
+            final List<String> recentWarnings = new ArrayList<>(warnings);
+            warnings.clear();
+
+            final List<FrameDebugDescription> 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));
+            Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() {
+                int stackIndex = 1;
+                int frameIndex = 1;
+
+                @Override
+                public FrameInstance visitFrame(FrameInstance frameInstance) {
+                    if (stackIndex < contextStackDepth) {
+                        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));
+                                frameIndex++;
+                            } else if (TRACE) {
+                                if (callNode != null) {
+                                    contextTrace("HIDDEN frame added: " + callNode);
+                                } else {
+                                    contextTrace("HIDDEN frame added");
+                                }
+                                frames.add(new FrameDebugDescription(frameIndex, frameInstance));
+                                frameIndex++;
+                            }
+                        } else if (TRACE) {
+                            if (callNode != null) {
+                                contextTrace("HIDDEN frame added: " + callNode);
+                            } else {
+                                contextTrace("HIDDEN frame added");
+                            }
+                            frames.add(new FrameDebugDescription(frameIndex, frameInstance));
+                            frameIndex++;
+                        }
+                        stackIndex++;
+                        return null;
+                    }
+                    return frameInstance;
+                }
+            });
+            contextStack = Collections.unmodifiableList(frames);
+
             if (TRACE) {
                 final String reason = haltReason == null ? "" : haltReason + "";
                 final String where = before ? "BEFORE" : "AFTER";
@@ -757,12 +811,9 @@
                 // printStack(OUT);
             }
 
-            final List<String> recentWarnings = new ArrayList<>(warnings);
-            warnings.clear();
-
             try {
                 // Pass control to the debug client with current execution suspended
-                ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, astNode, mFrame, recentWarnings, contextStackDepth));
+                ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, contextStack, recentWarnings));
                 // Debug client finished normally, execution resumes
                 // Presume that the client has set a new strategy (or default to Continue)
                 running = true;
@@ -840,8 +891,8 @@
         debugContext = debugContext.predecessor;
     }
 
-    Object evalInContext(SuspendedEvent ev, String code, FrameInstance frame) throws IOException {
-        return ACCESSOR.evalInContext(vm, ev, code, frame);
+    Object evalInContext(SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException {
+        return ACCESSOR.evalInContext(vm, ev, code, node, frame);
     }
 
     @SuppressWarnings("rawtypes")
@@ -874,8 +925,8 @@
         }
 
         @Override
-        protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException {
-            return super.evalInContext(vm, ev, code, frame);
+        protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException {
+            return super.evalInContext(vm, ev, code, node, frame);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/FrameDebugDescription.java	Sun Dec 06 20:13:49 2015 -0800
@@ -0,0 +1,69 @@
+/*
+ * 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.api.debug;
+
+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.nodes.Node;
+
+/**
+ * Summary of information about a frame for use by debugging clients.
+ */
+public final class FrameDebugDescription {
+    private final int index;
+    private final Node node;
+    private final MaterializedFrame frame;
+
+    FrameDebugDescription(int index, Node node, MaterializedFrame frame) {
+        this.index = index;
+        this.node = node;
+        this.frame = frame;
+    }
+
+    FrameDebugDescription(int index, FrameInstance frameInstance) {
+        this.index = index;
+        this.node = frameInstance.getCallNode();
+        this.frame = frameInstance.getFrame(FrameAccess.MATERIALIZE, true).materialize();
+    }
+
+    /**
+     * Position in the current stack: {@code 0} at the top.
+     */
+    public int index() {
+        return index;
+    }
+
+    /**
+     * AST location of the call that created the frame.
+     */
+    public Node node() {
+        return node;
+    }
+
+    public MaterializedFrame frame() {
+        return frame;
+    }
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java	Sun Dec 06 20:13:49 2015 -0800
@@ -25,14 +25,11 @@
 package com.oracle.truffle.api.debug;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.io.PrintStream;
 import java.util.Collections;
 import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.frame.FrameInstance;
-import com.oracle.truffle.api.frame.FrameInstanceVisitor;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.nodes.Node;
@@ -49,37 +46,28 @@
  */
 @SuppressWarnings("javadoc")
 public final class SuspendedEvent {
-    private final List<String> recentWarnings;
-    private final MaterializedFrame mFrame;
-    private final Node astNode;
-    private final List<FrameInstance> frames;
-    private final int stackSize;
-    private final Debugger debugger;
+
+    private static boolean TRACE = false;
+    private static final String TRACE_PREFIX = "EVENT: ";
+    private static final PrintStream OUT = System.out;
 
-    SuspendedEvent(Debugger prepares, Node astNode, MaterializedFrame mFrame, List<String> recentWarnings, final int stackDepth) {
-        this.debugger = prepares;
-        this.astNode = astNode;
-        this.mFrame = mFrame;
-        this.recentWarnings = recentWarnings;
+    private static void trace(String format, Object... args) {
+        if (TRACE) {
+            OUT.println(TRACE_PREFIX + String.format(format, args));
+        }
+    }
 
-        this.frames = new ArrayList<>();
-        // Map the Truffle stack for this execution, ignore nested executions
-        // The top (current) frame is not produced by the iterator.
-        frames.add(Truffle.getRuntime().getCurrentFrame());
-        Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() {
-            int frameCount = 1;
+    private final Debugger debugger;
+    private final List<FrameDebugDescription> stack;
+    private final List<String> warnings;
 
-            @Override
-            public FrameInstance visitFrame(FrameInstance frameInstance) {
-                if (frameCount < stackDepth) {
-                    frames.add(frameInstance);
-                    frameCount++;
-                    return null;
-                }
-                return frameInstance;
-            }
-        });
-        stackSize = frames.size();
+    SuspendedEvent(Debugger debugger, List<FrameDebugDescription> stack, List<String> warnings) {
+        this.debugger = debugger;
+        this.stack = stack;
+        this.warnings = warnings;
+        if (TRACE) {
+            trace("Execution suspended at Node=" + stack.get(0).node());
+        }
     }
 
     /**
@@ -95,15 +83,15 @@
     }
 
     public Node getNode() {
-        return astNode;
+        return stack.get(0).node();
     }
 
     public MaterializedFrame getFrame() {
-        return mFrame;
+        return stack.get(0).frame();
     }
 
     public List<String> getRecentWarnings() {
-        return Collections.unmodifiableList(recentWarnings);
+        return Collections.unmodifiableList(warnings);
     }
 
     /**
@@ -113,8 +101,8 @@
      * @return list of stack frames
      */
     @CompilerDirectives.TruffleBoundary
-    public List<FrameInstance> getStack() {
-        return Collections.unmodifiableList(frames);
+    public List<FrameDebugDescription> getStack() {
+        return stack;
     }
 
     /**
@@ -209,11 +197,11 @@
     public Object eval(String code, Integer frameNumber) throws IOException {
         int n = 0;
         if (frameNumber != null) {
-            if (frameNumber < 0 || frameNumber >= stackSize) {
+            if (frameNumber < 0 || frameNumber >= stack.size()) {
                 throw new IOException("invalid frame number");
             }
             n = frameNumber;
         }
-        return debugger.evalInContext(this, code, frames.get(n));
+        return debugger.evalInContext(this, code, stack.get(n).node(), stack.get(n).frame());
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Sun Dec 06 20:13:49 2015 -0800
@@ -39,7 +39,6 @@
 import com.oracle.truffle.api.TruffleLanguage.Env;
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.SuspendedEvent;
-import com.oracle.truffle.api.frame.FrameInstance;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.Instrumenter;
@@ -157,8 +156,8 @@
         return API.eval(l, s);
     }
 
-    protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException {
-        return API.evalInContext(vm, ev, code, frame);
+    protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException {
+        return API.evalInContext(vm, ev, code, node, frame);
     }
 
     protected Object importSymbol(Object vm, TruffleLanguage<?> queryingLang, String globalName) {
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java	Sun Dec 06 20:13:49 2015 -0800
@@ -203,7 +203,7 @@
 
     public static final REPLRemoteCommand CALL_CMD = new REPLRemoteCommand("call", null, "call a method/function") {
 
-        private final String[] help = {"call <name>: calls a function by name, arguments not yet supported"};
+        private final String[] help = {"call <name> <args>: calls a function by name"};
 
         @Override
         public String[] getHelp() {
@@ -242,6 +242,30 @@
         }
     };
 
+    public static final REPLRemoteCommand CALL_STEP_INTO_CMD = new REPLRemoteCommand("call-step-into", "calls", "Call a method/function and step into") {
+
+        private final String[] help = {"call <name> <args>: calls function by name and step into"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
+        @Override
+        public REPLMessage createRequest(REPLClientContext context, String[] args) {
+            final REPLMessage request = CALL_CMD.createRequest(context, args);
+            if (request != null) {
+                request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE);
+            }
+            return request;
+        }
+
+        @Override
+        void processReply(REPLClientContext context, REPLMessage[] replies) {
+            CALL_CMD.processReply(context, replies);
+        }
+    };
+
     public static final REPLRemoteCommand CLEAR_BREAK_CMD = new REPLRemoteCommand("clear", null, "Clear a breakpoint") {
 
         private final String[] help = {"clear <n>: clear breakpoint number <n>"};
@@ -670,13 +694,12 @@
 
         @Override
         public REPLMessage createRequest(REPLClientContext context, String[] args) {
-            if (args.length == 1) {
-                context.displayFailReply("no language specified");
-                return null;
-            }
+
             final REPLMessage request = new REPLMessage();
             request.put(REPLMessage.OP, REPLMessage.SET_LANGUAGE);
-            request.put(REPLMessage.LANG_NAME, args[1]);
+            if (args.length > 1) {
+                request.put(REPLMessage.LANG_NAME, args[1]);
+            }
             return request;
         }
 
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java	Sun Dec 06 20:13:49 2015 -0800
@@ -94,8 +94,8 @@
     static final String CODE_LINE_FORMAT = "    %3d  %s\n";
     static final String CODE_LINE_BREAK_FORMAT = "--> %3d  %s\n";
 
-    private static final String STACK_FRAME_FORMAT = "    %3d: at %s in %s    line =\"%s\"\n";
-    private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s    line =\"%s\"\n";
+    private static final String STACK_FRAME_FORMAT = "    %3d: at %s in %s    %s\n";
+    private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s    %s\n";
 
     // Top level commands
     private final Map<String, REPLCommand> commandMap = new HashMap<>();
@@ -158,6 +158,7 @@
         addCommand(REPLRemoteCommand.BREAK_AT_THROW_CMD);
         addCommand(REPLRemoteCommand.BREAK_AT_THROW_ONCE_CMD);
         addCommand(REPLRemoteCommand.CALL_CMD);
+        addCommand(REPLRemoteCommand.CALL_STEP_INTO_CMD);
         addCommand(REPLRemoteCommand.CLEAR_BREAK_CMD);
         addCommand(REPLRemoteCommand.CONDITION_BREAK_CMD);
         addCommand(REPLRemoteCommand.CONTINUE_CMD);
@@ -309,12 +310,13 @@
             if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.SUCCEEDED)) {
                 languageName = replies[0].get(REPLMessage.LANG_NAME);
             }
+            final String showLang = languageName == null ? "() " : "( " + languageName + " )";
             if (level == 0) {
                 // 0-level context; no executions halted.
                 if (selectedSource == null) {
-                    currentPrompt = languageName == null ? "() " : "( " + languageName + " ) ";
+                    currentPrompt = showLang + " ";
                 } else {
-                    currentPrompt = "(" + selectedSource.getShortName() + ") ";
+                    currentPrompt = "(" + selectedSource.getShortName() + ") " + showLang + " ";
                 }
             } else if (selectedSource != null && selectedSource != haltedSource) {
                 // User is focusing somewhere else than the current locn; show no line number.
@@ -322,7 +324,7 @@
                 sb.append("(<" + Integer.toString(level) + "> ");
                 sb.append(selectedSource.getShortName());
                 sb.append(")");
-                sb.append("(" + languageName + ")");
+                sb.append(showLang);
                 sb.append(" ");
                 currentPrompt = sb.toString();
             } else {
@@ -334,7 +336,7 @@
                     sb.append(":" + Integer.toString(haltedLineNumber));
                 }
                 sb.append(")");
-                sb.append("(" + languageName + ")");
+                sb.append(showLang);
                 sb.append(" ");
                 currentPrompt = sb.toString();
             }
@@ -466,7 +468,9 @@
                 for (REPLFrame frame : frameList) {
                     String sourceLineText = frame.sourceLineText();
                     if (sourceLineText == null) {
-                        sourceLineText = "<??>";
+                        sourceLineText = "";
+                    } else {
+                        sourceLineText = "line=\"" + sourceLineText + "\"";
                     }
                     if (frame.index() == selectedFrameNumber) {
                         writer.format(STACK_FRAME_SELECTED_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText);
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.java	Sun Dec 06 20:12:43 2015 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.tools.debug.shell.server;
-
-import com.oracle.truffle.api.frame.Frame;
-import com.oracle.truffle.api.frame.FrameInstance;
-import com.oracle.truffle.api.nodes.Node;
-
-public final class FrameDebugDescription {
-    private final int index;
-    private final Node node;
-    private final FrameInstance frameInstance;
-
-    FrameDebugDescription(int index, Node node, FrameInstance frameInstance) {
-        this.index = index;
-        this.node = node;
-        this.frameInstance = frameInstance;
-    }
-
-    /**
-     * Position in the current stack: {@code 0} at the top.
-     */
-    public int index() {
-        return index;
-    }
-
-    /**
-     * AST location.
-     */
-    public Node node() {
-        return node;
-    }
-
-    /**
-     * Access to the Truffle {@link Frame}.
-     */
-    public FrameInstance frameInstance() {
-        return frameInstance;
-    }
-}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Sun Dec 06 20:13:49 2015 -0800
@@ -30,9 +30,8 @@
 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.FrameDescriptor;
-import com.oracle.truffle.api.frame.FrameInstance;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.instrument.ASTPrinter;
 import com.oracle.truffle.api.instrument.KillException;
@@ -123,12 +122,10 @@
         return infoMessage;
     }
 
-    protected static final REPLMessage createFrameInfoMessage(final REPLServer replServer, FrameDebugDescription frame) {
+    protected static final REPLMessage createFrameInfoMessage(final REPLServer replServer, int number, Node node) {
         final Visualizer visualizer = replServer.getVisualizer();
         final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.FRAME_INFO);
-        infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(frame.index()));
-        final Node node = frame.node();
-
+        infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(number));
         infoMessage.put(REPLMessage.SOURCE_LOCATION, visualizer.displaySourceLocation(node));
         infoMessage.put(REPLMessage.METHOD_NAME, visualizer.displayMethodName(node));
 
@@ -151,18 +148,40 @@
 
         @Override
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
-            final REPLMessage reply = createReply();
-            final ArrayList<REPLMessage> frameMessages = new ArrayList<>();
-            for (FrameDebugDescription frame : replServer.getCurrentContext().getStack()) {
-                frameMessages.add(createFrameInfoMessage(replServer, frame));
+            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));
             }
-            if (frameMessages.size() > 0) {
-                return frameMessages.toArray(new REPLMessage[0]);
+            if (replies.size() > 0) {
+                return replies.toArray(new REPLMessage[0]);
             }
-            return finishReplyFailed(reply, "No stack");
+            return finishReplyFailed(new REPLMessage(REPLMessage.OP, REPLMessage.BACKTRACE), "No stack");
         }
     };
 
+    private static REPLMessage btMessage(int index, Node node, Visualizer visualizer) {
+        final REPLMessage btMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BACKTRACE);
+        btMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(index));
+        if (node != null) {
+            btMessage.put(REPLMessage.SOURCE_LOCATION, visualizer.displaySourceLocation(node));
+            btMessage.put(REPLMessage.METHOD_NAME, visualizer.displayMethodName(node));
+            SourceSection section = node.getSourceSection();
+            if (section == null) {
+                section = node.getEncapsulatingSourceSection();
+            }
+            if (section != null && section.getSource() != null) {
+                btMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath());
+                btMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine()));
+                btMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine()));
+            }
+            btMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED);
+        }
+        return btMessage;
+    }
+
     public static final REPLHandler BREAK_AT_LINE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_LINE) {
 
         @Override
@@ -299,8 +318,9 @@
                 }
                 argList.add(arg);
             }
+            final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO));
             try {
-                final Object result = replServer.call(callName, argList);
+                final Object result = replServer.getCurrentContext().call(callName, stepInto, argList);
                 reply.put(REPLMessage.VALUE, result == null ? "<void>" : result.toString());
             } catch (QuitException ex) {
                 throw ex;
@@ -463,20 +483,21 @@
             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.frameInstance().getFrame(FrameInstance.FrameAccess.READ_ONLY, true);
-            final Visualizer visualizer = replServer.getVisualizer();
-            final FrameDescriptor frameDescriptor = frame.getFrameDescriptor();
-            final List<? extends FrameSlot> slots = frameDescriptor.getSlots();
+            final Frame frame = frameDescription.frame();
+            final Node node = frameDescription.node();
+            List<? extends FrameSlot> slots = frame.getFrameDescriptor().getSlots();
+
             if (slots.size() == 0) {
-                final REPLMessage emptyFrameMessage = createFrameInfoMessage(replServer, frameDescription);
+                final REPLMessage emptyFrameMessage = createFrameInfoMessage(replServer, frameNumber, node);
                 return finishReplySucceeded(emptyFrameMessage, "empty frame");
             }
             final ArrayList<REPLMessage> replies = new ArrayList<>();
 
             for (FrameSlot slot : slots) {
-                final REPLMessage slotMessage = createFrameInfoMessage(replServer, frameDescription);
+                final REPLMessage slotMessage = createFrameInfoMessage(replServer, frameNumber, node);
                 slotMessage.put(REPLMessage.SLOT_INDEX, Integer.toString(slot.getIndex()));
                 slotMessage.put(REPLMessage.SLOT_ID, visualizer.displayIdentifier(slot));
                 slotMessage.put(REPLMessage.SLOT_VALUE, visualizer.displayValue(frame.getValue(slot), 0));
@@ -580,7 +601,9 @@
             final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.SET_LANGUAGE);
             String languageName = request.get(REPLMessage.LANG_NAME);
             if (languageName == null) {
-                return finishReplyFailed(reply, "missing language name");
+                final String oldLanguageName = replServer.getCurrentContext().getLanguageName();
+                reply.put(REPLMessage.LANG_NAME, reply.put(REPLMessage.LANG_NAME, oldLanguageName));
+                return finishReplySucceeded(reply, "Language set to " + oldLanguageName);
             }
             reply.put(REPLMessage.LANG_NAME, languageName);
             try {
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Sun Dec 06 20:12:43 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Sun Dec 06 20:13:49 2015 -0800
@@ -27,7 +27,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
@@ -39,10 +38,10 @@
 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.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;
@@ -313,6 +312,29 @@
             return event.getNode();
         }
 
+        Object call(String name, boolean stepInto, List<String> argList) throws IOException {
+            Value symbol = engine.findGlobalSymbol(name);
+            if (symbol == null) {
+                throw new IOException("symbol \"" + name + "\" not found");
+            }
+            final List<Object> args = new ArrayList<>();
+            for (String stringArg : argList) {
+                Integer intArg = null;
+                try {
+                    intArg = Integer.valueOf(stringArg);
+                    args.add(intArg);
+                } catch (NumberFormatException e) {
+                    args.add(stringArg);
+                }
+            }
+            this.steppingInto = stepInto;
+            try {
+                return symbol.invoke(null, args.toArray(new Object[0])).get();
+            } finally {
+                this.steppingInto = false;
+            }
+        }
+
         void eval(Source source, boolean stepInto) throws IOException {
             this.steppingInto = stepInto;
             try {
@@ -385,17 +407,7 @@
          * @return immutable list of stack elements
          */
         List<FrameDebugDescription> getStack() {
-            List<FrameDebugDescription> frames = new ArrayList<>();
-            int frameCount = 0;
-            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);
+            return event.getStack();
         }
 
         public String getLanguageName() {
@@ -404,11 +416,20 @@
 
         /**
          * Case-insensitive; returns actual language name set.
+         *
+         * @throws IOException if fails
          */
-        String setLanguage(String name) {
+        String setLanguage(String name) throws IOException {
+            assert name != null;
             final Language language = nameToLanguage.get(name);
             if (language == null) {
-                return null;
+                throw new IOException("Language \" + name + \" not supported");
+            }
+            if (language == currentLanguage) {
+                return currentLanguage.getName();
+            }
+            if (event != null) {
+                throw new IOException("Only supported at top level");
             }
             this.currentLanguage = language;
             return language.getName();
@@ -531,24 +552,6 @@
         breakpoints.remove(breakpoint);
     }
 
-    Object call(String name, List<String> argList) throws IOException {
-        Value symbol = engine.findGlobalSymbol(name);
-        if (symbol == null) {
-            throw new IOException("symbol \"" + name + "\" not found");
-        }
-        final List<Object> args = new ArrayList<>();
-        for (String stringArg : argList) {
-            Integer intArg = null;
-            try {
-                intArg = Integer.valueOf(stringArg);
-                args.add(intArg);
-            } catch (NumberFormatException e) {
-                args.add(stringArg);
-            }
-        }
-        return symbol.invoke(null, args.toArray(new Object[0])).get();
-    }
-
     /**
      * The intention to create a line breakpoint.
      */