8150382: JShell API: Allow setting remote JVM arguments
authorrfield
Wed, 27 Apr 2016 18:13:19 -0700
changeset 37745 4b6b59f8e327
parent 37744 bf4fd5e022c5
child 37746 f1045bf4d24b
8150382: JShell API: Allow setting remote JVM arguments 8151754: jshell tool: add command line options for setting feedback mode Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties
langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java
langtools/test/jdk/jshell/StartOptionTest.java
langtools/test/jdk/jshell/ToolSimpleTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java	Wed Apr 27 18:13:19 2016 -0700
@@ -35,6 +35,7 @@
 class ArgTokenizer {
 
     private final String str;
+    private final String prefix;
     private final int length;
     private int next = 0;
     private char buf[] = new char[20];
@@ -49,7 +50,12 @@
     private boolean isQuoted = false;
 
     ArgTokenizer(String arg) {
+        this("", arg);
+    }
+
+    ArgTokenizer(String prefix, String arg) {
         this.str = arg;
+        this.prefix = prefix;
         this.length = arg.length();
         quoteChar('"');
         quoteChar('\'');
@@ -88,7 +94,7 @@
     }
 
     String whole() {
-        return str;
+        return prefix + str;
     }
 
     void mark() {
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java	Wed Apr 27 18:13:19 2016 -0700
@@ -95,20 +95,20 @@
         return mode.getContinuationPrompt(nextId);
     }
 
-    public boolean setFeedback(JShellTool tool, ArgTokenizer at) {
-        return new Setter(tool, at).setFeedback();
+    public boolean setFeedback(MessageHandler messageHandler, ArgTokenizer at) {
+        return new Setter(messageHandler, at).setFeedback();
     }
 
-    public boolean setFormat(JShellTool tool, ArgTokenizer at) {
-        return new Setter(tool, at).setFormat();
+    public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) {
+        return new Setter(messageHandler, at).setFormat();
     }
 
-    public boolean setNewMode(JShellTool tool, ArgTokenizer at) {
-        return new Setter(tool, at).setNewMode();
+    public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) {
+        return new Setter(messageHandler, at).setNewMode();
     }
 
-    public boolean setPrompt(JShellTool tool, ArgTokenizer at) {
-        return new Setter(tool, at).setPrompt();
+    public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) {
+        return new Setter(messageHandler, at).setPrompt();
     }
 
     {
@@ -529,26 +529,26 @@
     private class Setter {
 
         private final ArgTokenizer at;
-        private final JShellTool tool;
+        private final MessageHandler messageHandler;
         boolean valid = true;
 
-        Setter(JShellTool tool, ArgTokenizer at) {
-            this.tool = tool;
+        Setter(MessageHandler messageHandler, ArgTokenizer at) {
+            this.messageHandler = messageHandler;
             this.at = at;
         }
 
         void fluff(String format, Object... args) {
-            tool.fluff(format, args);
+            messageHandler.fluff(format, args);
         }
 
-        void fluffmsg(String format, Object... args) {
-            tool.fluffmsg(format, args);
+        void fluffmsg(String messageKey, Object... args) {
+            messageHandler.fluffmsg(messageKey, args);
         }
 
         void errorat(String messageKey, Object... args) {
             Object[] a2 = Arrays.copyOf(args, args.length + 2);
-            a2[args.length] = "/set " + at.whole();
-            tool.errormsg(messageKey, a2);
+            a2[args.length] = at.whole();
+            messageHandler.errormsg(messageKey, a2);
         }
 
         // For /set prompt <mode> "<prompt>" "<continuation-prompt>"
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Wed Apr 27 18:13:19 2016 -0700
@@ -108,7 +108,7 @@
  * Command line REPL tool for Java using the JShell API.
  * @author Robert Field
  */
-public class JShellTool {
+public class JShellTool implements MessageHandler {
 
     private static final String LINE_SEP = System.getProperty("line.separator");
     private static final Pattern LINEBREAK = Pattern.compile("\\R");
@@ -166,6 +166,8 @@
     private boolean regenerateOnDeath = true;
     private boolean live = false;
     private boolean feedbackInitialized = false;
+    private String initialMode = null;
+    private List<String> remoteVMOptions = new ArrayList<>();
 
     SourceCodeAnalysis analysis;
     JShell state = null;
@@ -256,7 +258,8 @@
      * @param format printf format
      * @param args printf args
      */
-    void fluff(String format, Object... args) {
+    @Override
+    public void fluff(String format, Object... args) {
         if (feedback.shouldDisplayCommandFluff() && interactive()) {
             hard(format, args);
         }
@@ -362,7 +365,8 @@
      * @param key the resource key
      * @param args
      */
-    void errormsg(String key, Object... args) {
+    @Override
+    public void errormsg(String key, Object... args) {
         cmdout.println(prefix(messageFormat(key, args), feedback.getErrorPre()));
     }
 
@@ -383,7 +387,8 @@
      * @param key the resource key
      * @param args
      */
-    void fluffmsg(String key, Object... args) {
+    @Override
+    public void fluffmsg(String key, Object... args) {
         if (feedback.shouldDisplayCommandFluff() && interactive()) {
             hardmsg(key, args);
         }
@@ -512,6 +517,23 @@
                     case "-fullversion":
                         cmdout.printf("jshell %s\n", fullVersion());
                         return null;
+                    case "-feedback":
+                        if (ai.hasNext()) {
+                            initialMode = ai.next();
+                        } else {
+                            startmsg("jshell.err.opt.feedback.arg");
+                            return null;
+                        }
+                        break;
+                    case "-q":
+                        initialMode = "concise";
+                        break;
+                    case "-qq":
+                        initialMode = "silent";
+                        break;
+                    case "-v":
+                        initialMode = "verbose";
+                        break;
                     case "-startup":
                         if (cmdlineStartup != null) {
                             startmsg("jshell.err.opt.startup.conflict");
@@ -530,6 +552,10 @@
                         cmdlineStartup = "";
                         break;
                     default:
+                        if (arg.startsWith("-R")) {
+                            remoteVMOptions.add(arg.substring(2));
+                            break;
+                        }
                         startmsg("jshell.err.opt.unknown", arg);
                         printUsage();
                         return null;
@@ -567,6 +593,7 @@
                 .idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive)
                         ? currentNameSpace.tid(sn)
                         : errorNamespace.tid(sn))
+                .remoteVMOptions(remoteVMOptions.toArray(new String[remoteVMOptions.size()]))
                 .build();
         shutdownSubscription = state.onShutdown((JShell deadState) -> {
             if (deadState == state) {
@@ -596,6 +623,26 @@
             start = cmdlineStartup;
         }
         startUpRun(start);
+        if (initialMode != null) {
+            MessageHandler mh = new MessageHandler() {
+                @Override
+                public void fluff(String format, Object... args) {
+                }
+
+                @Override
+                public void fluffmsg(String messageKey, Object... args) {
+                }
+
+                @Override
+                public void errormsg(String messageKey, Object... args) {
+                    startmsg(messageKey, args);
+                }
+            };
+            if (!feedback.setFeedback(mh, new ArgTokenizer("-feedback ", initialMode))) {
+                regenerateOnDeath = false;
+            }
+            initialMode = null;
+        }
         currentNameSpace = mainNamespace;
     }
     //where
@@ -1050,7 +1097,7 @@
         "format", "feedback", "newmode", "prompt", "editor", "start"};
 
     final boolean cmdSet(String arg) {
-        ArgTokenizer at = new ArgTokenizer(arg.trim());
+        ArgTokenizer at = new ArgTokenizer("/set ", arg.trim());
         String which = setSubCommand(at);
         if (which == null) {
             return false;
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Wed Apr 27 18:13:19 2016 -0700
@@ -28,6 +28,7 @@
 For an introduction type: /help intro\n
 jshell.err.opt.classpath.conflict = Conflicting -classpath option.
 jshell.err.opt.classpath.arg = Argument to -classpath missing.
+jshell.err.opt.feedback.arg = Argument to -feedback missing. Mode required.
 jshell.err.opt.startup.conflict = Conflicting -startup or -nostartup option.
 jshell.err.opt.unknown = Unknown option: {0}
 
@@ -128,13 +129,23 @@
 
 help.usage = \
 Usage:   jshell <options> <load files>\n\
-where possible options include:\n\t\
-  -classpath <path>          Specify where to find user class files\n\t\
-  -cp <path>                 Specify where to find user class files\n\t\
-  -startup <file>            One run replacement for the start-up definitions\n\t\
-  -nostartup                 Do not run the start-up definitions\n\t\
-  -help                      Print a synopsis of standard options\n\t\
-  -version                   Version information\n
+where possible options include:\n\
+\    -classpath <path>    Specify where to find user class files\n\
+\    -cp <path>           Specify where to find user class files\n\
+\    -startup <file>      One run replacement for the start-up definitions\n\
+\    -nostartup           Do not run the start-up definitions\n\
+\    -feedback <mode>     Specify the initial feedback mode. The mode may be\n\
+\                         predefined (silent, concise, normal, or verbose) or\n\
+\                         previously user-defined\n\
+\    -q                   Quiet feedback.  Same as: -feedback concise\n\
+\    -qq                  Really quiet feedback.  Same as: -feedback silent\n\
+\    -v                   Verbose feedback.  Same as: -feedback verbose\n\
+\    -J<flag>             Pass <flag> directly to the runtime system.\n\
+\                         Use one -J for each runtime flag or flag argument\n\
+\    -R<flag>             Pass <flag> to the remote runtime system.\n\
+\                         Use one -R for each remote flag or flag argument\n\
+\    -help                Print this synopsis of standard options\n\
+\    -version             Version information\n
 
 help.list.summary = list the source you have typed
 help.list.args = [all|start|<name or id>]
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java	Wed Apr 27 18:13:19 2016 -0700
@@ -57,11 +57,19 @@
     private ObjectInputStream in;
     private ObjectOutputStream out;
     private final JShell proc;
+    private final String remoteVMOptions;
 
-    ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc) {
+    ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc, List<String> extraRemoteVMOptions) {
         this.env = env;
         this.maps = maps;
         this.proc = proc;
+        StringBuilder sb = new StringBuilder();
+        extraRemoteVMOptions.stream()
+                .forEach(s -> {
+                    sb.append(" ");
+                    sb.append(s);
+                });
+        this.remoteVMOptions = sb.toString();
     }
 
     void launch() throws IOException {
@@ -257,11 +265,9 @@
         //        Locale.getDefault());
 
         String connectorName = "com.sun.jdi.CommandLineLaunch";
-        String classPath = System.getProperty("java.class.path");
-        String javaArgs = "-classpath " + classPath;
         Map<String, String> argumentName2Value = new HashMap<>();
         argumentName2Value.put("main", "jdk.internal.jshell.remote.RemoteAgent " + port);
-        argumentName2Value.put("options", javaArgs);
+        argumentName2Value.put("options", remoteVMOptions);
 
         boolean launchImmediately = true;
         int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS;
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Wed Apr 27 18:13:19 2016 -0700
@@ -26,10 +26,11 @@
 package jdk.jshell;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -84,6 +85,7 @@
     final PrintStream err;
     final Supplier<String> tempVariableNameGenerator;
     final BiFunction<Snippet, Integer, String> idGenerator;
+    final List<String> extraRemoteVMOptions;
 
     private int nextKeyIndex = 1;
 
@@ -105,6 +107,7 @@
         this.err = b.err;
         this.tempVariableNameGenerator = b.tempVariableNameGenerator;
         this.idGenerator = b.idGenerator;
+        this.extraRemoteVMOptions = b.extraRemoteVMOptions;
 
         this.maps = new SnippetMaps(this);
         this.keyMap = new KeyMap(this);
@@ -139,6 +142,7 @@
         PrintStream err = System.err;
         Supplier<String> tempVariableNameGenerator = null;
         BiFunction<Snippet, Integer, String> idGenerator = null;
+        List<String> extraRemoteVMOptions = new ArrayList<>();
 
         Builder() { }
 
@@ -264,6 +268,18 @@
         }
 
         /**
+         * Set additional VM options for launching the VM.
+         *
+         * @param options The options for the remote VM.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder remoteVMOptions(String... options) {
+            this.extraRemoteVMOptions.addAll(Arrays.asList(options));
+            return this;
+        }
+
+        /**
          * Build a JShell state engine. This is the entry-point to all JShell
          * functionality. This creates a remote process for execution. It is
          * thus important to close the returned instance.
@@ -621,10 +637,10 @@
 
     ExecutionControl executionControl() {
         if (executionControl == null) {
-            this.executionControl = new ExecutionControl(new JDIEnv(this), maps, this);
+            this.executionControl = new ExecutionControl(new JDIEnv(this), maps, this, extraRemoteVMOptions);
             try {
                 executionControl.launch();
-            } catch (IOException ex) {
+            } catch (Throwable ex) {
                 throw new InternalError("Launching JDI execution engine threw: " + ex.getMessage(), ex);
             }
         }
--- a/langtools/test/jdk/jshell/StartOptionTest.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/test/jdk/jshell/StartOptionTest.java	Wed Apr 27 18:13:19 2016 -0700
@@ -22,7 +22,7 @@
  */
 
 /*
- * @test
+ * @test 8151754
  * @summary Testing start-up options.
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -34,6 +34,7 @@
  */
 
 import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
@@ -47,6 +48,7 @@
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 @Test
 public class StartOptionTest {
@@ -55,9 +57,24 @@
     private ByteArrayOutputStream err;
 
     private JShellTool getShellTool() {
-        return new JShellTool(null, new PrintStream(out), new PrintStream(err), null, null, null,
-                              null, new ReplToolTesting.MemoryPreferences(),
-                              Locale.ROOT);
+        class NoOutputAllowedStream extends OutputStream {
+            private final String label;
+            NoOutputAllowedStream(String label) {
+               this.label = label;
+            }
+            @Override
+            public void write(int b) { fail("Unexpected output to: " + label); }
+        }
+        return new JShellTool(
+                new TestingInputStream(),
+                new PrintStream(out),
+                new PrintStream(err),
+                new PrintStream(new NoOutputAllowedStream("console")),
+                new TestingInputStream(),
+                new PrintStream(new NoOutputAllowedStream("userout")),
+                new PrintStream(new NoOutputAllowedStream("usererr")),
+                new ReplToolTesting.MemoryPreferences(),
+                Locale.ROOT);
     }
 
     private String getOutput() {
@@ -133,6 +150,12 @@
     }
 
     @Test
+    public void testNegFeedbackOption() throws Exception {
+        start("", "Argument to -feedback missing. Mode required.", "-feedback");
+        start("", "Does not match any current feedback mode: blorp -- -feedback blorp", "-feedback", "blorp");
+    }
+
+    @Test
     public void testVersion() throws Exception {
         start(s -> assertTrue(s.startsWith("jshell")), null, "-version");
     }
--- a/langtools/test/jdk/jshell/ToolSimpleTest.java	Fri Apr 15 11:39:31 2016 +0200
+++ b/langtools/test/jdk/jshell/ToolSimpleTest.java	Wed Apr 27 18:13:19 2016 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8153716 8143955
+ * @bug 8153716 8143955 8151754 8150382
  * @summary Simple jshell tool tests
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -349,4 +349,39 @@
             System.setProperty("java.awt.headless", prevHeadless==null? "false" : prevHeadless);
         }
     }
+
+    public void testOptionQ() {
+        test(new String[]{"-q", "-nostartup"},
+                (a) -> assertCommand(a, "1+1", "$1 ==> 2"),
+                (a) -> assertCommand(a, "int x = 5", "")
+        );
+    }
+
+    public void testOptionQq() {
+        test(new String[]{"-qq", "-nostartup"},
+                (a) -> assertCommand(a, "1+1", "")
+        );
+    }
+
+    public void testOptionV() {
+        test(new String[]{"-v", "-nostartup"},
+                (a) -> assertCommand(a, "1+1",
+                        "$1 ==> 2\n" +
+                        "|  created scratch variable $1 : int")
+        );
+    }
+
+    public void testOptionFeedback() {
+        test(new String[]{"-feedback", "concise", "-nostartup"},
+                (a) -> assertCommand(a, "1+1", "$1 ==> 2"),
+                (a) -> assertCommand(a, "int x = 5", "")
+        );
+    }
+
+    public void testOptionR() {
+        test(new String[]{"-R-Dthe.sound=blorp", "-nostartup"},
+                (a) -> assertCommand(a, "System.getProperty(\"the.sound\")",
+                        "$1 ==> \"blorp\"")
+        );
+    }
 }