8150382: JShell API: Allow setting remote JVM arguments

8151754: jshell tool: add command line options for setting feedback mode

Reviewed-by: jlahoda
This commit is contained in:
Robert Field 2016-04-27 18:13:19 -07:00
parent d20efa360f
commit 83f65483e4
8 changed files with 185 additions and 41 deletions

View File

@ -35,6 +35,7 @@ import java.util.stream.Stream;
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 @@ class ArgTokenizer {
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 @@ class ArgTokenizer {
}
String whole() {
return str;
return prefix + str;
}
void mark() {

View File

@ -95,20 +95,20 @@ class Feedback {
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 @@ class Feedback {
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>"

View File

@ -108,7 +108,7 @@ import static jdk.jshell.Snippet.SubKind.VAR_VALUE_SUBKIND;
* 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 @@ public class JShellTool {
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 @@ public class JShellTool {
* @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 @@ public class JShellTool {
* @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 @@ public class JShellTool {
* @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 @@ public class JShellTool {
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 @@ public class JShellTool {
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 @@ public class JShellTool {
.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 @@ public class JShellTool {
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 @@ public class JShellTool {
"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;

View File

@ -28,6 +28,7 @@ Welcome to JShell -- Version {0}\n\
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 @@ jshell.console.incomplete = \nResults may be incomplete; try again later for com
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>]

View File

@ -57,11 +57,19 @@ class ExecutionControl {
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 @@ class ExecutionControl {
// 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;

View File

@ -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 @@ public class JShell implements AutoCloseable {
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 @@ public class JShell implements AutoCloseable {
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 @@ public class JShell implements AutoCloseable {
PrintStream err = System.err;
Supplier<String> tempVariableNameGenerator = null;
BiFunction<Snippet, Integer, String> idGenerator = null;
List<String> extraRemoteVMOptions = new ArrayList<>();
Builder() { }
@ -263,6 +267,18 @@ public class JShell implements AutoCloseable {
return this;
}
/**
* 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
@ -621,10 +637,10 @@ public class JShell implements AutoCloseable {
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);
}
}

View File

@ -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 org.testng.annotations.Test;
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 @@ public class StartOptionTest {
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() {
@ -132,6 +149,12 @@ public class StartOptionTest {
}
}
@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");

View File

@ -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 @@ public class ToolSimpleTest extends ReplToolTesting {
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\"")
);
}
}