changeset 22468:64658afaf9c0

Truffle/REPL debugger: add "loads" and "evals" commands that "step into"; default is now not to step into
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sat, 21 Nov 2015 17:53:53 -0800
parents 5573f12b94f8
children ff91249da77d
files truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/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/REPLHandler.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java
diffstat 5 files changed, 154 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java	Wed Nov 18 20:38:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java	Sat Nov 21 17:53:53 2015 -0800
@@ -120,6 +120,7 @@
     public static final String SUBTREE = "subtree";
     public static final String SUCCEEDED = "succeeded";
     public static final String TOPIC = "topic";
+    public static final String TRUE = "true";
     public static final String TRUFFLE = "truffle";
     public static final String TRUFFLE_AST = "truffle-ast";
     public static final String TRUFFLE_NODE = "truffle-node";
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java	Wed Nov 18 20:38:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java	Sat Nov 21 17:53:53 2015 -0800
@@ -229,6 +229,17 @@
             }
             return request;
         }
+
+        @Override
+        void processReply(REPLClientContext context, REPLMessage[] replies) {
+            REPLMessage firstReply = replies[0];
+            if (firstReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
+                final String result = firstReply.get(REPLMessage.DISPLAY_MSG);
+                context.displayFailReply(result != null ? result : firstReply.toString());
+            } else {
+                context.displayReply(firstReply.get(REPLMessage.VALUE));
+            }
+        }
     };
 
     public static final REPLRemoteCommand CLEAR_BREAK_CMD = new REPLRemoteCommand("clear", null, "Clear a breakpoint") {
@@ -467,6 +478,59 @@
         }
     };
 
+    public static final REPLRemoteCommand EVAL_CMD = new REPLRemoteCommand("eval", null, "Evaluate a string, in context of the current frame if any") {
+
+        private int evalCounter = 0;
+
+        private final String[] help = {"eval <string>: evaluate <string> in context of the current frame if any"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
+        @Override
+        public REPLMessage createRequest(REPLClientContext context, String[] args) {
+            if (args.length > 1) {
+                final String code = args[1];
+                if (!code.isEmpty()) {
+                    // Create a fake entry in the file maps and cache, based on this unique name
+                    final String fakeFileName = "<eval" + ++evalCounter + ">";
+                    Source.fromNamedText(fakeFileName, code);
+                    final REPLMessage request = new REPLMessage();
+                    request.put(REPLMessage.OP, REPLMessage.EVAL);
+                    request.put(REPLMessage.CODE, code);
+                    request.put(REPLMessage.SOURCE_NAME, fakeFileName);
+                    if (context.level() > 0) {
+                        // Specify a requested execution context, if one exists; otherwise top level
+                        request.put(REPLMessage.FRAME_NUMBER, Integer.toString(context.getSelectedFrameNumber()));
+                    }
+                    return request;
+                }
+            }
+            return null;
+        }
+    };
+
+    public static final REPLRemoteCommand EVAL_STEP_INTO_CMD = new REPLRemoteCommand("eval-step-into", "evals", "Evaluate and step into a string, in context of the current frame if any") {
+
+        private final String[] help = {"eval-step-into <string>: evaluate <string> in context of the current frame if any, and step into"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
+        @Override
+        public REPLMessage createRequest(REPLClientContext context, String[] args) {
+            final REPLMessage request = EVAL_CMD.createRequest(context, args);
+            if (request != null) {
+                request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE);
+            }
+            return request;
+        }
+    };
+
     public static final REPLRemoteCommand FRAME_CMD = new REPLRemoteCommand("frame", null, "Display a stack frame") {
 
         private final String[] help = {"frame : display currently selected frame", "frame <n> : display frame <n>"};
@@ -545,6 +609,13 @@
 
     public static final REPLRemoteCommand LOAD_CMD = new REPLRemoteCommand("load", null, "Load source") {
 
+        private final String[] help = new String[]{"load : load currently selected file source", "load <file name> : load file <file name>"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
         @Override
         public REPLMessage createRequest(REPLClientContext context, String[] args) {
             Source runSource = null;
@@ -569,8 +640,34 @@
         }
     };
 
+    public static final REPLRemoteCommand LOAD_STEP_INTO_CMD = new REPLRemoteCommand("load-step-into", "loads", "Load source and step in") {
+
+        private final String[] help = new String[]{"load : load currently selected file source and step in", "load <file name> : load file <file name> and step in"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
+        @Override
+        public REPLMessage createRequest(REPLClientContext context, String[] args) {
+            final REPLMessage request = LOAD_CMD.createRequest(context, args);
+            if (request != null) {
+                request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE);
+            }
+            return request;
+        }
+    };
+
     public static final REPLRemoteCommand SET_LANG_CMD = new REPLRemoteCommand("language", "lang", "Set current language") {
 
+        private final String[] help = new String[]{"lang <language short name>:  set default language, \"info lang\" displays choices"};
+
+        @Override
+        public String[] getHelp() {
+            return help;
+        }
+
         @Override
         public REPLMessage createRequest(REPLClientContext context, String[] args) {
             if (args.length == 1) {
@@ -593,9 +690,11 @@
 
     public static final REPLRemoteCommand STEP_INTO_CMD = new REPLRemoteCommand("step", "s", "(StepInto) next statement, going into functions.") {
 
+        private final String[] help = new String[]{"step into:  step to next statement (into calls)", "step <n>: step to nth next statement (into calls)"};
+
         @Override
         public String[] getHelp() {
-            return new String[]{"step into:  step to next statement (into calls)", "step <n>: step to nth next statement (into calls)"};
+            return help;
         }
 
         @Override
@@ -654,9 +753,12 @@
 
     public static final REPLRemoteCommand STEP_OVER_CMD = new REPLRemoteCommand("next", "n", "(StepOver) execute next line of code, not into functions.") {
 
+        private final String[] help = new String[]{"next:  (StepOver) execute next line of code, not into functions.",
+                        "next <n>: (StepOver) execute to nth next statement (not counting into functions)"};
+
         @Override
         public String[] getHelp() {
-            return new String[]{"next:  (StepOver) execute next line of code, not into functions.", "next <n>: (StepOver) execute to nth next statement (not counting into functions)"};
+            return help;
         }
 
         @Override
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java	Wed Nov 18 20:38:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java	Sat Nov 21 17:53:53 2015 -0800
@@ -165,7 +165,8 @@
         addCommand(REPLRemoteCommand.DISABLE_CMD);
         addCommand(REPLRemoteCommand.DOWN_CMD);
         addCommand(REPLRemoteCommand.ENABLE_CMD);
-        addCommand(evalCommand);
+        addCommand(REPLRemoteCommand.EVAL_CMD);
+        addCommand(REPLRemoteCommand.EVAL_STEP_INTO_CMD);
         addCommand(fileCommand);
         addCommand(REPLRemoteCommand.FRAME_CMD);
         addCommand(helpCommand);
@@ -173,6 +174,7 @@
         addCommand(REPLRemoteCommand.KILL_CMD);
         addCommand(listCommand);
         addCommand(REPLRemoteCommand.LOAD_CMD);
+        addCommand(REPLRemoteCommand.LOAD_STEP_INTO_CMD);
         addCommand(quitCommand);
         addCommand(setCommand);
         addCommand(REPLRemoteCommand.SET_LANG_CMD);
@@ -723,33 +725,6 @@
         }
     };
 
-    private final REPLCommand evalCommand = new REPLRemoteCommand("eval", null, "Evaluate a string, in context of the current frame if any") {
-
-        private int evalCounter = 0;
-
-        @Override
-        public REPLMessage createRequest(REPLClientContext context, String[] args) {
-            if (args.length > 1) {
-                final String code = args[1];
-                if (!code.isEmpty()) {
-                    // Create a fake entry in the file maps and cache, based on this unique name
-                    final String fakeFileName = "<eval" + ++evalCounter + ">";
-                    Source.fromNamedText(fakeFileName, code);
-                    final REPLMessage request = new REPLMessage();
-                    request.put(REPLMessage.OP, REPLMessage.EVAL);
-                    request.put(REPLMessage.CODE, code);
-                    request.put(REPLMessage.SOURCE_NAME, fakeFileName);
-                    if (clientContext.level > 0) {
-                        // Specify a requested execution context, if one exists; otherwise top level
-                        request.put(REPLMessage.FRAME_NUMBER, Integer.toString(context.getSelectedFrameNumber()));
-                    }
-                    return request;
-                }
-            }
-            return null;
-        }
-    };
-
     private final REPLCommand fileCommand = new REPLRemoteCommand("file", null, "Set/display current file for viewing") {
 
         final String[] help = {"file:  display current file path", "file <filename>: Set file to be current file for viewing"};
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Wed Nov 18 20:38:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java	Sat Nov 21 17:53:53 2015 -0800
@@ -137,7 +137,7 @@
             if (section == null) {
                 section = node.getEncapsulatingSourceSection();
             }
-            if (section != null) {
+            if (section != null && section.getSource() != null) {
                 infoMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath());
                 infoMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine()));
                 infoMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine()));
@@ -291,7 +291,7 @@
             if (callName == null) {
                 return finishReplyFailed(reply, "no name specified");
             }
-            final ArrayList<Object> argList = new ArrayList<>();
+            final ArrayList<String> argList = new ArrayList<>();
             for (int argCount = 0; argCount < REPLMessage.ARG_NAMES.length; argCount++) {
                 final String arg = request.get(REPLMessage.ARG_NAMES[argCount]);
                 if (arg == null) {
@@ -299,9 +299,9 @@
                 }
                 argList.add(arg);
             }
-            final Object[] args = argList.toArray(new Object[0]);
             try {
-                replServer.call(callName, args);
+                final Object result = replServer.call(callName, argList);
+                reply.put(REPLMessage.VALUE, result == null ? "<void>" : result.toString());
             } catch (QuitException ex) {
                 throw ex;
             } catch (KillException ex) {
@@ -407,8 +407,9 @@
             final String source = request.get(REPLMessage.CODE);
             final Visualizer visualizer = replServer.getVisualizer();
             final Integer frameNumber = request.getIntValue(REPLMessage.FRAME_NUMBER);
+            final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO));
             try {
-                Object returnValue = serverContext.eval(source, frameNumber);
+                Object returnValue = serverContext.eval(source, frameNumber, stepInto);
                 return finishReplySucceeded(reply, visualizer.displayValue(returnValue, 0));
             } catch (QuitException ex) {
                 throw ex;
@@ -549,11 +550,10 @@
         public REPLMessage[] receive(REPLMessage request, REPLServer replServer) {
             final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_SOURCE);
             final String fileName = request.get(REPLMessage.SOURCE_NAME);
+            final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO));
             try {
                 final Source fileSource = Source.fromFileName(fileName);
-                // TODO (mlvdv) language-specific
-                assert replServer.getLanguage().getMimeTypes().contains(fileSource.getMimeType());
-                replServer.eval(fileSource);
+                replServer.getCurrentContext().eval(fileSource, stepInto);
                 reply.put(REPLMessage.FILE_PATH, fileName);
                 return finishReplySucceeded(reply, fileName + "  loaded");
             } catch (QuitException ex) {
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Wed Nov 18 20:38:32 2015 -0800
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java	Sat Nov 21 17:53:53 2015 -0800
@@ -159,7 +159,7 @@
                     }
                 }
             }
-            if (!currentServerContext.isEval) {
+            if (currentServerContext.steppingInto) {
                 event.prepareStepInto();
             }
         }
@@ -289,7 +289,7 @@
         private final int level;
         private final SuspendedEvent event;
         private Language currentLanguage;
-        private boolean isEval = false;  // When true, run without StepInto
+        private boolean steppingInto = false;  // Only true during a "stepInto" engine call
 
         Context(Context predecessor, SuspendedEvent event, Language language) {
             assert language != null;
@@ -313,6 +313,15 @@
             return event.getNode();
         }
 
+        void eval(Source source, boolean stepInto) throws IOException {
+            this.steppingInto = stepInto;
+            try {
+                engine.eval(source);
+            } finally {
+                this.steppingInto = false;
+            }
+        }
+
         /**
          * Evaluates a code snippet in the context of a selected frame in the currently suspended
          * execution.
@@ -323,18 +332,26 @@
          * @return result of the evaluation
          * @throws IOException if something goes wrong
          */
-        Object eval(String code, Integer frameNumber) throws IOException {
+        Object eval(String code, Integer frameNumber, boolean stepInto) throws IOException {
             if (event == null) {
+                this.steppingInto = stepInto;
+                final String mimeType = defaultMIME(currentLanguage);
                 try {
-                    isEval = true;
-                    final String mimeType = defaultMIME(currentLanguage);
-                    final Value value = engine.eval(Source.fromText(code, "eval(\"" + code + "\")").withMimeType(mimeType));
-                    return value.get();
+                    return engine.eval(Source.fromText(code, "eval(\"" + code + "\")").withMimeType(mimeType)).get();
                 } finally {
-                    isEval = false;
+                    this.steppingInto = false;
+                }
+            } else {
+                if (stepInto) {
+                    event.prepareStepInto(1);
+                }
+                try {
+                    final Object result = event.eval(code, frameNumber);
+                    return (result instanceof Value) ? ((Value) result).get() : result;
+                } finally {
+                    event.prepareContinue();
                 }
             }
-            return event.eval(code, frameNumber);
         }
 
         /**
@@ -462,10 +479,6 @@
         return language.getMimeTypes().iterator().next();
     }
 
-    void eval(Source source) throws IOException {
-        engine.eval(source);
-    }
-
     Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
         Breakpoint breakpoint;
         if (db == null) {
@@ -518,12 +531,22 @@
         breakpoints.remove(breakpoint);
     }
 
-    void call(String name, Object... args) throws IOException {
+    Object call(String name, List<String> argList) throws IOException {
         Value symbol = engine.findGlobalSymbol(name);
         if (symbol == null) {
             throw new IOException("symbol \"" + name + "\" not found");
         }
-        symbol.invoke(null, args);
+        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();
     }
 
     /**