changeset 4594:b8c5746a893a

Merge
author alanb
date Fri, 06 Jan 2017 08:42:28 +0000
parents 78b2e7ca4434 2c5ee45f64f5
children 17e7aef7a017 871299ba2c5b
files .hgtags src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties src/jdk.jshell/share/classes/module-info.java test/Makefile
diffstat 57 files changed, 2294 insertions(+), 536 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed Dec 28 11:45:19 2016 +0000
+++ b/.hgtags	Fri Jan 06 08:42:28 2017 +0000
@@ -393,3 +393,4 @@
 586c93260d3b2f91a055b96118b141c05841694f jdk-9+148
 4d4cd7cd731c1952d7330ea5bcfda8bd26820fa5 jdk-9+149
 e5a42ddaf633fde14b983f740ae0e7e490741fd1 jdk-9+150
+4f348bd05341581df84ff1510d5b3a9b5b488367 jdk-9+151
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java	Fri Jan 06 08:42:28 2017 +0000
@@ -241,4 +241,21 @@
         }
     }
 
+    /** Returns true if the given value is within the allowed range for this type. */
+    public boolean checkRange(int value) {
+        switch (this) {
+            case BOOLEAN:
+                return 0 <= value && value <= 1;
+            case BYTE:
+                return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE;
+            case CHAR:
+                return Character.MIN_VALUE <= value && value <= Character.MAX_VALUE;
+            case SHORT:
+                return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE;
+            case INT:
+                return true;
+            default:
+                throw new AssertionError();
+        }
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri Jan 06 08:42:28 2017 +0000
@@ -2050,19 +2050,12 @@
             int value = ((Number)t.constValue()).intValue();
             switch (s.getTag()) {
             case BYTE:
-                if (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE)
+            case CHAR:
+            case SHORT:
+            case INT:
+                if (s.getTag().checkRange(value))
                     return true;
                 break;
-            case CHAR:
-                if (Character.MIN_VALUE <= value && value <= Character.MAX_VALUE)
-                    return true;
-                break;
-            case SHORT:
-                if (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE)
-                    return true;
-                break;
-            case INT:
-                return true;
             case CLASS:
                 switch (unboxedType(s).getTag()) {
                 case BYTE:
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Jan 06 08:42:28 2017 +0000
@@ -3190,7 +3190,7 @@
             if (TreeInfo.isStaticSelector(referenceTree.expr, names)) {
                 if (argtypes.nonEmpty() &&
                         (argtypes.head.hasTag(NONE) ||
-                        types.isSubtypeUnchecked(inferenceContext.asUndetVar(argtypes.head), site))) {
+                        types.isSubtypeUnchecked(inferenceContext.asUndetVar(argtypes.head), originalSite))) {
                     return new UnboundMethodReferenceLookupHelper(referenceTree, name,
                             originalSite, argtypes, typeargtypes, maxPhase);
                 } else {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Fri Jan 06 08:42:28 2017 +0000
@@ -1028,8 +1028,45 @@
                 protected void read(Symbol sym, int attrLen) {
                     Object v = readPool(nextChar());
                     // Ignore ConstantValue attribute if field not final.
-                    if ((sym.flags() & FINAL) != 0)
-                        ((VarSymbol) sym).setData(v);
+                    if ((sym.flags() & FINAL) == 0) {
+                        return;
+                    }
+                    VarSymbol var = (VarSymbol) sym;
+                    switch (var.type.getTag()) {
+                       case BOOLEAN:
+                       case BYTE:
+                       case CHAR:
+                       case SHORT:
+                       case INT:
+                           checkType(var, Integer.class, v);
+                           break;
+                       case LONG:
+                           checkType(var, Long.class, v);
+                           break;
+                       case FLOAT:
+                           checkType(var, Float.class, v);
+                           break;
+                       case DOUBLE:
+                           checkType(var, Double.class, v);
+                           break;
+                       case CLASS:
+                           Assert.check(var.type.tsym == syms.stringType.tsym);
+                           checkType(var, String.class, v);
+                           break;
+                       default:
+                           // ignore ConstantValue attribute if type is not primitive or String
+                           return;
+                    }
+                    if (v instanceof Integer && !var.type.getTag().checkRange((Integer) v)) {
+                        throw badClassFile("bad.constant.range", v, var, var.type);
+                    }
+                    var.setData(v);
+                }
+
+                void checkType(Symbol var, Class<?> clazz, Object value) {
+                    if (!clazz.isInstance(value)) {
+                        throw badClassFile("bad.constant.value", value, var, clazz.getSimpleName());
+                    }
                 }
             },
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jan 06 08:42:28 2017 +0000
@@ -1913,6 +1913,14 @@
 compiler.misc.class.file.not.found=\
     class file for {0} not found
 
+# 0: string (constant value), 1: symbol (constant field), 2: type (field type)
+compiler.misc.bad.constant.range=\
+    constant value ''{0}'' for {1} is outside the expected range for {2}
+
+# 0: string (constant value), 1: symbol (constant field), 2: string (expected class)
+compiler.misc.bad.constant.value=\
+    bad constant value ''{0}'' for {1}, expected {2}
+
 # 0: string (classfile major version), 1: string (classfile minor version)
 compiler.misc.invalid.default.interface=\
     default method found in version {0}.{1} classfile
--- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Jan 06 08:42:28 2017 +0000
@@ -25,15 +25,18 @@
 
 package jdk.internal.jshell.tool;
 
+import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.file.AccessDeniedException;
 import java.nio.file.FileSystems;
@@ -196,7 +199,9 @@
     private boolean debug = false;
     public boolean testPrompt = false;
     private String cmdlineClasspath = null;
+    private String defaultStartup = null;
     private String startup = null;
+    private String executionControlSpec = null;
     private EditorSetting editor = BUILT_IN_EDITOR;
 
     private static final String[] EDITOR_ENV_VARS = new String[] {
@@ -212,16 +217,9 @@
     static final String MODE_KEY     = "MODE";
     static final String REPLAY_RESTORE_KEY = "REPLAY_RESTORE";
 
-    static final String DEFAULT_STARTUP =
-            "\n" +
-            "import java.util.*;\n" +
-            "import java.io.*;\n" +
-            "import java.math.*;\n" +
-            "import java.net.*;\n" +
-            "import java.util.concurrent.*;\n" +
-            "import java.util.prefs.*;\n" +
-            "import java.util.regex.*;\n" +
-            "void printf(String format, Object... args) { System.out.printf(format, args); }\n";
+    static final String DEFAULT_STARTUP_NAME = "DEFAULT";
+    static final Pattern BUILTIN_FILE_PATTERN = Pattern.compile("\\w+");
+    static final String BUILTIN_FILE_PATH_FORMAT = "jrt:/jdk.jshell/jdk/jshell/tool/resources/%s.jsh";
 
     // Tool id (tid) mapping: the three name spaces
     NameSpace mainNamespace;
@@ -481,7 +479,7 @@
         if (startup == null) {
             startup = prefs.get(STARTUP_KEY);
             if (startup == null) {
-                startup = DEFAULT_STARTUP;
+                startup = defaultStartup();
             }
         }
 
@@ -546,6 +544,7 @@
         OptionSpec<String> st = parser.accepts("startup").withRequiredArg();
         parser.acceptsAll(asList("n", "no-startup"));
         OptionSpec<String> fb = parser.accepts("feedback").withRequiredArg();
+        OptionSpec<String> ec = parser.accepts("execution").withRequiredArg();
         parser.accepts("q");
         parser.accepts("s");
         parser.accepts("v");
@@ -604,14 +603,19 @@
         }
         if (options.has(st)) {
             List<String> sts = options.valuesOf(st);
-            if (sts.size() != 1 || options.has("no-startup")) {
-                startmsg("jshell.err.opt.startup.one");
+            if (options.has("no-startup")) {
+                startmsg("jshell.err.opt.startup.conflict");
                 return null;
             }
-            startup = readFile(sts.get(0), "--startup");
-            if (startup == null) {
-                return null;
+            StringBuilder sb = new StringBuilder();
+            for (String fn : sts) {
+                String s = readFile(fn, "--startup");
+                if (s == null) {
+                    return null;
+                }
+                sb.append(s);
             }
+            startup = sb.toString();
         } else if (options.has("no-startup")) {
             startup = "";
         }
@@ -648,6 +652,9 @@
             remoteVMOptions.add("--add-modules");
             remoteVMOptions.addAll(options.valuesOf(amods));
         }
+        if (options.has(ec)) {
+            executionControlSpec = options.valueOf(ec);
+        }
 
         if (options.has(addExports)) {
             List<String> exports = options.valuesOf(addExports).stream()
@@ -718,18 +725,21 @@
         // Reset the replayable history, saving the old for restore
         replayableHistoryPrevious = replayableHistory;
         replayableHistory = new ArrayList<>();
-
-        state = JShell.builder()
+        JShell.Builder builder =
+               JShell.builder()
                 .in(userin)
                 .out(userout)
                 .err(usererr)
-                .tempVariableNameGenerator(()-> "$" + currentNameSpace.tidNext())
+                .tempVariableNameGenerator(() -> "$" + currentNameSpace.tidNext())
                 .idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive())
                         ? currentNameSpace.tid(sn)
                         : errorNamespace.tid(sn))
                 .remoteVMOptions(remoteVMOptions.stream().toArray(String[]::new))
-                .compilerOptions(compilerOptions.stream().toArray(String[]::new))
-                .build();
+                .compilerOptions(compilerOptions.stream().toArray(String[]::new));
+        if (executionControlSpec != null) {
+            builder.executionEngine(executionControlSpec);
+        }
+        state = builder.build();
         shutdownSubscription = state.onShutdown((JShell deadState) -> {
             if (deadState == state) {
                 hardmsg("jshell.msg.terminated");
@@ -790,7 +800,7 @@
 
     //where
     private void startUpRun(String start) {
-        try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
+        try (IOContext suin = new ScannerIOContext(new StringReader(start))) {
             run(suin);
         } catch (Exception ex) {
             hardmsg("jshell.err.startup.unexpected.exception", ex);
@@ -1615,14 +1625,17 @@
     // The sub-command:  /set start <start-file>
     boolean setStart(ArgTokenizer at) {
         at.allowedOptions("-default", "-none", "-retain");
-        String fn = at.next();
+        List<String> fns = new ArrayList<>();
+        while (at.next() != null) {
+            fns.add(at.val());
+        }
         if (!checkOptionsAndRemainingInput(at)) {
             return false;
         }
         boolean defaultOption = at.hasOption("-default");
         boolean noneOption = at.hasOption("-none");
         boolean retainOption = at.hasOption("-retain");
-        boolean hasFile = fn != null;
+        boolean hasFile = !fns.isEmpty();
 
         int argCount = (defaultOption ? 1 : 0) + (noneOption ? 1 : 0) + (hasFile ? 1 : 0);
         if (argCount > 1) {
@@ -1635,13 +1648,17 @@
             return true;
         }
         if (hasFile) {
-            String init = readFile(fn, "/set start");
-            if (init == null) {
-                return false;
+            StringBuilder sb = new StringBuilder();
+            for (String fn : fns) {
+                String s = readFile(fn, "/set start");
+                if (s == null) {
+                    return false;
+                }
+                sb.append(s);
             }
-            startup = init;
+            startup = sb.toString();
         } else if (defaultOption) {
-            startup = DEFAULT_STARTUP;
+            startup = defaultStartup();
         } else if (noneOption) {
             startup = "";
         }
@@ -1665,7 +1682,7 @@
     void showSetStart(boolean isRetained, String start) {
         String cmd = "/set start" + (isRetained ? " -retain " : " ");
         String stset;
-        if (start.equals(DEFAULT_STARTUP)) {
+        if (start.equals(defaultStartup())) {
             stset = cmd + "-default";
         } else if (start.isEmpty()) {
             stset = cmd + "-none";
@@ -2158,7 +2175,16 @@
     private boolean runFile(String filename, String context) {
         if (!filename.isEmpty()) {
             try {
-                run(new FileScannerIOContext(toPathResolvingUserHome(filename).toString()));
+                Path path = toPathResolvingUserHome(filename);
+                Reader reader;
+                String resource;
+                if (!Files.exists(path) && (resource = getResource(filename)) != null) {
+                    // Not found as file, but found as resource
+                    reader = new StringReader(resource);
+                } else {
+                    reader = new FileReader(path.toString());
+                }
+                run(new ScannerIOContext(reader));
                 return true;
             } catch (FileNotFoundException e) {
                 errormsg("jshell.err.file.not.found", context, filename, e.getMessage());
@@ -2181,11 +2207,16 @@
     String readFile(String filename, String context) {
         if (filename != null) {
             try {
-                byte[] encoded = Files.readAllBytes(Paths.get(filename));
+                byte[] encoded = Files.readAllBytes(toPathResolvingUserHome(filename));
                 return new String(encoded);
             } catch (AccessDeniedException e) {
                 errormsg("jshell.err.file.not.accessible", context, filename, e.getMessage());
             } catch (NoSuchFileException e) {
+                String resource = getResource(filename);
+                if (resource != null) {
+                    // Not found as file, but found as resource
+                    return resource;
+                }
                 errormsg("jshell.err.file.not.found", context, filename);
             } catch (Exception e) {
                 errormsg("jshell.err.file.exception", context, filename, e);
@@ -2197,6 +2228,44 @@
 
     }
 
+    // Read a built-in file from resources or null
+    String getResource(String name) {
+        if (BUILTIN_FILE_PATTERN.matcher(name).matches()) {
+            try {
+                return readResource(name);
+            } catch (Throwable t) {
+                // Fall-through to null
+            }
+        }
+        return null;
+    }
+
+    // Read a built-in file from resources
+    String readResource(String name) throws IOException {
+        // Attempt to find the file as a resource
+        String spec = String.format(BUILTIN_FILE_PATH_FORMAT, name);
+        URL url = new URL(spec);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
+        return reader.lines().collect(Collectors.joining("\n"));
+    }
+
+    // retrieve the default startup string
+    String defaultStartup() {
+        if (defaultStartup == null) {
+            defaultStartup = ""; // failure case
+            try {
+                defaultStartup = readResource(DEFAULT_STARTUP_NAME);
+            } catch (AccessDeniedException e) {
+                errormsg("jshell.err.file.not.accessible", "jshell", DEFAULT_STARTUP_NAME, e.getMessage());
+            } catch (NoSuchFileException e) {
+                errormsg("jshell.err.file.not.found", "jshell", DEFAULT_STARTUP_NAME);
+            } catch (Exception e) {
+                errormsg("jshell.err.file.exception", "jshell", DEFAULT_STARTUP_NAME, e);
+            }
+        }
+        return defaultStartup;
+    }
+
     private boolean cmdReset() {
         live = false;
         fluffmsg("jshell.msg.resetting.state");
@@ -2902,6 +2971,10 @@
         this.scannerIn = scannerIn;
     }
 
+    ScannerIOContext(Reader rdr) throws FileNotFoundException {
+        this(new Scanner(rdr));
+    }
+
     @Override
     public String readLine(String prompt, String prefix) {
         if (scannerIn.hasNextLine()) {
@@ -2922,17 +2995,6 @@
     }
 }
 
-class FileScannerIOContext extends ScannerIOContext {
-
-    FileScannerIOContext(String fn) throws FileNotFoundException {
-        this(new FileReader(fn));
-    }
-
-    FileScannerIOContext(Reader rdr) throws FileNotFoundException {
-        super(new Scanner(rdr));
-    }
-}
-
 class ReloadIOContext extends NonInteractiveIOContext {
     private final Iterator<String> it;
     private final PrintStream echoStream;
--- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Fri Jan 06 08:42:28 2017 +0000
@@ -29,7 +29,7 @@
 jshell.err.opt.arg = Argument to {0} missing.
 jshell.err.opt.invalid = Invalid options: {0}.
 jshell.err.opt.one = Only one {0} option may be used.
-jshell.err.opt.startup.one = Only one --startup or --no-startup option may be used.
+jshell.err.opt.startup.conflict = Conflicting options: both --startup and --no-startup were used.
 jshell.err.opt.feedback.one = Only one feedback option (--feedback, -q, -s, or -v) may be used.
 jshell.err.opt.unknown = Unknown option: {0}
 
@@ -150,8 +150,8 @@
 jshell.console.see.more = <press tab to see more>
 jshell.console.see.javadoc = <press shift-tab again to see javadoc>
 jshell.console.see.help = <press shift-tab again to see detailed help>
-jshell.console.see.next.page = -- Press space for next page, Q to quit. --
-jshell.console.see.next.javadoc = -- Press space for next javadoc, Q to quit. --
+jshell.console.see.next.page = <press space for next page, Q to quit>
+jshell.console.see.next.javadoc = <press space for next javadoc, Q to quit>
 jshell.console.no.javadoc = <no javadoc found>
 jshell.console.do.nothing = Do nothing
 jshell.console.choice = Choice: \
@@ -169,12 +169,12 @@
 \    --module-path <path>  Specify where to find application modules\n\
 \    --add-modules <module>(,<module>)*\n\
 \                          Specify modules to resolve, or all modules on the\n\
-\                           module path if <module> is ALL-MODULE-PATHs\n\
+\                            module path if <module> is ALL-MODULE-PATHs\n\
 \    --startup <file>      One run replacement for the start-up definitions\n\
 \    --no-startup          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\
+\                            predefined (silent, concise, normal, or verbose) or\n\
+\                            previously user-defined\n\
 \    -q                    Quiet feedback.  Same as: --feedback concise\n\
 \    -s                    Really quiet feedback.  Same as: --feedback silent\n\
 \    -v                    Verbose feedback.  Same as: --feedback verbose\n\
@@ -189,6 +189,10 @@
 \    -X                    Print help on non-standard options\n
 help.usage.x = \
 \    --add-exports <module>/<package>   Export specified module-private package to snippets\n\
+\    --execution <spec>                 Specify an alternate execution engine.\n\
+\                                         Where <spec> is an ExecutionControl spec.\n\
+\                                         See the documentation of the package\n\
+\                                         jdk.jshell.spi for the syntax of the spec\n\
 \    \n\
 \These options are non-standard and subject to change without notice.\n
 
--- a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Fri Jan 06 08:42:28 2017 +0000
@@ -48,11 +48,10 @@
 import java.util.stream.Stream;
 import jdk.internal.jshell.debug.InternalDebugControl;
 import jdk.jshell.Snippet.Status;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
 import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
+import jdk.jshell.spi.ExecutionControlProvider;
 import jdk.jshell.spi.ExecutionEnv;
-import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
 import static jdk.jshell.Util.expunge;
 
 /**
@@ -116,15 +115,20 @@
         this.idGenerator = b.idGenerator;
         this.extraRemoteVMOptions = b.extraRemoteVMOptions;
         this.extraCompilerOptions = b.extraCompilerOptions;
-        ExecutionControl.Generator executionControlGenerator = b.executionControlGenerator==null
-                ? failOverExecutionControlGenerator(
-                        JdiDefaultExecutionControl.listen(InetAddress.getLoopbackAddress().getHostAddress()),
-                        JdiDefaultExecutionControl.launch(),
-                        JdiDefaultExecutionControl.listen(null)
-                  )
-                : b.executionControlGenerator;
         try {
-            executionControl = executionControlGenerator.generate(new ExecutionEnvImpl());
+            if (b.executionControlProvider != null) {
+                executionControl = b.executionControlProvider.generate(new ExecutionEnvImpl(),
+                        b.executionControlParameters == null
+                                ? b.executionControlProvider.defaultParameters()
+                                : b.executionControlParameters);
+            } else {
+                String loopback = InetAddress.getLoopbackAddress().getHostAddress();
+                String spec = b.executionControlSpec == null
+                        ? "failover:0(jdi:hostname(" + loopback + ")),"
+                          + "1(jdi:launch(true)), 2(jdi)"
+                        : b.executionControlSpec;
+                executionControl = ExecutionControl.generate(new ExecutionEnvImpl(), spec);
+            }
         } catch (Throwable ex) {
             throw new IllegalStateException("Launching JShell execution engine threw: " + ex.getMessage(), ex);
         }
@@ -164,7 +168,9 @@
         BiFunction<Snippet, Integer, String> idGenerator = null;
         List<String> extraRemoteVMOptions = new ArrayList<>();
         List<String> extraCompilerOptions = new ArrayList<>();
-        ExecutionControl.Generator executionControlGenerator;
+        ExecutionControlProvider executionControlProvider;
+        Map<String,String> executionControlParameters;
+        String executionControlSpec;
 
         Builder() { }
 
@@ -322,14 +328,39 @@
 
         /**
          * Sets the custom engine for execution. Snippet execution will be
-         * provided by the specified {@link ExecutionControl} instance.
+         * provided by the {@link ExecutionControl} instance selected by the
+         * specified execution control spec.
+         * Use, at most, one of these overloaded {@code executionEngine} builder
+         * methods.
          *
-         * @param executionControlGenerator the execution engine generator
+         * @param executionControlSpec the execution control spec,
+         * which is documented in the {@link jdk.jshell.spi}
+         * package documentation.
          * @return the {@code Builder} instance (for use in chained
          * initialization)
          */
-        public Builder executionEngine(ExecutionControl.Generator executionControlGenerator) {
-            this.executionControlGenerator = executionControlGenerator;
+        public Builder executionEngine(String executionControlSpec) {
+            this.executionControlSpec = executionControlSpec;
+            return this;
+        }
+
+        /**
+         * Sets the custom engine for execution. Snippet execution will be
+         * provided by the specified {@link ExecutionControl} instance.
+         * Use, at most, one of these overloaded {@code executionEngine} builder
+         * methods.
+         *
+         * @param executionControlProvider the provider to supply the execution
+         * engine
+         * @param executionControlParameters the parameters to the provider, or
+         * {@code null} for default parameters
+         * @return the {@code Builder} instance (for use in chained
+         * initialization)
+         */
+        public Builder executionEngine(ExecutionControlProvider executionControlProvider,
+                Map<String,String> executionControlParameters) {
+            this.executionControlProvider = executionControlProvider;
+            this.executionControlParameters = executionControlParameters;
             return this;
         }
 
--- a/src/jdk.jshell/share/classes/jdk/jshell/execution/DirectExecutionControl.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/DirectExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -202,12 +202,27 @@
         } else if (value instanceof Character) {
             return "'" + value + "'";
         } else if (value.getClass().isArray()) {
-            String tn = value.getClass().getTypeName();
+            int dims = 0;
+            Class<?> t = value.getClass();
+            while (true) {
+                Class<?> ct = t.getComponentType();
+                if (ct == null) {
+                    break;
+                }
+                ++dims;
+                t = ct;
+            }
+            String tn = t.getTypeName();
             int len = Array.getLength(value);
             StringBuilder sb = new StringBuilder();
-            sb.append(tn.substring(tn.lastIndexOf('.') + 1, tn.length() - 1));
+            sb.append(tn.substring(tn.lastIndexOf('.') + 1, tn.length()));
+            sb.append("[");
             sb.append(len);
-            sb.append("] { ");
+            sb.append("]");
+            for (int i = 1; i < dims; ++i) {
+                sb.append("[]");
+            }
+            sb.append(" { ");
             for (int i = 0; i < len; ++i) {
                 sb.append(valueString(Array.get(value, i)));
                 if (i < len - 1) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/FailOverExecutionControlProvider.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016, 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 jdk.jshell.execution;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+
+/**
+ * Tries other providers in sequence until one works.
+ */
+public class FailOverExecutionControlProvider  implements ExecutionControlProvider{
+
+    private Logger logger = null;
+
+    /**
+     * Create an instance. The instance can be used to start and return an
+     * {@link ExecutionControl} instance by attempting to start a series of
+     * {@code ExecutionControl} specs, until one is successful.
+     *
+     * @see #generate(jdk.jshell.spi.ExecutionEnv, java.util.Map)
+     */
+    public FailOverExecutionControlProvider() {
+    }
+
+    /**
+     * The unique name of this {@code ExecutionControlProvider}.
+     *
+     * @return "failover"
+     */
+    @Override
+    public String name() {
+        return "failover";
+    }
+
+    /**
+     * Create and return the default parameter map for this
+     * {@code ExecutionControlProvider}. There are ten parameters, "0" through
+     * "9", their values are {@code ExecutionControlProvider} specification
+     * strings, or empty string.
+     *
+     * @return a default parameter map
+     */
+    @Override
+    public Map<String, String> defaultParameters() {
+        Map<String, String> dp = new HashMap<>();
+        dp.put("0", "jdi");
+        for (int i = 1; i <= 9; ++i) {
+            dp.put("" + i, "");
+        }
+        return dp;
+    }
+
+    /**
+     * Create and return a locally executing {@code ExecutionControl} instance.
+     * At least one parameter should have a spec.
+     *
+     * @param env the execution environment, provided by JShell
+     * @param parameters the modified parameter map.
+     * @return the execution engine
+     * @throws Throwable if all the given providers fail, the exception that
+     * occurred on the first attempt to create the execution engine.
+     */
+    @Override
+    public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters)
+            throws Throwable {
+        Throwable thrown = null;
+        for (int i = 0; i <= 9; ++i) {
+            String param = parameters.get("" + i);
+            if (param != null && !param.isEmpty()) {
+                try {
+                    ExecutionControl ec =  ExecutionControl.generate(env, param);
+                    logger().finest(
+                            String.format("FailOverExecutionControlProvider: Success %s -- %d = %s\n",
+                                    name(), i, param));
+                    return ec;
+                } catch (Throwable ex) {
+                    logger().warning(
+                            String.format("FailOverExecutionControlProvider: Failure %s -- %d = %s -- %s\n",
+                                    name(), i, param, ex.toString()));
+                    StringWriter writer = new StringWriter();
+                    PrintWriter log = new PrintWriter(writer);
+                    log.println("FailOverExecutionControlProvider:");
+                    ex.printStackTrace(log);
+                    logger().fine(log.toString());
+                    // only care about the first, and only if they all fail
+                    if (thrown == null) {
+                        thrown = ex;
+                    }
+                }
+            }
+
+        }
+        logger().severe("FailOverExecutionControlProvider: Terminating, failovers exhausted");
+        if (thrown == null) {
+            throw new IllegalArgumentException("All least one parameter must be set to a provider.");
+        }
+        throw thrown;
+    }
+
+    private Logger logger() {
+        if (logger == null) {
+            logger = Logger.getLogger("jdk.jshell.execution");
+            logger.setLevel(Level.ALL);
+        }
+        return logger;
+    }
+
+}
--- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -63,77 +63,15 @@
  */
 public class JdiDefaultExecutionControl extends JdiExecutionControl {
 
-    /**
-     * Default time-out expressed in milliseconds.
-     */
-    private static final int DEFAULT_TIMEOUT = 5000;
-
     private VirtualMachine vm;
     private Process process;
+    private final String remoteAgent;
 
     private final Object STOP_LOCK = new Object();
     private boolean userCodeRunning = false;
 
     /**
      * Creates an ExecutionControl instance based on a JDI
-     * {@code LaunchingConnector}. Same as
-     * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), true, null, defaultTimeout())}.
-     *
-     * @return the generator
-     */
-    public static ExecutionControl.Generator launch() {
-        return create(defaultRemoteAgent(), true, null, defaultTimeout());
-    }
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
-     * {@code ListeningConnector}. Same as
-     * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), false, host, defaultTimeout())}.
-     *
-     * @param host explicit hostname to use, if null use discovered
-     * hostname, applies to listening only (!isLaunch)
-     * @return the generator
-     */
-    public static ExecutionControl.Generator listen(String host) {
-        return create(defaultRemoteAgent(), false, host, defaultTimeout());
-    }
-
-    /**
-     * Creates a JDI based ExecutionControl instance.
-     *
-     * @param remoteAgent the remote agent to launch
-     * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
-     * otherwise we start explicitly and use ListeningConnector
-     * @param host explicit hostname to use, if null use discovered
-     * hostname, applies to listening only (!isLaunch)
-     * @param timeout the start-up time-out in milliseconds
-     * @return the generator
-     */
-    public static ExecutionControl.Generator create(String remoteAgent,
-            boolean isLaunch, String host, int timeout) {
-        return env -> create(env, remoteAgent, isLaunch, host, timeout);
-    }
-
-    /**
-     * Default remote agent.
-     *
-     * @return the name of the standard remote agent
-     */
-    public static String defaultRemoteAgent() {
-        return RemoteExecutionControl.class.getName();
-    }
-
-    /**
-     * Default remote connection time-out
-     *
-     * @return time to wait for connection before failing, expressed in milliseconds.
-     */
-    public static int defaultTimeout() {
-        return DEFAULT_TIMEOUT;
-    }
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
      * {@code ListeningConnector} or {@code LaunchingConnector}.
      *
      * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
@@ -150,7 +88,7 @@
      * @return the channel
      * @throws IOException if there are errors in set-up
      */
-    private static ExecutionControl create(ExecutionEnv env, String remoteAgent,
+    static ExecutionControl create(ExecutionEnv env, String remoteAgent,
             boolean isLaunch, String host, int timeout) throws IOException {
         try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
             // timeout on I/O-socket
@@ -181,7 +119,8 @@
             outputs.put("err", env.userErr());
             Map<String, InputStream> input = new HashMap<>();
             input.put("in", env.userIn());
-            return remoteInputOutput(socket.getInputStream(), out, outputs, input, (objIn, objOut) -> new JdiDefaultExecutionControl(objOut, objIn, vm, process, deathListeners));
+            return remoteInputOutput(socket.getInputStream(), out, outputs, input,
+                    (objIn, objOut) -> new JdiDefaultExecutionControl(objOut, objIn, vm, process, remoteAgent, deathListeners));
         }
     }
 
@@ -192,10 +131,12 @@
      * @param cmdin the input for responses
      */
     private JdiDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
-            VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
+            VirtualMachine vm, Process process, String remoteAgent,
+            List<Consumer<String>> deathListeners) {
         super(cmdout, cmdin);
         this.vm = vm;
         this.process = process;
+        this.remoteAgent = remoteAgent;
         deathListeners.add(s -> disposeVM());
     }
 
@@ -237,7 +178,7 @@
                 for (ThreadReference thread : vm().allThreads()) {
                     // could also tag the thread (e.g. using name), to find it easier
                     for (StackFrame frame : thread.frames()) {
-                        if (defaultRemoteAgent().equals(frame.location().declaringType().name()) &&
+                        if (remoteAgent.equals(frame.location().declaringType().name()) &&
                                 (    "invoke".equals(frame.location().method().name())
                                 || "varValue".equals(frame.location().method().name()))) {
                             ObjectReference thiz = frame.thisObject();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControlProvider.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016, 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 jdk.jshell.execution;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+
+/**
+ * A provider of remote JDI-controlled execution engines.
+ * @author Robert Field
+ */
+public class JdiExecutionControlProvider implements ExecutionControlProvider {
+
+    /**
+     * The remote agent to launch.
+     */
+    public static final String PARAM_REMOTE_AGENT = "remoteAgent";
+
+    /**
+     * Milliseconds before connect timeout.
+     */
+    public static final String PARAM_TIMEOUT = "timeout";
+
+    /**
+     * The local hostname to connect to.
+     */
+    public static final String PARAM_HOST_NAME = "hostname";
+
+    /**
+     * Should JDI-controlled launching be used?
+     */
+    public static final String PARAM_LAUNCH = "launch";
+
+    /**
+     * Default time-out expressed in milliseconds.
+     */
+    private static final int DEFAULT_TIMEOUT = 5000;
+
+    /**
+     * Create an instance.  An instance can be used to
+     * {@linkplain  #generate generate} an {@link ExecutionControl} instance
+     * that uses the Java Debug Interface as part of the control of a remote
+     * process.
+     */
+    public JdiExecutionControlProvider() {
+    }
+
+    /**
+     * The unique name of this {@code ExecutionControlProvider}.
+     *
+     * @return "jdi"
+     */
+    @Override
+    public String name() {
+        return "jdi";
+    }
+
+    /**
+     * Create and return the default parameter map for this
+     * {@code ExecutionControlProvider}. The map can optionally be modified;
+     * Modified or unmodified it can be passed to
+     * {@link #generate(jdk.jshell.spi.ExecutionEnv, java.util.Map) }.
+     * <table summary="Parameters">
+     *   <tr>
+     *     <th>Parameter</th>
+     *     <th>Description</th>
+     *     <th>Constant Field</th>
+     *   </tr>
+     *   <tr>
+     *     <td>remoteAgent</td>
+     *     <td>the remote agent to launch</td>
+     *     <td>{@link #PARAM_REMOTE_AGENT}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>timeout</td>
+     *     <td>milliseconds before connect timeout</td>
+     *     <td>{@link #PARAM_TIMEOUT}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>launch</td>
+     *     <td>"true" for JDI controlled launch</td>
+     *     <td>{@link #PARAM_LAUNCH}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>hostname</td>
+     *     <td>connect to the named of the local host ("" for discovered)</td>
+     *     <td>{@link #PARAM_HOST_NAME}</td>
+     *   </tr>
+     * </table>
+     *
+     * @return the default parameter map
+     */
+    @Override
+    public Map<String, String> defaultParameters() {
+        Map<String, String> dp = new HashMap<>();
+        dp.put(PARAM_REMOTE_AGENT, RemoteExecutionControl.class.getName());
+        dp.put(PARAM_TIMEOUT, "" + DEFAULT_TIMEOUT);
+        dp.put(PARAM_HOST_NAME, "");
+        dp.put(PARAM_LAUNCH, "false");
+        return dp;
+    }
+
+    @Override
+    public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters)
+            throws IOException {
+        Map<String, String> dp  = defaultParameters();
+        if (parameters == null) {
+            parameters = dp;
+        }
+        String remoteAgent = parameters.getOrDefault(PARAM_REMOTE_AGENT, dp.get(PARAM_REMOTE_AGENT));
+        int timeout = Integer.parseUnsignedInt(
+                parameters.getOrDefault(PARAM_TIMEOUT, dp.get(PARAM_TIMEOUT)));
+        String host = parameters.getOrDefault(PARAM_HOST_NAME, dp.get(PARAM_HOST_NAME));
+        String sIsLaunch = parameters.getOrDefault(PARAM_LAUNCH, dp.get(PARAM_LAUNCH)).toLowerCase(Locale.ROOT);
+        boolean isLaunch = sIsLaunch.length() > 0
+                && ("true".startsWith(sIsLaunch) || "yes".startsWith(sIsLaunch));
+        return JdiDefaultExecutionControl.create(env, remoteAgent, isLaunch, host, timeout);
+    }
+
+}
--- a/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -27,7 +27,6 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.concurrent.atomic.AtomicReference;
-import jdk.jshell.spi.ExecutionControl;
 
 /**
  * An implementation of {@link jdk.jshell.spi.ExecutionControl} which executes
@@ -42,15 +41,6 @@
     private ThreadGroup execThreadGroup;
 
     /**
-     * Creates a local ExecutionControl instance.
-     *
-     * @return the generator
-     */
-    public static ExecutionControl.Generator create() {
-        return env -> new LocalExecutionControl();
-    }
-
-    /**
      * Creates an instance, delegating loader operations to the specified
      * delegate.
      *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControlProvider.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, 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 jdk.jshell.execution;
+
+import java.util.Map;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+
+/**
+ * A provider of execution engines which run in the same process as JShell.
+ * @author Robert Field
+ */
+public class LocalExecutionControlProvider implements ExecutionControlProvider{
+
+    /**
+     * Create an instance.  An instance can be used to
+     * {@linkplain  #generate generate} an {@link ExecutionControl} instance
+     * that executes code in the same process.
+     */
+    public LocalExecutionControlProvider() {
+    }
+
+    /**
+     * The unique name of this {@code ExecutionControlProvider}.
+     *
+     * @return "local"
+     */
+    @Override
+    public String name() {
+        return "local";
+    }
+
+    /**
+     * Create and return the default parameter map for
+     * {@code LocalExecutionControlProvider}.
+     * {@code LocalExecutionControlProvider} has no parameters.
+     *
+     * @return an empty parameter map
+     */
+    @Override
+    public Map<String,String> defaultParameters() {
+        return ExecutionControlProvider.super.defaultParameters();
+    }
+
+    /**
+     * Create and return a locally executing {@code ExecutionControl} instance.
+     *
+     * @param env the execution environment, provided by JShell
+     * @param parameters the {@linkplain #defaultParameters()  default} or
+     * modified parameter map.
+     * @return the execution engine
+     */
+    @Override
+    public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) {
+        return new LocalExecutionControl();
+    }
+
+}
--- a/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java	Fri Jan 06 08:42:28 2017 +0000
@@ -64,36 +64,6 @@
     private Util() {}
 
     /**
-     * Create a composite {@link ExecutionControl.Generator} instance that, when
-     * generating, will try each specified generator until successfully creating
-     * an {@link ExecutionControl} instance, or, if all fail, will re-throw the
-     * first exception.
-     *
-     * @param gec0 the first instance to try
-     * @param gecs the second through Nth instance to try
-     * @return the fail-over generator
-     */
-    public static ExecutionControl.Generator failOverExecutionControlGenerator(
-            ExecutionControl.Generator gec0, ExecutionControl.Generator... gecs) {
-        return (ExecutionEnv env) -> {
-            Throwable thrown;
-            try {
-                return gec0.generate(env);
-            } catch (Throwable ex) {
-                thrown = ex;
-            }
-            for (ExecutionControl.Generator gec : gecs) {
-                try {
-                    return gec.generate(env);
-                } catch (Throwable ignore) {
-                    // only care about the first, and only if they all fail
-                }
-            }
-            throw thrown;
-        };
-    }
-
-    /**
      * Forward commands from the input to the specified {@link ExecutionControl}
      * instance, then responses back on the output.
      * @param ec the direct instance of {@link ExecutionControl} to process commands
--- a/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -25,6 +25,11 @@
 package jdk.jshell.spi;
 
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
 
 /**
  * This interface specifies the functionality that must provided to implement a
@@ -40,31 +45,10 @@
  * <p>
  * Methods defined in this interface should only be called by the core JShell
  * implementation.
- * <p>
- * To install an {@code ExecutionControl}, its {@code Generator} is passed to
- * {@link jdk.jshell.JShell.Builder#executionEngine(ExecutionControl.Generator)  }.
  */
 public interface ExecutionControl extends AutoCloseable {
 
     /**
-     * Defines a functional interface for creating {@link ExecutionControl}
-     * instances.
-     */
-    @FunctionalInterface
-    public interface Generator {
-
-        /**
-         * Generates an execution engine, given an execution environment.
-         *
-         * @param env the context in which the {@link ExecutionControl} is to
-         * be created
-         * @return the created instance
-         * @throws Throwable if problems occurred
-         */
-        ExecutionControl generate(ExecutionEnv env) throws Throwable;
-    }
-
-    /**
      * Attempts to load new classes.
      *
      * @param cbcs the class name and bytecodes to load
@@ -176,9 +160,151 @@
      * <p>
      * No calls to methods on this interface should be made after close.
      */
+    @Override
     void close();
 
     /**
+     * Search for a provider, then create and return the
+     * {@code ExecutionControl} instance.
+     *
+     * @param env the execution environment (provided by JShell)
+     * @param name the name of provider
+     * @param parameters the parameter map.
+     * @return the execution engine
+     * @throws Throwable an exception that occurred attempting to find or create
+     * the execution engine.
+     * @throws IllegalArgumentException if no ExecutionControlProvider has the
+     * specified {@code name} and {@code parameters}.
+     */
+    static ExecutionControl generate(ExecutionEnv env, String name, Map<String, String> parameters)
+            throws Throwable {
+        Set<String> keys = parameters == null
+                ? Collections.emptySet()
+                : parameters.keySet();
+        for (ExecutionControlProvider p : ServiceLoader.load(ExecutionControlProvider.class)) {
+            if (p.name().equals(name)
+                && p.defaultParameters().keySet().containsAll(keys)) {
+                return p.generate(env, parameters);
+            }
+        }
+        throw new IllegalArgumentException("No ExecutionControlProvider with name '"
+                + name + "' and parameter keys: " + keys.toString());
+    }
+
+    /**
+     * Search for a provider, then create and return the
+     * {@code ExecutionControl} instance.
+     *
+     * @param env the execution environment (provided by JShell)
+     * @param spec the {@code ExecutionControl} spec, which is described in
+     * the documentation of this
+     * {@linkplain jdk.jshell.spi package documentation}.
+     * @return the execution engine
+     * @throws Throwable an exception that occurred attempting to find or create
+     * the execution engine.
+     * @throws IllegalArgumentException if no ExecutionControlProvider has the
+     * specified {@code name} and {@code parameters}.
+     * @throws IllegalArgumentException if {@code spec} is malformed
+     */
+    static ExecutionControl generate(ExecutionEnv env, String spec)
+            throws Throwable {
+        class SpecReader {
+
+            int len = spec.length();
+            int i = -1;
+
+            char ch;
+
+            SpecReader() {
+                next();
+            }
+
+            boolean more() {
+                return i < len;
+            }
+
+            char current() {
+                return ch;
+            }
+
+            final boolean next() {
+                ++i;
+                if (i < len) {
+                    ch = spec.charAt(i);
+                    return true;
+                }
+                i = len;
+                return false;
+            }
+
+            void skipWhite() {
+                while (more() && Character.isWhitespace(ch)) {
+                    next();
+                }
+            }
+
+            String readId() {
+                skipWhite();
+                StringBuilder sb = new StringBuilder();
+                while (more() && Character.isJavaIdentifierPart(ch)) {
+                    sb.append(ch);
+                    next();
+                }
+                skipWhite();
+                String id = sb.toString();
+                if (id.isEmpty()) {
+                    throw new IllegalArgumentException("Expected identifier in " + spec);
+                }
+                return id;
+            }
+
+            void expect(char exp) {
+                skipWhite();
+                if (!more() || ch != exp) {
+                    throw new IllegalArgumentException("Expected '" + exp + "' in " + spec);
+                }
+                next();
+                skipWhite();
+            }
+
+            String readValue() {
+                expect('(');
+                int parenDepth = 1;
+                StringBuilder sb = new StringBuilder();
+                while (more()) {
+                    if (ch == ')') {
+                        --parenDepth;
+                        if (parenDepth == 0) {
+                            break;
+                        }
+                    } else if (ch == '(') {
+                        ++parenDepth;
+                    }
+                    sb.append(ch);
+                    next();
+                }
+                expect(')');
+                return sb.toString();
+            }
+        }
+        Map<String, String> parameters = new HashMap<>();
+        SpecReader sr = new SpecReader();
+        String name = sr.readId();
+        if (sr.more()) {
+            sr.expect(':');
+            while (sr.more()) {
+                String key = sr.readId();
+                String value = sr.readValue();
+                parameters.put(key, value);
+                if (sr.more()) {
+                    sr.expect(',');
+                }
+            }
+        }
+        return generate(env, name, parameters);
+    }
+
+    /**
      * Bundles class name with class bytecodes.
      */
     public static final class ClassBytecodes implements Serializable {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControlProvider.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, 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 jdk.jshell.spi;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * The provider used by JShell to generate the execution engine needed to
+ * evaluate Snippets.  Alternate execution engines can be created by
+ * implementing this interface, then configuring JShell with the provider or
+ * the providers name and parameter specifier.
+ * @author Robert Field
+ */
+public interface ExecutionControlProvider {
+
+    /**
+     * The unique name of this {@code ExecutionControlProvider}.  The name must
+     * be a sequence of characters from the Basic Multilingual Plane which are
+     * {@link Character#isJavaIdentifierPart(char) }.
+     *
+     * @return the ExecutionControlProvider's name
+     */
+    String name();
+
+    /**
+     * Create and return the default parameter map for this
+     * {@code ExecutionControlProvider}. The map can optionally be modified;
+     * Modified or unmodified it can be passed to
+     * {@link #generate(jdk.jshell.spi.ExecutionEnv, java.util.Map) }.
+     *
+     * @return the default parameter map
+     */
+    default Map<String,String> defaultParameters() {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Create and return the {@code ExecutionControl} instance.
+     *
+     * @param env the execution environment, provided by JShell
+     * @param parameters the {@linkplain #defaultParameters() default} or
+     * modified parameter map.
+     * @return the execution engine
+     * @throws Throwable an exception that occurred attempting to create the
+     * execution engine.
+     */
+    ExecutionControl generate(ExecutionEnv env, Map<String,String> parameters)
+            throws Throwable;
+}
--- a/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionEnv.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionEnv.java	Fri Jan 06 08:42:28 2017 +0000
@@ -28,7 +28,6 @@
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.List;
-import jdk.jshell.JShell;
 
 /**
  * Functionality made available to a pluggable JShell execution engine.  It is
--- a/src/jdk.jshell/share/classes/jdk/jshell/spi/package-info.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/package-info.java	Fri Jan 06 08:42:28 2017 +0000
@@ -30,9 +30,41 @@
  * and in the case of executable Snippets, execution.  The JShell
  * implementation includes a default execution engine (currently a remote
  * process which is JDI controlled).  By implementing the
- * {@link jdk.jshell.spi.ExecutionControl} interface and installing it with
- * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl.Generator) }
- * other execution engines can be used.
+ * {@link jdk.jshell.spi.ExecutionControl} interface and its generating class,
+ * an implementation of the {@link jdk.jshell.spi.ExecutionControlProvider}
+ * interface, and installing it with
+ * {@link jdk.jshell.JShell.Builder#executionEngine(String)}
+ * other execution engines can be used. Where the passed String is an
+ * {@code ExecutionControl} spec.
+ * <p>
+ * The {@code ExecutionControl} spec is the {@code ExecutionControlProvider}
+ * name optionally followed by a parameter specification.
+ * The syntax of the spec is:
+ * <pre>
+ * spec   := name : params
+ *        | name
+ * name   := identifier
+ * params := param , params
+ *        | param
+ *        |
+ * param  := identifier ( value )
+ * </pre>
+ * Where 'name' is the {@code ExecutionControlProvider}
+ * {@linkplain ExecutionControlProvider#name() name}.
+ * Where 'param' is a Map key from
+ * {@link ExecutionControlProvider#defaultParameters()} and the parenthesized
+ * value; See, for example,
+ * {@link jdk.jshell.execution.JdiExecutionControlProvider}.
+ * Where 'identifier' is a sequence of
+ * {@linkplain java.lang.Character#isJavaIdentifierPart(char)
+ * Java identifier part characters} from the Basic Multilingual Plane.
+ * <p>
+ * For example:
+ * <ul>
+ *   <li>local</li>
+ *   <li>jdi:hostname(localhost)</li>
+ *   <li>failover:1(jdi),2(jdi:launch(true),timeout(3000)),3(local)</li>
+ * </ul>
  *
  * @see jdk.jshell.execution for execution implementation support
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/DEFAULT.jsh	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,11 @@
+import java.io.*;
+import java.math.*;
+import java.net.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+import java.util.prefs.*;
+import java.util.regex.*;
+import java.util.stream.*;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/JAVASE.jsh	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,173 @@
+import java.applet.*;
+import java.awt.*;
+import java.awt.color.*;
+import java.awt.datatransfer.*;
+import java.awt.desktop.*;
+import java.awt.dnd.*;
+import java.awt.event.*;
+import java.awt.font.*;
+import java.awt.geom.*;
+import java.awt.im.*;
+import java.awt.im.spi.*;
+import java.awt.image.*;
+import java.awt.image.renderable.*;
+import java.awt.print.*;
+import java.beans.*;
+import java.beans.beancontext.*;
+import java.io.*;
+import java.lang.*;
+import java.lang.annotation.*;
+import java.lang.instrument.*;
+import java.lang.invoke.*;
+import java.lang.management.*;
+import java.lang.module.*;
+import java.lang.ref.*;
+import java.lang.reflect.*;
+import java.math.*;
+import java.net.*;
+import java.net.spi.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.*;
+import java.nio.charset.*;
+import java.nio.charset.spi.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.*;
+import java.rmi.*;
+import java.rmi.activation.*;
+import java.rmi.dgc.*;
+import java.rmi.registry.*;
+import java.rmi.server.*;
+import java.security.*;
+import java.security.acl.*;
+import java.security.cert.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.sql.*;
+import java.text.*;
+import java.text.spi.*;
+import java.time.*;
+import java.time.chrono.*;
+import java.time.format.*;
+import java.time.temporal.*;
+import java.time.zone.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.concurrent.locks.*;
+import java.util.function.*;
+import java.util.jar.*;
+import java.util.logging.*;
+import java.util.prefs.*;
+import java.util.regex.*;
+import java.util.spi.*;
+import java.util.stream.*;
+import java.util.zip.*;
+import javax.accessibility.*;
+import javax.annotation.processing.*;
+import javax.crypto.*;
+import javax.crypto.interfaces.*;
+import javax.crypto.spec.*;
+import javax.imageio.*;
+import javax.imageio.event.*;
+import javax.imageio.metadata.*;
+import javax.imageio.plugins.bmp.*;
+import javax.imageio.plugins.jpeg.*;
+import javax.imageio.plugins.tiff.*;
+import javax.imageio.spi.*;
+import javax.imageio.stream.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+import javax.management.*;
+import javax.management.loading.*;
+import javax.management.modelmbean.*;
+import javax.management.monitor.*;
+import javax.management.openmbean.*;
+import javax.management.relation.*;
+import javax.management.remote.*;
+import javax.management.remote.rmi.*;
+import javax.management.timer.*;
+import javax.naming.*;
+import javax.naming.directory.*;
+import javax.naming.event.*;
+import javax.naming.ldap.*;
+import javax.naming.spi.*;
+import javax.net.*;
+import javax.net.ssl.*;
+import javax.print.*;
+import javax.print.attribute.*;
+import javax.print.attribute.standard.*;
+import javax.print.event.*;
+import javax.rmi.ssl.*;
+import javax.script.*;
+import javax.security.auth.*;
+import javax.security.auth.callback.*;
+import javax.security.auth.kerberos.*;
+import javax.security.auth.login.*;
+import javax.security.auth.spi.*;
+import javax.security.auth.x500.*;
+import javax.security.cert.*;
+import javax.security.sasl.*;
+import javax.sound.midi.*;
+import javax.sound.midi.spi.*;
+import javax.sound.sampled.*;
+import javax.sound.sampled.spi.*;
+import javax.sql.*;
+import javax.sql.rowset.*;
+import javax.sql.rowset.serial.*;
+import javax.sql.rowset.spi.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.colorchooser.*;
+import javax.swing.event.*;
+import javax.swing.filechooser.*;
+import javax.swing.plaf.*;
+import javax.swing.plaf.basic.*;
+import javax.swing.plaf.metal.*;
+import javax.swing.plaf.multi.*;
+import javax.swing.plaf.nimbus.*;
+import javax.swing.plaf.synth.*;
+import javax.swing.table.*;
+import javax.swing.text.*;
+import javax.swing.text.html.*;
+import javax.swing.text.html.parser.*;
+import javax.swing.text.rtf.*;
+import javax.swing.tree.*;
+import javax.swing.undo.*;
+import javax.tools.*;
+import javax.transaction.xa.*;
+import javax.xml.*;
+import javax.xml.catalog.*;
+import javax.xml.crypto.*;
+import javax.xml.crypto.dom.*;
+import javax.xml.crypto.dsig.*;
+import javax.xml.crypto.dsig.dom.*;
+import javax.xml.crypto.dsig.keyinfo.*;
+import javax.xml.crypto.dsig.spec.*;
+import javax.xml.datatype.*;
+import javax.xml.namespace.*;
+import javax.xml.parsers.*;
+import javax.xml.stream.*;
+import javax.xml.stream.events.*;
+import javax.xml.stream.util.*;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.sax.*;
+import javax.xml.transform.stax.*;
+import javax.xml.transform.stream.*;
+import javax.xml.validation.*;
+import javax.xml.xpath.*;
+import org.ietf.jgss.*;
+import org.w3c.dom.*;
+import org.w3c.dom.bootstrap.*;
+import org.w3c.dom.events.*;
+import org.w3c.dom.ls.*;
+import org.w3c.dom.ranges.*;
+import org.w3c.dom.traversal.*;
+import org.w3c.dom.views.*;
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+import org.xml.sax.helpers.*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/PRINTING.jsh	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,21 @@
+void print(boolean b) { System.out.print(b); }
+void print(char c) { System.out.print(c); }
+void print(int i) { System.out.print(i); }
+void print(long l) { System.out.print(l); }
+void print(float f) { System.out.print(f); }
+void print(double d) { System.out.print(d); }
+void print(char s[]) { System.out.print(s); }
+void print(String s) { System.out.print(s); }
+void print(Object obj) { System.out.print(obj); }
+void println() { System.out.println(); }
+void println(boolean b) { System.out.println(b); }
+void println(char c) { System.out.println(c); }
+void println(int i) { System.out.println(i); }
+void println(long l) { System.out.println(l); }
+void println(float f) { System.out.println(f); }
+void println(double d) { System.out.println(d); }
+void println(char s[]) { System.out.println(s); }
+void println(String s) { System.out.println(s); }
+void println(Object obj) { System.out.println(obj); }
+void printf(Locale l, String format, Object... args) { System.out.printf(l, format, args); }
+void printf(String format, Object... args) { System.out.printf(format, args); }
--- a/src/jdk.jshell/share/classes/module-info.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/src/jdk.jshell/share/classes/module-info.java	Fri Jan 06 08:42:28 2017 +0000
@@ -56,6 +56,7 @@
     requires transitive java.compiler;
     requires transitive jdk.jdi;
     requires transitive java.prefs;
+    requires java.logging;
     requires jdk.compiler;
     requires jdk.internal.le;
     requires jdk.internal.ed;
@@ -66,7 +67,14 @@
     exports jdk.jshell.execution;
     exports jdk.jshell.tool;
 
+    uses jdk.jshell.spi.ExecutionControlProvider;
     uses jdk.internal.editor.spi.BuildInEditorProvider;
 
     provides javax.tools.Tool with jdk.internal.jshell.tool.JShellToolProvider;
+    provides jdk.jshell.spi.ExecutionControlProvider
+        with jdk.jshell.execution.JdiExecutionControlProvider;
+    provides jdk.jshell.spi.ExecutionControlProvider
+        with jdk.jshell.execution.LocalExecutionControlProvider;
+    provides jdk.jshell.spi.ExecutionControlProvider
+        with jdk.jshell.execution.FailOverExecutionControlProvider;
 }
--- a/test/Makefile	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/Makefile	Fri Jan 06 08:42:28 2017 +0000
@@ -244,7 +244,7 @@
 EXIT_IF_FATAL = status=$$?; if [ $$status -ge $(1) ]; then exit $$status ; fi
 
 # Root of all test results
-TEST_OUTPUT_DIR = $(TEST_ROOT)/../build/$(PLATFORM)-$(ARCH)/test/langtools
+TEST_OUTPUT_DIR ?= $(TEST_ROOT)/../build/$(PLATFORM)-$(ARCH)/test/langtools
 ABS_TEST_OUTPUT_DIR := \
 	$(shell mkdir -p $(TEST_OUTPUT_DIR); \
 		cd  $(TEST_OUTPUT_DIR); \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/BadExecutionControlSpecTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8168615
+ * @summary Test bad input to ExecutionControl.generate
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.jdeps/com.sun.tools.javap
+ *          jdk.jshell/jdk.internal.jshell.tool
+ * @run testng BadExecutionControlSpecTest
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.List;
+import org.testng.annotations.Test;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionEnv;
+import static org.testng.Assert.fail;
+
+@Test
+public class BadExecutionControlSpecTest {
+    private static void assertIllegal(String spec) throws Throwable {
+        try {
+            ExecutionEnv env = new ExecutionEnv() {
+                @Override
+                public InputStream userIn() {
+                    return new ByteArrayInputStream(new byte[0]);
+                }
+
+                @Override
+                public PrintStream userOut() {
+                    return new PrintStream(new ByteArrayOutputStream());
+                }
+
+                @Override
+                public PrintStream userErr() {
+                    return new PrintStream(new ByteArrayOutputStream());
+                }
+
+                @Override
+                public List<String> extraRemoteVMOptions() {
+                    return Collections.emptyList();
+                }
+
+                @Override
+                public void closeDown() {
+                }
+
+            };
+            ExecutionControl.generate(env, spec);
+            fail("Expected exception -- " + spec);
+        } catch (IllegalArgumentException ex) {
+            // The expected happened
+        }
+    }
+
+    public void syntaxTest() throws Throwable {
+        assertIllegal(":launch(true)");
+        assertIllegal("jdi:launch(true");
+        assertIllegal("jdi:launch(true)$");
+        assertIllegal("jdi:,");
+    }
+
+    public void notFoundTest() throws Throwable {
+        assertIllegal("fruitbats");
+        assertIllegal("jdi:baz(true)");
+        assertIllegal("random:launch(true)");
+        assertIllegal("jdi:,");
+    }
+}
--- a/test/jdk/jshell/DyingRemoteAgent.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/DyingRemoteAgent.java	Fri Jan 06 08:42:28 2017 +0000
@@ -21,10 +21,11 @@
  * questions.
  */
 
+import java.util.Map;
 import jdk.jshell.JShell;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
+import jdk.jshell.execution.JdiExecutionControlProvider;
 import jdk.jshell.execution.RemoteExecutionControl;
-import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout;
+import jdk.jshell.spi.ExecutionControlProvider;
 
 class DyingRemoteAgent extends RemoteExecutionControl {
 
@@ -39,11 +40,13 @@
     }
 
     static JShell state(boolean isLaunch, String host) {
-        return JShell.builder().executionEngine(
-                JdiDefaultExecutionControl.create(
-                        DyingRemoteAgent.class.getName(),
-                        isLaunch,
-                        host,
-                        defaultTimeout())).build();
+        ExecutionControlProvider ecp = new JdiExecutionControlProvider();
+        Map<String,String> pm = ecp.defaultParameters();
+        pm.put(JdiExecutionControlProvider.PARAM_REMOTE_AGENT, DyingRemoteAgent.class.getName());
+        pm.put(JdiExecutionControlProvider.PARAM_HOST_NAME, host==null? "" : host);
+        pm.put(JdiExecutionControlProvider.PARAM_LAUNCH, ""+isLaunch);
+        return JShell.builder()
+                .executionEngine(ecp, pm)
+                .build();
     }
 }
--- a/test/jdk/jshell/EditorTestBase.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/EditorTestBase.java	Fri Jan 06 08:42:28 2017 +0000
@@ -219,7 +219,7 @@
     public void testStartup() {
         testEditor(true, new String[0],
                 a -> assertEditInput(a, "/ed", s -> assertTrue(s.isEmpty(), "Checking of startup: " + s), this::cancel),
-                a -> assertEditInput(a, "/ed printf", assertStartsWith("void printf"), this::cancel));
+                a -> assertEditInput(a, "/ed s1", assertStartsWith("import"), this::cancel));
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/ExecutionControlSpecTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8168615
+ * @summary Test ExecutionControlProvider specs can load user ExecutionControlProviders
+ * with direct maps.
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.jdeps/com.sun.tools.javap
+ *          jdk.jshell/jdk.jshell.execution
+ *          jdk.jshell/jdk.jshell.spi
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
+ * @build KullaTesting Compiler
+ * @run testng ExecutionControlSpecTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+
+public class ExecutionControlSpecTest extends KullaTesting {
+
+    ClassLoader ccl;
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        String mod = "my.ec";
+        String pkg = "package my.ec;\n";
+        Compiler compiler = new Compiler();
+        Path modDir = Paths.get("mod");
+        compiler.compile(modDir,
+                pkg +
+                "public class PrefixingExecutionControl extends jdk.jshell.execution.LocalExecutionControl {\n" +
+                "    @Override\n" +
+                "    public String invoke(String className, String methodName)\n" +
+                "            throws RunException, InternalException, EngineTerminationException {\n" +
+                "        return \"Blah:\" + super.invoke(className, methodName);\n" +
+                "    }\n" +
+                "}\n",
+                pkg +
+                "public class PrefixingExecutionControlProvider implements jdk.jshell.spi.ExecutionControlProvider {\n" +
+                "    @Override\n" +
+                "    public String name() {\n" +
+                "        return \"prefixing\";\n" +
+                "    }\n" +
+                "    @Override\n" +
+                "    public jdk.jshell.spi.ExecutionControl generate(jdk.jshell.spi.ExecutionEnv env,\n" +
+                "            java.util.Map<String, String> parameters) {\n" +
+                "        return new PrefixingExecutionControl();\n" +
+                "    }\n" +
+                "}\n",
+                "module my.ec {\n" +
+                "    requires transitive jdk.jshell;\n" +
+                "    provides jdk.jshell.spi.ExecutionControlProvider\n" +
+                "        with my.ec.PrefixingExecutionControlProvider;\n" +
+                " }");
+        Path modPath = compiler.getPath(modDir);
+        ccl = createAndRunFromModule(mod, modPath);
+
+        setUp(builder -> builder.executionEngine("prefixing"));
+    }
+
+    @AfterMethod
+    @Override
+    public void tearDown() {
+        super.tearDown();
+        Thread.currentThread().setContextClassLoader(ccl);
+    }
+
+    @Test
+    public void testPrefix() {
+        assertEval("43;", "Blah:43");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/FailOverDirectExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8168615
+ * @summary Test that fail-over works for fail-over ExecutionControlProvider
+ * with direct maps.
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.jdeps/com.sun.tools.javap
+ *          jdk.jshell/jdk.jshell.execution
+ *          jdk.jshell/jdk.jshell.spi
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
+ * @build KullaTesting ExecutionControlTestBase Compiler
+ * @run testng FailOverDirectExecutionControlTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.execution.FailOverExecutionControlProvider;
+import jdk.jshell.spi.ExecutionControlProvider;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class FailOverDirectExecutionControlTest extends ExecutionControlTestBase {
+
+    ClassLoader ccl;
+    ExecutionControlProvider provider;
+    Map<Level, List<String>> logged = new HashMap<>();
+
+    private class LogTestHandler extends Handler {
+
+        LogTestHandler() {
+            setLevel(Level.ALL);
+            setFilter(lr -> lr.getLoggerName().equals("jdk.jshell.execution"));
+        }
+
+        @Override
+        public void publish(LogRecord lr) {
+            List<String> l = logged.get(lr.getLevel());
+            if (l == null) {
+                l = new ArrayList<>();
+                logged.put(lr.getLevel(), l);
+            }
+            l.add(lr.getMessage());
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        Logger logger = Logger.getLogger("jdk.jshell.execution");
+        logger.setLevel(Level.ALL);
+        logger.addHandler(new LogTestHandler());
+        Compiler compiler = new Compiler();
+        Path modDir = Paths.get("mod");
+        compiler.compile(modDir,
+                "package my.provide; import java.util.Map;\n" +
+                "import jdk.jshell.spi.ExecutionControl;\n" +
+                "import jdk.jshell.spi.ExecutionControlProvider;\n" +
+                "import jdk.jshell.spi.ExecutionEnv;\n" +
+                "public class AlwaysFailingProvider implements ExecutionControlProvider {\n" +
+                "    @Override\n" +
+                "    public String name() {\n" +
+                "        return \"alwaysFailing\";\n" +
+                "    }\n" +
+                "    @Override\n" +
+                "    public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable {\n" +
+                "        throw new UnsupportedOperationException(\"This operation intentionally broken.\");\n" +
+                "    }\n" +
+                "}\n",
+                "module my.provide {\n" +
+                "    requires transitive jdk.jshell;\n" +
+                "    provides jdk.jshell.spi.ExecutionControlProvider\n" +
+                "        with my.provide.AlwaysFailingProvider;\n" +
+                " }");
+        Path modPath = compiler.getPath(modDir);
+        ccl = createAndRunFromModule("my.provide", modPath);
+
+        provider = new FailOverExecutionControlProvider();
+        Map<String, String> pm = provider.defaultParameters();
+        pm.put("0", "alwaysFailing");
+        pm.put("1", "alwaysFailing");
+        pm.put("2", "jdi");
+        setUp(builder -> builder.executionEngine(provider, pm));
+    }
+
+    @AfterMethod
+    @Override
+    public void tearDown() {
+        super.tearDown();
+        Thread.currentThread().setContextClassLoader(ccl);
+    }
+
+    @Override
+    public void variables() {
+        super.variables();
+        assertEquals(logged.get(Level.FINEST).size(), 1);
+        assertEquals(logged.get(Level.FINE).size(), 2);
+        assertEquals(logged.get(Level.WARNING).size(), 2);
+        assertNull(logged.get(Level.SEVERE));
+        String log = logged.get(Level.WARNING).get(0);
+        assertTrue(log.contains("Failure failover -- 0 = alwaysFailing"), log);
+        assertTrue(log.contains("This operation intentionally broken"), log);
+        log = logged.get(Level.WARNING).get(1);
+        assertTrue(log.contains("Failure failover -- 1 = alwaysFailing"), log);
+        assertTrue(log.contains("This operation intentionally broken"), log);
+        log = logged.get(Level.FINEST).get(0);
+        assertTrue(log.contains("Success failover -- 2 = jdi"), log);
+    }
+}
--- a/test/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8131029 8160127 8159935 8169519
+ * @bug 8131029 8160127 8159935 8169519 8168615
  * @summary Test that fail-over works for fail-over ExecutionControl generators.
  * @modules jdk.jshell/jdk.jshell.execution
  *          jdk.jshell/jdk.jshell.spi
@@ -33,9 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
-import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout;
-import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
 
 @Test
 public class FailOverExecutionControlDyingLaunchTest extends ExecutionControlTestBase {
@@ -43,12 +40,7 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
-                JdiDefaultExecutionControl.create(
-                        DyingRemoteAgent.class.getName(),
-                        true,
-                        null,
-                        defaultTimeout()),
-                JdiDefaultExecutionControl.launch())));
+        setUp(builder -> builder.executionEngine(
+                "failover:0(jdi:remoteAgent(DyingRemoteAgent),launch(true)), 4(jdi:launch(true))"));
     }
 }
--- a/test/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -33,9 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
-import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout;
-import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
 
 @Test
 public class FailOverExecutionControlHangingLaunchTest extends ExecutionControlTestBase {
@@ -43,12 +40,7 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
-                JdiDefaultExecutionControl.create(
-                        HangingRemoteAgent.class.getName(),
-                        true,
-                        null,
-                        defaultTimeout()),
-                JdiDefaultExecutionControl.launch())));
+        setUp(builder -> builder.executionEngine(
+                "failover:0(jdi:remoteAgent(HangingRemoteAgent),launch(true)), 1(jdi:launch(true))"));
     }
 }
--- a/test/jdk/jshell/FailOverExecutionControlHangingListenTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/FailOverExecutionControlHangingListenTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -34,9 +34,6 @@
 import java.net.InetAddress;
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
-import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout;
-import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
 
 @Test
 public class FailOverExecutionControlHangingListenTest extends ExecutionControlTestBase {
@@ -45,12 +42,8 @@
     @Override
     public void setUp() {
         String loopback = InetAddress.getLoopbackAddress().getHostAddress();
-        setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
-                JdiDefaultExecutionControl.create(
-                        HangingRemoteAgent.class.getName(),
-                        false,
-                        loopback,
-                        defaultTimeout()),
-                JdiDefaultExecutionControl.listen(loopback))));
+         setUp(builder -> builder.executionEngine(
+                "failover:0(jdi:remoteAgent(HangingRemoteAgent),hostname(" + loopback + ")),"
+                 + "1(jdi:hostname(" + loopback + "))"));
     }
 }
--- a/test/jdk/jshell/FailOverExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/FailOverExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8131029 8160127 8159935
+ * @bug 8131029 8160127 8159935 8168615
  * @summary Test that fail-over works for fail-over ExecutionControl generators.
  * @modules jdk.jshell/jdk.jshell.execution
  *          jdk.jshell/jdk.jshell.spi
@@ -33,10 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
-import jdk.jshell.spi.ExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
-import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
 
 @Test
 public class FailOverExecutionControlTest extends ExecutionControlTestBase {
@@ -44,18 +40,7 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
-                new AlwaysFailingGenerator(),
-                new AlwaysFailingGenerator(),
-                JdiDefaultExecutionControl.launch())));
+        setUp(builder -> builder.executionEngine("failover:0(nonExistent), 1(nonExistent), 2(jdi:launch(true))"));
     }
 
-    class AlwaysFailingGenerator implements ExecutionControl.Generator {
-
-        @Override
-        public ExecutionControl generate(ExecutionEnv env) throws UnsupportedOperationException {
-            throw new UnsupportedOperationException("This operation intentionally broken.");
-        }
-
-    }
 }
--- a/test/jdk/jshell/HangingRemoteAgent.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/HangingRemoteAgent.java	Fri Jan 06 08:42:28 2017 +0000
@@ -21,9 +21,11 @@
  * questions.
  */
 
+import java.util.Map;
 import jdk.jshell.JShell;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
+import jdk.jshell.execution.JdiExecutionControlProvider;
 import jdk.jshell.execution.RemoteExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
 
 /**
  * Hang for three minutes (long enough to cause a timeout).
@@ -51,12 +53,15 @@
     }
 
     static JShell state(boolean isLaunch, String host) {
-        return JShell.builder().executionEngine(
-                JdiDefaultExecutionControl.create(
-                        HangingRemoteAgent.class.getName(),
-                        isLaunch,
-                        host,
-                        TIMEOUT)).build();
+        ExecutionControlProvider ecp = new JdiExecutionControlProvider();
+        Map<String,String> pm = ecp.defaultParameters();
+        pm.put(JdiExecutionControlProvider.PARAM_REMOTE_AGENT, HangingRemoteAgent.class.getName());
+        pm.put(JdiExecutionControlProvider.PARAM_HOST_NAME, host==null? "" : host);
+        pm.put(JdiExecutionControlProvider.PARAM_LAUNCH, ""+isLaunch);
+        pm.put(JdiExecutionControlProvider.PARAM_TIMEOUT, ""+TIMEOUT);
+        return JShell.builder()
+                .executionEngine(ecp, pm)
+                .build();
     }
 
 }
--- a/test/jdk/jshell/JdiBadOptionLaunchExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiBadOptionLaunchExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -31,7 +31,6 @@
 
 import org.testng.annotations.Test;
 import jdk.jshell.JShell;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -44,7 +43,7 @@
     public void badOptionLaunchTest() {
         try {
             JShell.builder()
-                    .executionEngine(JdiDefaultExecutionControl.launch())
+                    .executionEngine("jdi:launch(true)")
                     .remoteVMOptions("-BadBadOption")
                     .build();
         } catch (IllegalStateException ex) {
--- a/test/jdk/jshell/JdiBadOptionListenExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiBadOptionListenExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -31,7 +31,6 @@
 
 import org.testng.annotations.Test;
 import jdk.jshell.JShell;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -44,7 +43,7 @@
     public void badOptionListenTest() {
         try {
             JShell.builder()
-                    .executionEngine(JdiDefaultExecutionControl.listen(null))
+                    .executionEngine("jdi")
                     .remoteVMOptions("-BadBadOption")
                     .build();
         } catch (IllegalStateException ex) {
--- a/test/jdk/jshell/JdiBogusHostListenExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiBogusHostListenExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8169519
+ * @bug 8169519 8168615
  * @summary Tests for JDI connector failure
  * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution
  * @run testng JdiBogusHostListenExecutionControlTest
@@ -31,7 +31,6 @@
 
 import org.testng.annotations.Test;
 import jdk.jshell.JShell;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -44,7 +43,7 @@
     public void badOptionListenTest() {
         try {
             JShell.builder()
-                    .executionEngine(JdiDefaultExecutionControl.listen("BattyRumbleBuckets-Snurfle-99-Blip"))
+                    .executionEngine("jdi:hostname(BattyRumbleBuckets-Snurfle-99-Blip)")
                     .build();
         } catch (IllegalStateException ex) {
             assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage());
--- a/test/jdk/jshell/JdiLaunchingExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiLaunchingExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8164518
+ * @bug 8164518 8168615
  * @summary Tests for standard JDI connector (without failover) -- launching
  * @modules jdk.jshell/jdk.jshell.execution
  * @build KullaTesting ExecutionControlTestBase
@@ -33,7 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 
 @Test
 public class JdiLaunchingExecutionControlTest extends ExecutionControlTestBase {
@@ -41,6 +40,6 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.launch()));
+        setUp(builder -> builder.executionEngine("jdi:launch(true)"));
     }
 }
--- a/test/jdk/jshell/JdiListeningExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiListeningExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -33,7 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 
 @Test
 public class JdiListeningExecutionControlTest extends ExecutionControlTestBase {
@@ -41,6 +40,6 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.listen(null)));
+        setUp(builder -> builder.executionEngine("jdi"));
     }
 }
--- a/test/jdk/jshell/JdiListeningLocalhostExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/JdiListeningLocalhostExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -33,7 +33,6 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JdiDefaultExecutionControl;
 
 @Test
 public class JdiListeningLocalhostExecutionControlTest extends ExecutionControlTestBase {
@@ -41,6 +40,6 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.listen("localhost")));
+        setUp(builder -> builder.executionEngine("jdi:hostname(localhost)"));
     }
 }
--- a/test/jdk/jshell/KullaTesting.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/KullaTesting.java	Fri Jan 06 08:42:28 2017 +0000
@@ -28,6 +28,10 @@
 import java.io.PrintStream;
 import java.io.StringWriter;
 import java.lang.reflect.Method;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.reflect.Layer;
+import java.nio.file.Paths;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -209,6 +213,19 @@
         classpath = null;
     }
 
+    public ClassLoader createAndRunFromModule(String moduleName, Path modPath) {
+        ModuleFinder finder = ModuleFinder.of(modPath);
+        Layer parent = Layer.boot();
+        Configuration cf = parent.configuration()
+                .resolveRequires(finder, ModuleFinder.of(), Set.of(moduleName));
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Layer layer = parent.defineModulesWithOneLoader(cf, scl);
+        ClassLoader loader = layer.findLoader(moduleName);
+        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(loader);
+        return ccl;
+    }
+
     public List<String> assertUnresolvedDependencies(DeclarationSnippet key, int unresolvedSize) {
         List<String> unresolved = getState().unresolvedDependencies(key).collect(toList());
         assertEquals(unresolved.size(), unresolvedSize, "Input: " + key.source() + ", checking unresolved: ");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/MyExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.execution.JdiExecutionControl;
+import jdk.jshell.execution.JdiInitiator;
+import jdk.jshell.execution.Util;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionEnv;
+import static org.testng.Assert.fail;
+import static jdk.jshell.execution.Util.remoteInputOutput;
+
+class MyExecutionControl extends JdiExecutionControl {
+
+    private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName();
+    private static final int TIMEOUT = 2000;
+
+    private VirtualMachine vm;
+    private Process process;
+
+    /**
+     * Creates an ExecutionControl instance based on a JDI
+     * {@code ListeningConnector} or {@code LaunchingConnector}.
+     *
+     * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
+     * commands and results. This socket also transports the user
+     * input/output/error.
+     *
+     * @param env the context passed by
+         * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
+     * @return the channel
+     * @throws IOException if there are errors in set-up
+     */
+    static ExecutionControl make(ExecutionEnv env, UserJdiUserRemoteTest test) throws IOException {
+        try (final ServerSocket listener = new ServerSocket(0)) {
+            // timeout for socket
+            listener.setSoTimeout(TIMEOUT);
+            int port = listener.getLocalPort();
+
+            // Set-up the JDI connection
+            List<String> opts = new ArrayList<>(env.extraRemoteVMOptions());
+            opts.add("-classpath");
+            opts.add(System.getProperty("java.class.path")
+                    + System.getProperty("path.separator")
+                    + System.getProperty("user.dir"));
+            JdiInitiator jdii = new JdiInitiator(port,
+                    opts, REMOTE_AGENT, true, null, TIMEOUT);
+            VirtualMachine vm = jdii.vm();
+            Process process = jdii.process();
+
+            List<Consumer<String>> deathListeners = new ArrayList<>();
+            deathListeners.add(s -> env.closeDown());
+            Util.detectJdiExitEvent(vm, s -> {
+                for (Consumer<String> h : deathListeners) {
+                    h.accept(s);
+                }
+            });
+
+            // Set-up the commands/reslts on the socket.  Piggy-back snippet
+            // output.
+            Socket socket = listener.accept();
+            // out before in -- match remote creation so we don't hang
+            OutputStream out = socket.getOutputStream();
+            Map<String, OutputStream> outputs = new HashMap<>();
+            outputs.put("out", env.userOut());
+            outputs.put("err", env.userErr());
+            outputs.put("aux", test.auxStream);
+            Map<String, InputStream> input = new HashMap<>();
+            input.put("in", env.userIn());
+            ExecutionControl myec = remoteInputOutput(socket.getInputStream(),
+                    out, outputs, input,
+                    (objIn, objOut) -> new MyExecutionControl(objOut, objIn, vm, process, deathListeners));
+            test.currentEC = myec;
+            return myec;
+        }
+    }
+
+    /**
+     * Create an instance.
+     *
+     * @param out the output for commands
+     * @param in the input for responses
+     */
+    private MyExecutionControl(ObjectOutput out, ObjectInput in,
+            VirtualMachine vm, Process process,
+            List<Consumer<String>> deathListeners) {
+        super(out, in);
+        this.vm = vm;
+        this.process = process;
+        deathListeners.add(s -> disposeVM());
+    }
+
+    @Override
+    public void close() {
+        super.close();
+        disposeVM();
+    }
+
+    private synchronized void disposeVM() {
+        try {
+            if (vm != null) {
+                vm.dispose(); // This could NPE, so it is caught below
+                vm = null;
+            }
+        } catch (VMDisconnectedException ex) {
+            // Ignore if already closed
+        } catch (Throwable e) {
+            fail("disposeVM threw: " + e);
+        } finally {
+            if (process != null) {
+                process.destroy();
+                process = null;
+            }
+        }
+    }
+
+    @Override
+    protected synchronized VirtualMachine vm() throws EngineTerminationException {
+        if (vm == null) {
+            throw new EngineTerminationException("VM closed");
+        } else {
+            return vm;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/MyExecutionControlProvider.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+import java.util.Map;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+
+public class MyExecutionControlProvider implements ExecutionControlProvider {
+
+    private final UserJdiUserRemoteTest test;
+
+    MyExecutionControlProvider(UserJdiUserRemoteTest test) {
+        this.test = test;
+    }
+
+    @Override
+    public String name() {
+        return "my";
+    }
+
+    @Override
+    public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable {
+        return MyExecutionControl.make(env, test);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/MyRemoteExecutionControl.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import jdk.jshell.execution.DirectExecutionControl;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.InternalException;
+import jdk.jshell.spi.ExecutionControl.RunException;
+import static jdk.jshell.execution.Util.forwardExecutionControlAndIO;
+
+/**
+ * A custom remote agent to verify aux channel and custom ExecutionControl.
+ */
+public class MyRemoteExecutionControl extends DirectExecutionControl implements ExecutionControl {
+
+    static PrintStream auxPrint;
+
+    /**
+     * Launch the agent, connecting to the JShell-core over the socket specified
+     * in the command-line argument.
+     *
+     * @param args standard command-line arguments, expectation is the socket
+     * number is the only argument
+     * @throws Exception any unexpected exception
+     */
+    public static void main(String[] args) throws Exception {
+        try {
+            String loopBack = null;
+            Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
+            InputStream inStream = socket.getInputStream();
+            OutputStream outStream = socket.getOutputStream();
+            Map<String, Consumer<OutputStream>> outputs = new HashMap<>();
+            outputs.put("out", st -> System.setOut(new PrintStream(st, true)));
+            outputs.put("err", st -> System.setErr(new PrintStream(st, true)));
+            outputs.put("aux", st -> { auxPrint = new PrintStream(st, true); });
+            Map<String, Consumer<InputStream>> input = new HashMap<>();
+            input.put("in", st -> System.setIn(st));
+            forwardExecutionControlAndIO(new MyRemoteExecutionControl(), inStream, outStream, outputs, input);
+        } catch (Throwable ex) {
+            throw ex;
+        }
+    }
+
+    @Override
+    public String varValue(String className, String varName)
+            throws RunException, EngineTerminationException, InternalException {
+        auxPrint.print(varName);
+        return super.varValue(className, varName);
+    }
+
+    @Override
+    public Object extensionCommand(String className, Object arg)
+            throws RunException, EngineTerminationException, InternalException {
+        if (!arg.equals("test")) {
+            throw new InternalException("expected extensionCommand arg to be 'test' got: " + arg);
+        }
+        return "ribbit";
+    }
+
+}
--- a/test/jdk/jshell/ReplToolTesting.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/ReplToolTesting.java	Fri Jan 06 08:42:28 2017 +0000
@@ -25,6 +25,7 @@
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -54,22 +55,45 @@
 
     private final static String DEFAULT_STARTUP_MESSAGE = "|  Welcome to";
     final static List<ImportInfo> START_UP_IMPORTS = Stream.of(
-                    "java.util.*",
                     "java.io.*",
                     "java.math.*",
                     "java.net.*",
+                    "java.nio.file.*",
+                    "java.util.*",
                     "java.util.concurrent.*",
+                    "java.util.function.*",
                     "java.util.prefs.*",
-                    "java.util.regex.*")
+                    "java.util.regex.*",
+                    "java.util.stream.*")
                     .map(s -> new ImportInfo("import " + s + ";", "", s))
                     .collect(toList());
-    final static List<MethodInfo> START_UP_METHODS = Stream.of(
-                    new MethodInfo("void printf(String format, Object... args) { System.out.printf(format, args); }",
-                            "(String,Object...)void", "printf"))
+    final static List<MethodInfo> START_UP_METHODS = Stream.<MethodInfo>of()
                     .collect(toList());
-    final static List<String> START_UP_CMD_METHOD = Stream.of(
-                    "|    printf (String,Object...)void")
+    final static List<String> START_UP_CMD_METHOD = Stream.<String>of()
                     .collect(toList());
+    final static List<String> PRINTING_CMD_METHOD = Stream.of(
+            "|    print (boolean)void",
+            "|    print (char)void",
+            "|    print (int)void",
+            "|    print (long)void",
+            "|    print (float)void",
+            "|    print (double)void",
+            "|    print (char s[])void",
+            "|    print (String)void",
+            "|    print (Object)void",
+            "|    println ()void",
+            "|    println (boolean)void",
+            "|    println (char)void",
+            "|    println (int)void",
+            "|    println (long)void",
+            "|    println (float)void",
+            "|    println (double)void",
+            "|    println (char s[])void",
+            "|    println (String)void",
+            "|    println (Object)void",
+            "|    printf (Locale,String,Object...)void",
+            "|    printf (String,Object...)void")
+            .collect(toList());
     final static List<String> START_UP = Collections.unmodifiableList(
             Stream.concat(START_UP_IMPORTS.stream(), START_UP_METHODS.stream())
             .map(s -> s.getSource())
@@ -432,9 +456,14 @@
         assertCommand(after, cmd, out, "", null, "", "");
     }
 
-    public void assertCommandOutputContains(boolean after, String cmd, String has) {
-        assertCommandCheckOutput(after, cmd, (s) ->
-                        assertTrue(s.contains(has), "Output: \'" + s + "' does not contain: " + has));
+    public void assertCommandOutputContains(boolean after, String cmd, String... hasThese) {
+        assertCommandCheckOutput(after, cmd, (s)
+                -> assertTrue(Arrays.stream(hasThese)
+                                    .allMatch(has -> s.contains(has)),
+                        "Output: \'" + s + "' does not contain: "
+                                + Arrays.stream(hasThese)
+                                        .filter(has -> !s.contains(has))
+                                        .collect(Collectors.joining(", "))));
     }
 
     public void assertCommandOutputStartsWith(boolean after, String cmd, String starts) {
@@ -467,7 +496,7 @@
     }
 
     public Consumer<String> assertStartsWith(String prefix) {
-        return (output) -> assertTrue(output.startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
+        return (output) -> assertTrue(output.trim().startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
     }
 
     public void assertOutput(String got, String expected, String display) {
--- a/test/jdk/jshell/SimpleRegressionTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/SimpleRegressionTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -22,7 +22,7 @@
  */
 
 /*
- * @test 8130450 8158906 8154374 8166400
+ * @test 8130450 8158906 8154374 8166400 8171892
  * @summary simple regression test
  * @build KullaTesting TestingInputStream
  * @run testng SimpleRegressionTest
@@ -150,7 +150,7 @@
         assertEval("C.class.getClassLoader() == Thread.currentThread().getContextClassLoader()", "true");
     }
 
-    public void testArayRepresentation() {
+    public void testArrayRepresentation() {
         assertEval("new int[4]", "int[4] { 0, 0, 0, 0 }");
         assertEval("new int[0]", "int[0] {  }");
         assertEval("new byte[2]", "byte[2] { 0, 0 }");
@@ -162,8 +162,24 @@
         assertEval("new char[] { 'a', 34, 77 }", "char[3] { 'a', '\"', 'M' }");
         assertEval("new boolean[] { false, true }", "boolean[2] { false, true }");
         assertEval("new int[][] { new int[] {44, 55}, new int[] {88,99}}",
-                "int[][2] { int[2] { 44, 55 }, int[2] { 88, 99 } }");
+                "int[2][] { int[2] { 44, 55 }, int[2] { 88, 99 } }");
         assertEval("new Object[] { \"howdy\", new int[] { 33, 44, 55 }, new String[] { \"up\", \"down\" }}",
                 "Object[3] { \"howdy\", int[3] { 33, 44, 55 }, String[2] { \"up\", \"down\" } }");
     }
+
+    public void testMultiDimArrayRepresentation() {
+        assertEval("new int[3][1]",
+                "int[3][] { int[1] { 0 }, int[1] { 0 }, int[1] { 0 } }");
+        assertEval("new int[3][]",
+                "int[3][] { null, null, null }");
+        assertEval("new int[][] { new int[] {44}, new int[] {77, 88,99}}",
+                "int[2][] { int[1] { 44 }, int[3] { 77, 88, 99 } }");
+        assertEval("new String[3][1]",
+                "String[3][] { String[1] { null }, String[1] { null }, String[1] { null } }");
+        assertEval("class C { }");
+        assertEval("new C[3][2]",
+                "C[3][] { C[2] { null, null }, C[2] { null, null }, C[2] { null, null } }");
+        assertEval("new boolean[2][1][3]",
+                "boolean[2][][] { boolean[1][] { boolean[3] { false, false, false } }, boolean[1][] { boolean[3] { false, false, false } } }");
+    }
 }
--- a/test/jdk/jshell/StartOptionTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/StartOptionTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -22,7 +22,7 @@
  */
 
 /*
- * @test 8151754 8080883 8160089 8170162 8166581
+ * @test 8151754 8080883 8160089 8170162 8166581 8172102
  * @summary Testing start-up options.
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -33,17 +33,14 @@
  * @run testng StartOptionTest
  */
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.Locale;
-import java.util.ServiceLoader;
 import java.util.function.Consumer;
 
-import javax.tools.Tool;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -157,9 +154,8 @@
         Path p = compiler.getPath("file.txt");
         compiler.writeToFile(p);
         start("", "Argument to startup missing.", "--startup");
-        start("", "Only one --startup or --no-startup option may be used.", "--startup", p.toString(), "--startup", p.toString());
-        start("", "Only one --startup or --no-startup option may be used.", "--no-startup", "--startup", p.toString());
-        start("", "Only one --startup or --no-startup option may be used.", "--startup", p.toString(), "--no-startup");
+        start("", "Conflicting options: both --startup and --no-startup were used.", "--no-startup", "--startup", p.toString());
+        start("", "Conflicting options: both --startup and --no-startup were used.", "--startup", p.toString(), "--no-startup");
         start("", "Argument to startup missing.", "--no-startup", "--startup");
     }
 
@@ -176,6 +172,7 @@
 
     public void testStartupUnknown() throws Exception {
         start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "UNKNOWN");
+        start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "DEFAULT", "--startup", "UNKNOWN");
     }
 
     public void testClasspath() throws Exception {
--- a/test/jdk/jshell/ToolBasicTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/ToolBasicTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347 8154714 8166649 8167643 8170162
+ * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347 8154714 8166649 8167643 8170162 8172102
  * @summary Tests for Basic tests for REPL tool
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -310,10 +310,10 @@
                 (a) -> evaluateExpression(a, "A", "new A()", "A")
         );
         test(new String[]{"--no-startup"},
-                (a) -> assertCommandCheckOutput(a, "printf(\"\")", assertStartsWith("|  Error:\n|  cannot find symbol"))
+                (a) -> assertCommandCheckOutput(a, "Pattern.compile(\"x+\")", assertStartsWith("|  Error:\n|  cannot find symbol"))
         );
         test(
-                (a) -> assertCommand(a, "printf(\"A\")", "", "", null, "A", "")
+                (a) -> assertCommand(a, "Pattern.compile(\"x+\")", "$1 ==> x+", "", null, "", "")
         );
     }
 
@@ -385,6 +385,16 @@
         }
     }
 
+    public void testOpenResource() {
+        test(
+                (a) -> assertCommand(a, "/open PRINTING", ""),
+                (a) -> assertCommandOutputContains(a, "/list",
+                        "void println", "System.out.printf"),
+                (a) -> assertCommand(a, "printf(\"%4.2f\", Math.PI)",
+                        "", "", null, "3.14", "")
+        );
+    }
+
     public void testSave() throws IOException {
         Compiler compiler = new Compiler();
         Path path = compiler.getPath("testSave.repl");
--- a/test/jdk/jshell/ToolCommandOptionTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/ToolCommandOptionTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
  /*
  * @test
- * @bug 8157395 8157393 8157517 8158738 8167128 8163840 8167637 8170368
+ * @bug 8157395 8157393 8157517 8158738 8167128 8163840 8167637 8170368 8172102
  * @summary Tests of jshell comand options, and undoing operations
  * @modules jdk.jshell/jdk.internal.jshell.tool
  *          jdk.compiler/com.sun.tools.javac.api
@@ -46,9 +46,9 @@
                 (a) -> assertCommand(a, "/li",
                         "1 : int x;"),
                 (a) -> assertCommandOutputStartsWith(a, "/lis -st",
-                        "\n  s1 : import"),
+                        "s1 : import"),
                 (a) -> assertCommandOutputStartsWith(a, "/list -all",
-                        "\n  s1 : import"),
+                        "s1 : import"),
                 (a) -> assertCommandOutputContains(a, "/list -all",
                         "1 : int x;"),
                 (a) -> assertCommandOutputContains(a, "/list -history",
@@ -260,10 +260,20 @@
                         "|  Specify no more than one of -default, -none, or a startup file name -- /set start foo -default"),
                 (a) -> assertCommand(a, "/set start frfg",
                         "|  File 'frfg' for '/set start' is not found."),
+                (a) -> assertCommand(a, "/set start DEFAULT frfg",
+                        "|  File 'frfg' for '/set start' is not found."),
                 (a) -> assertCommand(a, "/set start -default",
                         ""),
                 (a) -> assertCommand(a, "/set start",
                         "|  /set start -default"),
+                (a) -> assertCommand(a, "/set start DEFAULT",
+                        ""),
+                (a) -> assertCommand(a, "/set start",
+                        "|  /set start -default"),
+                (a) -> assertCommand(a, "/set start DEFAULT PRINTING",
+                        ""),
+                (a) -> assertCommandOutputContains(a, "/set start",
+                        "void println", "import java.util.*"),
                 (a) -> assertCommand(a, "/set start " + startup.toString(),
                         ""),
                 (a) -> assertCommand(a, "/set start",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jshell/ToolLocalSimpleTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8168615 8172102
+ * @summary Test all the ToolSimpleTest tests, but in local execution. Verify --execution flag
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.jdeps/com.sun.tools.javap
+ *          jdk.jshell/jdk.internal.jshell.tool
+ * @build KullaTesting TestingInputStream ToolSimpleTest
+ * @run testng ToolLocalSimpleTest
+ */
+
+import java.util.Locale;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+public class ToolLocalSimpleTest extends ToolSimpleTest {
+
+    @Override
+    public void test(Locale locale, boolean isDefaultStartUp, String[] args, String startUpMessage, ReplTest... tests) {
+        String[] wargs = new String[args.length + 2];
+        wargs[0] = "--execution";
+        wargs[1] = "local";
+        System.arraycopy(args, 0, wargs, 2, args.length);
+        super.test(locale, isDefaultStartUp, wargs, startUpMessage, tests);
+    }
+
+    @Test
+    public void verifyLocal() {
+        System.setProperty("LOCAL_CHECK", "Here");
+        assertEquals(System.getProperty("LOCAL_CHECK"), "Here");
+        test(new String[]{"--no-startup"},
+                a -> assertCommand(a, "System.getProperty(\"LOCAL_CHECK\")", "$1 ==> \"Here\""),
+                a -> assertCommand(a, "System.setProperty(\"LOCAL_CHECK\", \"After\")", "$2 ==> \"Here\"")
+        );
+        assertEquals(System.getProperty("LOCAL_CHECK"), "After");
+    }
+
+    @Override
+    @Test
+    public void testOptionR() {
+        test(new String[]{"-R-Dthe.sound=blorp", "--no-startup"},
+                (a) -> assertCommand(a, "System.getProperty(\"the.sound\")",
+                        "$1 ==> null")
+        );
+    }
+
+    @Override
+    @Test
+    public void testCompoundStart() {
+        test(new String[]{"--startup", "DEFAULT", "--startup", "PRINTING"},
+                (a) -> assertCommandOutputContains(a, "/list -start",
+                        "System.out.println", "import java.util.concurrent")
+        );
+    }
+
+    @Test
+    public void testOptionBadR() {
+        test(new String[]{"-R-RottenLiver"},
+                (a) -> assertCommand(a, "43",
+                        "$1 ==> 43")
+        );
+    }
+
+}
--- a/test/jdk/jshell/ToolSimpleTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/ToolSimpleTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368
+ * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102
  * @summary Simple jshell tool tests
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -45,9 +45,9 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
-@Test
 public class ToolSimpleTest extends ReplToolTesting {
 
+    @Test
     public void testRemaining() {
         test(
                 (a) -> assertCommand(a, "int z; z =", "z ==> 0"),
@@ -62,6 +62,7 @@
         );
     }
 
+    @Test
     public void testOpenComment() {
         test(
                 (a) -> assertCommand(a, "int z = /* blah", ""),
@@ -72,6 +73,7 @@
         );
     }
 
+    @Test
     public void oneLineOfError() {
         test(
                 (a) -> assertCommand(a, "12+", null),
@@ -80,6 +82,7 @@
         );
     }
 
+    @Test
     public void defineVariables() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/list", assertList()),
@@ -96,6 +99,7 @@
         );
     }
 
+    @Test
     public void defineMethods() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/list", assertList()),
@@ -112,6 +116,7 @@
         );
     }
 
+    @Test
     public void defineTypes() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/list", assertList()),
@@ -131,6 +136,7 @@
         );
     }
 
+    @Test
     public void defineImports() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/list", assertList()),
@@ -150,6 +156,7 @@
         );
     }
 
+    @Test
     public void defineVar() {
         test(
                 (a) -> assertCommand(a, "int x = 72", "x ==> 72"),
@@ -158,6 +165,7 @@
         );
     }
 
+    @Test
     public void defineUnresolvedVar() {
         test(
                 (a) -> assertCommand(a, "undefined x",
@@ -166,6 +174,7 @@
         );
     }
 
+    @Test
     public void testUnresolved() {
         test(
                 (a) -> assertCommand(a, "int f() { return g() + x + new A().a; }",
@@ -178,16 +187,19 @@
         );
     }
 
+    @Test
     public void testUnknownCommand() {
         test((a) -> assertCommand(a, "/unknown",
                 "|  No such command or snippet id: /unknown\n" +
                 "|  Type /help for help."));
     }
 
+    @Test
     public void testEmptyClassPath() {
         test(after -> assertCommand(after, "/classpath", "|  The /classpath command requires a path argument."));
     }
 
+    @Test
     public void testNoArgument() {
         test(
                 (a) -> assertCommand(a, "/save",
@@ -199,6 +211,7 @@
         );
     }
 
+    @Test
     public void testDebug() {
         test(
                 (a) -> assertCommand(a, "/deb", "|  Debugging on"),
@@ -208,6 +221,7 @@
         );
     }
 
+    @Test
     public void testDrop() {
         test(false, new String[]{"--no-startup"},
                 a -> assertVariable(a, "int", "a"),
@@ -240,6 +254,7 @@
         );
     }
 
+    @Test
     public void testDropNegative() {
         test(false, new String[]{"--no-startup"},
                 a -> assertCommandOutputStartsWith(a, "/drop 0", "|  No such snippet: 0"),
@@ -255,6 +270,7 @@
         );
     }
 
+    @Test
     public void testAmbiguousDrop() {
         Consumer<String> check = s -> {
             assertTrue(s.startsWith("|  The argument references more than one import, variable, method, or class"), s);
@@ -280,6 +296,7 @@
         );
     }
 
+    @Test
     public void testApplicationOfPost() {
         test(
                 (a) -> assertCommand(a, "/set mode t normal -command", "|  Created new feedback mode: t"),
@@ -290,6 +307,7 @@
         );
     }
 
+    @Test
     public void testHelpLength() {
         Consumer<String> testOutput = (s) -> {
             List<String> ss = Stream.of(s.split("\n"))
@@ -304,6 +322,7 @@
         );
     }
 
+    @Test
     public void testHelp() {
         test(
                 (a) -> assertHelp(a, "/?", "/list", "/help", "/exit", "intro"),
@@ -315,6 +334,7 @@
         );
     }
 
+    @Test
     public void testHelpFormat() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/help", s -> {
@@ -355,6 +375,7 @@
         }
     }
 
+    @Test
     public void testListArgs() {
         String arg = "qqqq";
         List<String> startVarList = new ArrayList<>(START_UP);
@@ -370,13 +391,14 @@
                         s -> checkLineToList(s, START_UP)),
                 a -> assertCommandCheckOutput(a, "/list -all",
                         s -> checkLineToList(s, startVarList)),
-                a -> assertCommandCheckOutput(a, "/list printf",
-                        s -> assertTrue(s.contains("void printf"))),
+                a -> assertCommandOutputStartsWith(a, "/list s3",
+                        "s3 : import"),
                 a -> assertCommandOutputStartsWith(a, "/list " + arg,
                         "|  No such snippet: " + arg)
         );
     }
 
+    @Test
     public void testVarsArgs() {
         String arg = "qqqq";
         List<String> startVarList = new ArrayList<>();
@@ -395,25 +417,28 @@
                         s -> checkLineToList(s, startVarList)),
                 a -> assertCommandOutputStartsWith(a, "/vars -all",
                         "|    int aardvark = 0\n|    int a = "),
-                a -> assertCommandOutputStartsWith(a, "/vars printf",
-                        "|  This command does not accept the snippet 'printf'"),
+                a -> assertCommandOutputStartsWith(a, "/vars f",
+                        "|  This command does not accept the snippet 'f'"),
                 a -> assertCommand(a, "/var " + arg,
                         "|  No such snippet: " + arg)
         );
     }
 
+    @Test
     public void testMethodsArgs() {
         String arg = "qqqq";
-        List<String> startMethodList = new ArrayList<>(START_UP_CMD_METHOD);
-        test(
+        List<String> printingMethodList = new ArrayList<>(PRINTING_CMD_METHOD);
+        test(new String[]{"--startup", "PRINTING"},
                 a -> assertCommandCheckOutput(a, "/methods -all",
-                        s -> checkLineToList(s, startMethodList)),
+                        s -> checkLineToList(s, printingMethodList)),
                 a -> assertCommandCheckOutput(a, "/methods -start",
-                        s -> checkLineToList(s, startMethodList)),
-                a -> assertCommandCheckOutput(a, "/methods printf",
-                        s -> checkLineToList(s, startMethodList)),
+                        s -> checkLineToList(s, printingMethodList)),
+                a -> assertCommandCheckOutput(a, "/methods print println printf",
+                        s -> checkLineToList(s, printingMethodList)),
+                a -> assertCommandCheckOutput(a, "/methods println",
+                        s -> assertEquals(s.trim().split("\n").length, 10)),
                 a -> assertCommandCheckOutput(a, "/methods",
-                        s -> checkLineToList(s, startMethodList)),
+                        s -> checkLineToList(s, printingMethodList)),
                 a -> assertCommandOutputStartsWith(a, "/methods " + arg,
                         "|  No such snippet: " + arg),
                 a -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
@@ -425,9 +450,9 @@
                 a -> assertCommandOutputStartsWith(a, "/methods aardvark",
                         "|  This command does not accept the snippet 'aardvark' : int aardvark"),
                 a -> assertCommandCheckOutput(a, "/methods -start",
-                        s -> checkLineToList(s, startMethodList)),
-                a -> assertCommandCheckOutput(a, "/methods printf",
-                        s -> checkLineToList(s, startMethodList)),
+                        s -> checkLineToList(s, printingMethodList)),
+                a -> assertCommandCheckOutput(a, "/methods print println printf",
+                        s -> checkLineToList(s, printingMethodList)),
                 a -> assertCommandOutputStartsWith(a, "/methods g",
                         "|    g ()void"),
                 a -> assertCommandOutputStartsWith(a, "/methods f",
@@ -436,6 +461,7 @@
         );
     }
 
+    @Test
     public void testTypesArgs() {
         String arg = "qqqq";
         List<String> startTypeList = new ArrayList<>();
@@ -468,6 +494,24 @@
                         s -> checkLineToList(s, startTypeList))
         );
     }
+
+    @Test
+    public void testCompoundStart() {
+        test(new String[]{"--startup", "DEFAULT", "--startup", "PRINTING"},
+                (a) -> assertCommand(a, "printf(\"%4.2f\", Math.PI)",
+                        "", "", null, "3.14", "")
+        );
+    }
+
+    @Test
+    public void testJavaSeStart() {
+        test(new String[]{"--startup", "JAVASE"},
+                (a) -> assertCommand(a, "ZoneOffsetTransitionRule.TimeDefinition.WALL",
+                        "$1 ==> WALL")
+        );
+    }
+
+    @Test
     public void defineClasses() {
         test(
                 (a) -> assertCommandCheckOutput(a, "/list", assertList()),
@@ -486,6 +530,8 @@
                 (a) -> assertCommandCheckOutput(a, "/types", assertClasses())
         );
     }
+
+    @Test
     public void testCommandPrefix() {
         test(a -> assertCommandCheckOutput(a, "/s",
                       assertStartsWith("|  Command: '/s' is ambiguous: /save, /set")),
@@ -496,6 +542,7 @@
                       assertStartsWith("|  '/save' requires a filename argument.")));
     }
 
+    @Test
     public void testOptionQ() {
         test(Locale.ROOT, false, new String[]{"-q", "--no-startup"}, "",
                 (a) -> assertCommand(a, "1+1", "$1 ==> 2"),
@@ -503,12 +550,14 @@
         );
     }
 
+    @Test
     public void testOptionS() {
         test(Locale.ROOT, false, new String[]{"-s", "--no-startup"}, "",
                 (a) -> assertCommand(a, "1+1", "")
         );
     }
 
+    @Test
     public void testOptionV() {
         test(new String[]{"-v", "--no-startup"},
                 (a) -> assertCommand(a, "1+1",
@@ -517,6 +566,7 @@
         );
     }
 
+    @Test
     public void testOptionFeedback() {
         test(Locale.ROOT, false, new String[]{"--feedback", "concise", "--no-startup"}, "",
                 (a) -> assertCommand(a, "1+1", "$1 ==> 2"),
@@ -524,6 +574,7 @@
         );
     }
 
+    @Test
     public void testCompoundOptions() {
         Consumer<String> confirmNoStartup = s -> {
                     assertEquals(0, Stream.of(s.split("\n"))
@@ -546,6 +597,7 @@
         );
     }
 
+    @Test
     public void testOptionR() {
         test(new String[]{"-R-Dthe.sound=blorp", "--no-startup"},
                 (a) -> assertCommand(a, "System.getProperty(\"the.sound\")",
@@ -553,6 +605,7 @@
         );
     }
 
+    @Test
     public void test8156910() {
         test(
                 (a) -> assertCommandOutputContains(a, "System.out.println(\"%5d\", 10);", "%5d"),
--- a/test/jdk/jshell/UserExecutionControlTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/UserExecutionControlTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,14 +23,13 @@
 
 /*
  * @test
- * @bug 8156101 8159935 8159122
+ * @bug 8156101 8159935 8159122 8168615
  * @summary Tests for ExecutionControl SPI
  * @build KullaTesting ExecutionControlTestBase
  * @run testng UserExecutionControlTest
  */
 
 
-import jdk.jshell.execution.LocalExecutionControl;
 import org.testng.annotations.Test;
 import static org.testng.Assert.assertEquals;
 import org.testng.annotations.BeforeMethod;
@@ -41,12 +40,13 @@
     @BeforeMethod
     @Override
     public void setUp() {
-        setUp(builder -> builder.executionEngine(LocalExecutionControl.create()));
+        setUp(builder -> builder.executionEngine("local"));
     }
 
     public void verifyLocal() throws ClassNotFoundException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
         System.setProperty("LOCAL_CHECK", "TBD");
         assertEquals(System.getProperty("LOCAL_CHECK"), "TBD");
+        assertEval("System.getProperty(\"LOCAL_CHECK\")", "\"TBD\"");
         assertEval("System.setProperty(\"LOCAL_CHECK\", \"local\")");
         assertEquals(System.getProperty("LOCAL_CHECK"), "local");
     }
--- a/test/jdk/jshell/UserJdiUserRemoteTest.java	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/jdk/jshell/UserJdiUserRemoteTest.java	Fri Jan 06 08:42:28 2017 +0000
@@ -23,9 +23,9 @@
 
  /*
  * @test
- * @bug 8160128 8159935
+ * @bug 8160128 8159935 8168615
  * @summary Tests for Aux channel, custom remote agents, custom JDI implementations.
- * @build KullaTesting ExecutionControlTestBase
+ * @build KullaTesting ExecutionControlTestBase MyExecutionControl MyRemoteExecutionControl MyExecutionControlProvider
  * @run testng UserJdiUserRemoteTest
  */
 import java.io.ByteArrayOutputStream;
@@ -34,34 +34,10 @@
 import jdk.jshell.Snippet;
 import static jdk.jshell.Snippet.Status.OVERWRITTEN;
 import static jdk.jshell.Snippet.Status.VALID;
-import java.io.IOException;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
-import java.net.ServerSocket;
-import java.util.ArrayList;
-import java.util.List;
-import com.sun.jdi.VMDisconnectedException;
-import com.sun.jdi.VirtualMachine;
 import jdk.jshell.VarSnippet;
-import jdk.jshell.execution.DirectExecutionControl;
-import jdk.jshell.execution.JdiExecutionControl;
-import jdk.jshell.execution.JdiInitiator;
-import jdk.jshell.execution.Util;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.net.Socket;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Consumer;
 import jdk.jshell.spi.ExecutionControl;
 import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
-import jdk.jshell.spi.ExecutionEnv;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-import static jdk.jshell.execution.Util.forwardExecutionControlAndIO;
-import static jdk.jshell.execution.Util.remoteInputOutput;
 
 @Test
 public class UserJdiUserRemoteTest extends ExecutionControlTestBase {
@@ -73,7 +49,7 @@
     @Override
     public void setUp() {
         auxStream = new ByteArrayOutputStream();
-        setUp(builder -> builder.executionEngine(MyExecutionControl.create(this)));
+        setUp(builder -> builder.executionEngine(new MyExecutionControlProvider(this), null));
     }
 
     public void testVarValue() {
@@ -114,174 +90,3 @@
         assertActiveKeys();
     }
 }
-
-class MyExecutionControl extends JdiExecutionControl {
-
-    private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName();
-    private static final int TIMEOUT = 2000;
-
-    private VirtualMachine vm;
-    private Process process;
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
-     * {@code LaunchingConnector}.
-     *
-     * @return the generator
-     */
-    public static ExecutionControl.Generator create(UserJdiUserRemoteTest test) {
-        return env -> make(env, test);
-    }
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
-     * {@code ListeningConnector} or {@code LaunchingConnector}.
-     *
-     * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
-     * commands and results. This socket also transports the user
-     * input/output/error.
-     *
-     * @param env the context passed by
-         * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
-     * @return the channel
-     * @throws IOException if there are errors in set-up
-     */
-    static ExecutionControl make(ExecutionEnv env, UserJdiUserRemoteTest test) throws IOException {
-        try (final ServerSocket listener = new ServerSocket(0)) {
-            // timeout for socket
-            listener.setSoTimeout(TIMEOUT);
-            int port = listener.getLocalPort();
-
-            // Set-up the JDI connection
-            List<String> opts = new ArrayList<>(env.extraRemoteVMOptions());
-            opts.add("-classpath");
-            opts.add(System.getProperty("java.class.path")
-                    + System.getProperty("path.separator")
-                    + System.getProperty("user.dir"));
-            JdiInitiator jdii = new JdiInitiator(port,
-                    opts, REMOTE_AGENT, true, null, TIMEOUT);
-            VirtualMachine vm = jdii.vm();
-            Process process = jdii.process();
-
-            List<Consumer<String>> deathListeners = new ArrayList<>();
-            deathListeners.add(s -> env.closeDown());
-            Util.detectJdiExitEvent(vm, s -> {
-                for (Consumer<String> h : deathListeners) {
-                    h.accept(s);
-                }
-            });
-
-            // Set-up the commands/reslts on the socket.  Piggy-back snippet
-            // output.
-            Socket socket = listener.accept();
-            // out before in -- match remote creation so we don't hang
-            OutputStream out = socket.getOutputStream();
-            Map<String, OutputStream> outputs = new HashMap<>();
-            outputs.put("out", env.userOut());
-            outputs.put("err", env.userErr());
-            outputs.put("aux", test.auxStream);
-            Map<String, InputStream> input = new HashMap<>();
-            input.put("in", env.userIn());
-            ExecutionControl myec = remoteInputOutput(socket.getInputStream(), out, outputs, input, (objIn, objOut) -> new MyExecutionControl(objOut, objIn, vm, process, deathListeners));
-            test.currentEC = myec;
-            return myec;
-        }
-    }
-
-    /**
-     * Create an instance.
-     *
-     * @param out the output for commands
-     * @param in the input for responses
-     */
-    private MyExecutionControl(ObjectOutput out, ObjectInput in,
-            VirtualMachine vm, Process process,
-            List<Consumer<String>> deathListeners) {
-        super(out, in);
-        this.vm = vm;
-        this.process = process;
-        deathListeners.add(s -> disposeVM());
-    }
-
-    @Override
-    public void close() {
-        super.close();
-        disposeVM();
-    }
-
-    private synchronized void disposeVM() {
-        try {
-            if (vm != null) {
-                vm.dispose(); // This could NPE, so it is caught below
-                vm = null;
-            }
-        } catch (VMDisconnectedException ex) {
-            // Ignore if already closed
-        } catch (Throwable e) {
-            fail("disposeVM threw: " + e);
-        } finally {
-            if (process != null) {
-                process.destroy();
-                process = null;
-            }
-        }
-    }
-
-    @Override
-    protected synchronized VirtualMachine vm() throws EngineTerminationException {
-        if (vm == null) {
-            throw new EngineTerminationException("VM closed");
-        } else {
-            return vm;
-        }
-    }
-
-}
-
-class MyRemoteExecutionControl extends DirectExecutionControl implements ExecutionControl {
-
-    static PrintStream auxPrint;
-
-    /**
-     * Launch the agent, connecting to the JShell-core over the socket specified
-     * in the command-line argument.
-     *
-     * @param args standard command-line arguments, expectation is the socket
-     * number is the only argument
-     * @throws Exception any unexpected exception
-     */
-    public static void main(String[] args) throws Exception {
-        try {
-            String loopBack = null;
-            Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
-            InputStream inStream = socket.getInputStream();
-            OutputStream outStream = socket.getOutputStream();
-            Map<String, Consumer<OutputStream>> outputs = new HashMap<>();
-            outputs.put("out", st -> System.setOut(new PrintStream(st, true)));
-            outputs.put("err", st -> System.setErr(new PrintStream(st, true)));
-            outputs.put("aux", st -> { auxPrint = new PrintStream(st, true); });
-            Map<String, Consumer<InputStream>> input = new HashMap<>();
-            input.put("in", st -> System.setIn(st));
-            forwardExecutionControlAndIO(new MyRemoteExecutionControl(), inStream, outStream, outputs, input);
-        } catch (Throwable ex) {
-            throw ex;
-        }
-    }
-
-    @Override
-    public String varValue(String className, String varName)
-            throws RunException, EngineTerminationException, InternalException {
-        auxPrint.print(varName);
-        return super.varValue(className, varName);
-    }
-
-    @Override
-    public Object extensionCommand(String className, Object arg)
-            throws RunException, EngineTerminationException, InternalException {
-        if (!arg.equals("test")) {
-            throw new InternalException("expected extensionCommand arg to be 'test' got: " + arg);
-        }
-        return "ribbit";
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/classreader/8171132/BadConstantValue.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2016 Google, Inc.  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.
+ */
+
+/*
+ * @test
+ * @bug 8171132
+ * @summary Improve class reading of invalid or out-of-range ConstantValue attributes
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.code
+ *          jdk.compiler/com.sun.tools.javac.jvm
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build BadConstantValue
+ * @run main BadConstantValue
+ */
+
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ClassWriter;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
+import com.sun.tools.classfile.ConstantValue_attribute;
+import com.sun.tools.classfile.Field;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.ClassFinder.BadClassFile;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.JCDiagnostic;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Objects;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+public class BadConstantValue {
+
+    static final File classesdir = new File("badconstants");
+
+    public static void main(String[] args) throws Exception {
+        // report errors for ConstantValues of the wrong type
+        testInvalidConstantType("int");
+        testInvalidConstantType("short");
+        testInvalidConstantType("byte");
+        testInvalidConstantType("char");
+        testInvalidConstantType("boolean");
+
+        // report errors for ConstantValues outside the expected range
+        testValidConstRange("int", Integer.MAX_VALUE);
+        testValidConstRange("int", Integer.MIN_VALUE);
+
+        testValidConstRange("short", Short.MAX_VALUE);
+        testValidConstRange("short", Short.MIN_VALUE);
+        testInvalidConstRange("short", Short.MAX_VALUE + 1);
+        testInvalidConstRange("short", Short.MIN_VALUE - 1);
+
+        testValidConstRange("byte", Byte.MAX_VALUE);
+        testValidConstRange("byte", Byte.MIN_VALUE);
+        testInvalidConstRange("byte", Byte.MAX_VALUE + 1);
+        testInvalidConstRange("byte", Byte.MIN_VALUE - 1);
+
+        testValidConstRange("char", Character.MAX_VALUE);
+        testValidConstRange("char", Character.MIN_VALUE);
+        testInvalidConstRange("char", Character.MAX_VALUE + 1);
+        testInvalidConstRange("char", Character.MIN_VALUE - 1);
+
+        testValidConstRange("boolean", 0);
+        testValidConstRange("boolean", 1);
+        testInvalidConstRange("boolean", 2);
+        testInvalidConstRange("boolean", Integer.MIN_VALUE);
+        testInvalidConstRange("boolean", Integer.MAX_VALUE);
+    }
+
+    /**
+     * Tests that a constant value of the given {@code type} and initialized with an out-of-range
+     * {@code value} is rejected.
+     */
+    private static void testInvalidConstRange(String type, int value) throws Exception {
+        createConstantWithValue(type, value);
+        BadClassFile badClassFile = loadBadClass("Lib");
+        if (badClassFile == null) {
+            throw new AssertionError("did not see expected error");
+        }
+        JCDiagnostic diagnostic = (JCDiagnostic) badClassFile.getDiagnostic().getArgs()[1];
+        assertEquals("compiler.misc.bad.constant.range", diagnostic.getCode());
+        assertEquals(3, diagnostic.getArgs().length);
+        assertEquals(value, diagnostic.getArgs()[0]);
+        assertEquals("B", diagnostic.getArgs()[1].toString());
+        assertEquals(type, String.valueOf(diagnostic.getArgs()[2]));
+    }
+
+    /**
+     * Tests that a constant value of the given {@code type} and initialized with {@code value} is
+     * accepted.
+     */
+    private static void testValidConstRange(String type, int value) throws Exception {
+        createConstantWithValue(type, value);
+        BadClassFile badClassFile = loadBadClass("Lib");
+        if (badClassFile != null) {
+          throw new AssertionError("saw unexpected error", badClassFile);
+        }
+    }
+
+    /**
+     * Creates a class file containing a constant field with the given type and value, which may be
+     * outside the expected range.
+     */
+    private static void createConstantWithValue(String type, int value) throws Exception {
+        // Create a class with two constants, A and B. A is of type int and has value "actual";
+        // B is of type "type" and is initialized to that type's default value.
+        File lib = writeFile(classesdir, "Lib.java", String.format(
+                "class Lib { static final int A = %s; static final %s B = %s; }",
+                value, type, (type.equals("boolean") ? "false" : "0")));
+        compile("-d", classesdir.getPath(), lib.getPath());
+        File libClass = new File(classesdir, "Lib.class");
+        // Rewrite the class to only have field B of type "type" and with "value" (potentially
+        // out of range).
+        swapConstantValues(libClass);
+    }
+
+    /** Tests that a field of the given integral type with a constant string value is rejected. */
+    private static void testInvalidConstantType(String type) throws Exception {
+        // create a class file with field that has an invalid CONSTANT_String ConstantValue
+        File lib = writeFile(classesdir, "Lib.java", String.format(
+                "class Lib { static final String A = \"hello\"; static final %s CONST = %s; }",
+                type, type.equals("boolean") ? "false" : "0"));
+        compile("-d", classesdir.getPath(), lib.getPath());
+        File libClass = new File(classesdir, "Lib.class");
+        swapConstantValues(libClass);
+
+        BadClassFile badClassFile = loadBadClass("Lib");
+
+        JCDiagnostic diagnostic = (JCDiagnostic) badClassFile.getDiagnostic().getArgs()[1];
+        assertEquals("compiler.misc.bad.constant.value", diagnostic.getCode());
+        assertEquals(3, diagnostic.getArgs().length);
+        assertEquals("hello", diagnostic.getArgs()[0]);
+        assertEquals("CONST", diagnostic.getArgs()[1].toString());
+        assertEquals("Integer", diagnostic.getArgs()[2]);
+    }
+
+    private static BadClassFile loadBadClass(String className) {
+        // load the class, and save the thrown BadClassFile exception
+        JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+        JavacTaskImpl task = (JavacTaskImpl) c.getTask(null, null, null,
+                Arrays.asList("-classpath", classesdir.getPath()), null, null);
+        Symtab syms = Symtab.instance(task.getContext());
+        task.ensureEntered();
+        BadClassFile badClassFile;
+        try {
+            com.sun.tools.javac.main.JavaCompiler.instance(task.getContext())
+                    .resolveIdent(syms.unnamedModule, className).complete();
+        } catch (BadClassFile e) {
+            return e;
+        }
+        return null;
+    }
+
+    /**
+     * Given a class file with two constant fields A and B, replaces both with a single field with
+     * B's type and A's ConstantValue attribute.
+     */
+    private static void swapConstantValues(File file) throws Exception {
+        ClassFile cf = ClassFile.read(file);
+        Field a = cf.fields[0];
+        Field b = cf.fields[1];
+        Field[] fields = {
+            new Field(b.access_flags, b.name_index, b.descriptor, a.attributes),
+        };
+        cf = new ClassFile(cf.magic, Target.JDK1_7.minorVersion, Target.JDK1_7.majorVersion,
+                cf.constant_pool, cf.access_flags, cf.this_class, cf.super_class, cf.interfaces,
+                fields, cf.methods, cf.attributes);
+        new ClassWriter().write(cf, file);
+    }
+
+    static String compile(String... args) throws Exception {
+        System.err.println("compile: " + Arrays.asList(args));
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        int rc = com.sun.tools.javac.Main.compile(args, pw);
+        pw.close();
+        String out = sw.toString();
+        if (out.length() > 0) {
+            System.err.println(out);
+        }
+        if (rc != 0) {
+            throw new AssertionError("compilation failed, rc=" + rc);
+        }
+        return out;
+    }
+
+    static File writeFile(File dir, String path, String body) throws IOException {
+        File f = new File(dir, path);
+        f.getParentFile().mkdirs();
+        FileWriter out = new FileWriter(f);
+        out.write(body);
+        out.close();
+        return f;
+    }
+
+    static void assertEquals(Object expected, Object actual) {
+        Assert.check(Objects.equals(expected, actual),
+                String.format("expected: %s, but was: %s", expected, actual));
+    }
+}
--- a/test/tools/javac/diags/examples.not-yet.txt	Wed Dec 28 11:45:19 2016 +0000
+++ b/test/tools/javac/diags/examples.not-yet.txt	Fri Jan 06 08:42:28 2017 +0000
@@ -43,6 +43,8 @@
 compiler.misc.bad.class.signature                       # bad class file
 compiler.misc.bad.const.pool.tag                        # bad class file
 compiler.misc.bad.const.pool.tag.at                     # bad class file
+compiler.misc.bad.constant.range                        # bad class file
+compiler.misc.bad.constant.value                        # bad class file
 compiler.misc.bad.enclosing.class                       # bad class file
 compiler.misc.bad.enclosing.method                      # bad class file
 compiler.misc.bad.runtime.invisible.param.annotations   # bad class file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/8169091/T8169091.java	Fri Jan 06 08:42:28 2017 +0000
@@ -0,0 +1,15 @@
+/*
+ * @test
+ * @bug 8169091
+ * @summary Method reference T::methodName for generic type T does not compile any more
+ * @compile T8169091.java
+ */
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+interface T8169091 {
+    static <T extends Comparable<? super T>> Comparator<T> comparator() {
+        return (Comparator<T> & Serializable)T::compareTo;
+    }
+}