8161983: JShell API: Clean-up following 8160127 et. al.
authorrfield
Sun, 06 Nov 2016 22:50:46 -0800
changeset 41941 a935ac3f5274
parent 41940 048d559e9da7
child 41942 260c724e5614
8161983: JShell API: Clean-up following 8160127 et. al. Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIDefaultExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIEventHandler.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIInitiator.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiEventHandler.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java
langtools/test/jdk/jshell/FailOverExecutionControlTest.java
langtools/test/jdk/jshell/JDILaunchingExecutionControlTest.java
langtools/test/jdk/jshell/JDIListeningExecutionControlTest.java
langtools/test/jdk/jshell/JDIListeningLocalhostExecutionControlTest.java
langtools/test/jdk/jshell/JdiLaunchingExecutionControlTest.java
langtools/test/jdk/jshell/JdiListeningExecutionControlTest.java
langtools/test/jdk/jshell/JdiListeningLocalhostExecutionControlTest.java
langtools/test/jdk/jshell/UserJDIUserRemoteTest.java
langtools/test/jdk/jshell/UserJdiUserRemoteTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Fri Nov 04 14:47:25 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Sun Nov 06 22:50:46 2016 -0800
@@ -47,7 +47,7 @@
 import java.util.stream.Stream;
 import jdk.internal.jshell.debug.InternalDebugControl;
 import jdk.jshell.Snippet.Status;
-import jdk.jshell.execution.JDIDefaultExecutionControl;
+import jdk.jshell.execution.JdiDefaultExecutionControl;
 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
 import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
 import jdk.jshell.spi.ExecutionEnv;
@@ -117,10 +117,9 @@
         this.extraRemoteVMOptions = b.extraRemoteVMOptions;
         this.extraCompilerOptions = b.extraCompilerOptions;
         this.executionControlGenerator = b.executionControlGenerator==null
-                ? failOverExecutionControlGenerator(
-                        JDIDefaultExecutionControl.launch(),
-                        JDIDefaultExecutionControl.listen("localhost"),
-                        JDIDefaultExecutionControl.listen(null))
+                ? failOverExecutionControlGenerator(JdiDefaultExecutionControl.launch(),
+                        JdiDefaultExecutionControl.listen("localhost"),
+                        JdiDefaultExecutionControl.listen(null))
                 : b.executionControlGenerator;
 
         this.maps = new SnippetMaps(this);
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIDefaultExecutionControl.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-/*
- * 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.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.BooleanValue;
-import com.sun.jdi.ClassNotLoadedException;
-import com.sun.jdi.Field;
-import com.sun.jdi.IncompatibleThreadStateException;
-import com.sun.jdi.InvalidTypeException;
-import com.sun.jdi.ObjectReference;
-import com.sun.jdi.StackFrame;
-import com.sun.jdi.ThreadReference;
-import com.sun.jdi.VMDisconnectedException;
-import com.sun.jdi.VirtualMachine;
-import jdk.jshell.spi.ExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
-import static jdk.jshell.execution.Util.remoteInputOutput;
-
-/**
- * The implementation of {@link jdk.jshell.spi.ExecutionControl} that the
- * JShell-core uses by default.
- * Launches a remote process -- the "remote agent".
- * Interfaces to the remote agent over a socket and via JDI.
- * Designed to work with {@link RemoteExecutionControl}.
- *
- * @author Robert Field
- * @author Jan Lahoda
- */
-public class JDIDefaultExecutionControl extends JDIExecutionControl {
-
-    private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
-
-    private VirtualMachine vm;
-    private Process process;
-
-    private final Object STOP_LOCK = new Object();
-    private boolean userCodeRunning = false;
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
-     * {@code LaunchingConnector}.
-     *
-     * @return the generator
-     */
-    public static ExecutionControl.Generator launch() {
-        return env -> create(env, true, null);
-    }
-
-    /**
-     * Creates an ExecutionControl instance based on a JDI
-     * {@code ListeningConnector}.
-     *
-     * @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 env -> create(env, false, host);
-    }
-
-    /**
-     * 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) }
-     * @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)
-     * @return the channel
-     * @throws IOException if there are errors in set-up
-     */
-    private static ExecutionControl create(ExecutionEnv env,
-            boolean isLaunch, String host) throws IOException {
-        try (final ServerSocket listener = new ServerSocket(0)) {
-            // timeout after 60 seconds
-            listener.setSoTimeout(60000);
-            int port = listener.getLocalPort();
-
-            // Set-up the JDI connection
-            JDIInitiator jdii = new JDIInitiator(port,
-                    env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch, host);
-            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());
-            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));
-        }
-    }
-
-    /**
-     * Create an instance.
-     *
-     * @param cmdout the output for commands
-     * @param cmdin the input for responses
-     */
-    private JDIDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
-            VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
-        super(cmdout, cmdin);
-        this.vm = vm;
-        this.process = process;
-        deathListeners.add(s -> disposeVM());
-    }
-
-    @Override
-    public String invoke(String classname, String methodname)
-            throws RunException,
-            EngineTerminationException, InternalException {
-        String res;
-        synchronized (STOP_LOCK) {
-            userCodeRunning = true;
-        }
-        try {
-            res = super.invoke(classname, methodname);
-        } finally {
-            synchronized (STOP_LOCK) {
-                userCodeRunning = false;
-            }
-        }
-        return res;
-    }
-
-    /**
-     * Interrupts a running remote invoke by manipulating remote variables
-     * and sending a stop via JDI.
-     *
-     * @throws EngineTerminationException the execution engine has terminated
-     * @throws InternalException an internal problem occurred
-     */
-    @Override
-    public void stop() throws EngineTerminationException, InternalException {
-        synchronized (STOP_LOCK) {
-            if (!userCodeRunning) {
-                return;
-            }
-
-            vm().suspend();
-            try {
-                OUTER:
-                for (ThreadReference thread : vm().allThreads()) {
-                    // could also tag the thread (e.g. using name), to find it easier
-                    for (StackFrame frame : thread.frames()) {
-                        if (REMOTE_AGENT.equals(frame.location().declaringType().name()) &&
-                                (    "invoke".equals(frame.location().method().name())
-                                || "varValue".equals(frame.location().method().name()))) {
-                            ObjectReference thiz = frame.thisObject();
-                            Field inClientCode = thiz.referenceType().fieldByName("inClientCode");
-                            Field expectingStop = thiz.referenceType().fieldByName("expectingStop");
-                            Field stopException = thiz.referenceType().fieldByName("stopException");
-                            if (((BooleanValue) thiz.getValue(inClientCode)).value()) {
-                                thiz.setValue(expectingStop, vm().mirrorOf(true));
-                                ObjectReference stopInstance = (ObjectReference) thiz.getValue(stopException);
-
-                                vm().resume();
-                                debug("Attempting to stop the client code...\n");
-                                thread.stop(stopInstance);
-                                thiz.setValue(expectingStop, vm().mirrorOf(false));
-                            }
-
-                            break OUTER;
-                        }
-                    }
-                }
-            } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException ex) {
-                throw new InternalException("Exception on remote stop: " + ex);
-            } finally {
-                vm().resume();
-            }
-        }
-    }
-
-    @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 ex) {
-            debug(ex, "disposeVM");
-        } 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;
-        }
-    }
-
-    /**
-     * Log debugging information. Arguments as for {@code printf}.
-     *
-     * @param format a format string as described in Format string syntax
-     * @param args arguments referenced by the format specifiers in the format
-     * string.
-     */
-    private static void debug(String format, Object... args) {
-        // Reserved for future logging
-    }
-
-    /**
-     * Log a serious unexpected internal exception.
-     *
-     * @param ex the exception
-     * @param where a description of the context of the exception
-     */
-    private static void debug(Throwable ex, String where) {
-        // Reserved for future logging
-    }
-
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIEventHandler.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 1998, 2014, 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.function.Consumer;
-import com.sun.jdi.*;
-import com.sun.jdi.event.*;
-
-/**
- * Handler of Java Debug Interface events.
- * Adapted from jdb EventHandler.
- * Only exit and disconnect events processed.
- */
-class JDIEventHandler implements Runnable {
-
-    private final Thread thread;
-    private volatile boolean connected = true;
-    private boolean completed = false;
-    private final VirtualMachine vm;
-    private final Consumer<String> reportVMExit;
-
-    /**
-     * Creates an event handler. Start with {@code start()}.
-     *
-     * @param vm the virtual machine for which to handle events
-     * @param reportVMExit callback to report exit/disconnect
-     * (passed true if the VM has died)
-     */
-    JDIEventHandler(VirtualMachine vm, Consumer<String> reportVMExit) {
-        this.vm = vm;
-        this.reportVMExit = reportVMExit;
-        this.thread = new Thread(this, "event-handler");
-        this.thread.setDaemon(true);
-    }
-
-    /**
-     * Starts the event handler.
-     */
-    void start() {
-        thread.start();
-    }
-
-    synchronized void shutdown() {
-        connected = false;  // force run() loop termination
-        thread.interrupt();
-        while (!completed) {
-            try {wait();} catch (InterruptedException exc) {}
-        }
-    }
-
-    @Override
-    public void run() {
-        EventQueue queue = vm.eventQueue();
-        while (connected) {
-            try {
-                EventSet eventSet = queue.remove();
-                boolean resumeStoppedApp = false;
-                EventIterator it = eventSet.eventIterator();
-                while (it.hasNext()) {
-                    resumeStoppedApp |= handleEvent(it.nextEvent());
-                }
-
-                if (resumeStoppedApp) {
-                    eventSet.resume();
-                }
-            } catch (InterruptedException exc) {
-                // Do nothing. Any changes will be seen at top of loop.
-            } catch (VMDisconnectedException discExc) {
-                handleDisconnectedException();
-                break;
-            }
-        }
-        synchronized (this) {
-            completed = true;
-            notifyAll();
-        }
-    }
-
-    private boolean handleEvent(Event event) {
-        handleExitEvent(event);
-        return true;
-    }
-
-    private void handleExitEvent(Event event) {
-        if (event instanceof VMDeathEvent) {
-            reportVMExit.accept("VM Died");
-        } else if (event instanceof VMDisconnectEvent) {
-            connected = false;
-            reportVMExit.accept("VM Disconnected");
-        } else {
-            // ignore everything else
-        }
-    }
-
-    private synchronized void handleDisconnectedException() {
-        /*
-         * A VMDisconnectedException has happened while dealing with
-         * another event. We need to flush the event queue, dealing only
-         * with exit events (VMDeath, VMDisconnect) so that we terminate
-         * correctly.
-         */
-        EventQueue queue = vm.eventQueue();
-        while (connected) {
-            try {
-                EventSet eventSet = queue.remove();
-                EventIterator iter = eventSet.eventIterator();
-                while (iter.hasNext()) {
-                    handleExitEvent(iter.next());
-                }
-            } catch (InterruptedException exc) {
-                // ignore
-            } catch (InternalError exc) {
-                // ignore
-            }
-        }
-    }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIExecutionControl.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2014, 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.ObjectInput;
-import java.io.ObjectOutput;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Stream;
-import com.sun.jdi.ReferenceType;
-import com.sun.jdi.VirtualMachine;
-import jdk.jshell.spi.ExecutionControl;
-import static java.util.stream.Collectors.toMap;
-
-/**
- * Abstract JDI implementation of {@link jdk.jshell.spi.ExecutionControl}
- */
-public abstract class JDIExecutionControl extends StreamingExecutionControl implements ExecutionControl {
-
-    /**
-     * Mapping from class names to JDI {@link ReferenceType}.
-     */
-    private final Map<String, ReferenceType> toReferenceType = new HashMap<>();
-
-    /**
-     * Create an instance.
-     * @param out the output from the remote agent
-     * @param in the input to the remote agent
-     */
-    protected JDIExecutionControl(ObjectOutput out, ObjectInput in) {
-        super(out, in);
-    }
-
-    /**
-     * Returns the JDI {@link VirtualMachine} instance.
-     *
-     * @return the virtual machine
-     * @throws EngineTerminationException if the VM is dead/disconnected
-     */
-    protected abstract VirtualMachine vm() throws EngineTerminationException;
-
-    /**
-     * Redefine the specified classes. Where 'redefine' is, as in JDI and JVMTI,
-     * an in-place replacement of the classes (preserving class identity) --
-     * that is, existing references to the class do not need to be recompiled.
-     * This implementation uses JDI
-     * {@link com.sun.jdi.VirtualMachine#redefineClasses(java.util.Map) }.
-     * It will be unsuccessful if
-     * the signature of the class has changed (see the JDI spec). The
-     * JShell-core is designed to adapt to unsuccessful redefine.
-     */
-    @Override
-    public void redefine(ClassBytecodes[] cbcs)
-            throws ClassInstallException, EngineTerminationException {
-        try {
-            // Convert to the JDI ReferenceType to class bytes map form needed
-            // by JDI.
-            VirtualMachine vm = vm();
-            Map<ReferenceType, byte[]> rmp = Stream.of(cbcs)
-                    .collect(toMap(
-                            cbc -> referenceType(vm, cbc.name()),
-                            cbc -> cbc.bytecodes()));
-            // Attempt redefine.  Throws exceptions on failure.
-            vm().redefineClasses(rmp);
-        } catch (EngineTerminationException ex) {
-            throw ex;
-        } catch (Exception ex) {
-            throw new ClassInstallException("redefine: " + ex.getMessage(), new boolean[cbcs.length]);
-        }
-    }
-
-    /**
-     * Returns the JDI {@link ReferenceType} corresponding to the specified
-     * class name.
-     *
-     * @param vm the current JDI {@link VirtualMachine} as returned by
-     * {@code vm()}
-     * @param name the class name to look-up
-     * @return the corresponding {@link ReferenceType}
-     */
-    protected ReferenceType referenceType(VirtualMachine vm, String name) {
-        return toReferenceType.computeIfAbsent(name, n -> nameToRef(vm, n));
-    }
-
-    private static ReferenceType nameToRef(VirtualMachine vm, String name) {
-        List<ReferenceType> rtl = vm.classesByName(name);
-        if (rtl.size() != 1) {
-            return null;
-        }
-        return rtl.get(0);
-    }
-
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIInitiator.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,223 +0,0 @@
-/*
- * 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.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import com.sun.jdi.Bootstrap;
-import com.sun.jdi.VirtualMachine;
-import com.sun.jdi.connect.Connector;
-import com.sun.jdi.connect.LaunchingConnector;
-import com.sun.jdi.connect.ListeningConnector;
-
-/**
- * Sets up a JDI connection, providing the resulting JDI {@link VirtualMachine}
- * and the {@link Process} the remote agent is running in.
- */
-public class JDIInitiator {
-
-    private VirtualMachine vm;
-    private Process process = null;
-    private final Connector connector;
-    private final String remoteAgent;
-    private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
-
-    /**
-     * Start the remote agent and establish a JDI connection to it.
-     *
-     * @param port the socket port for (non-JDI) commands
-     * @param remoteVMOptions any user requested VM options
-     * @param remoteAgent full class name of 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)
-     */
-    public JDIInitiator(int port, List<String> remoteVMOptions, String remoteAgent,
-            boolean isLaunch, String host) {
-        this.remoteAgent = remoteAgent;
-        String connectorName
-                = isLaunch
-                        ? "com.sun.jdi.CommandLineLaunch"
-                        : "com.sun.jdi.SocketListen";
-        this.connector = findConnector(connectorName);
-        if (connector == null) {
-            throw new IllegalArgumentException("No connector named: " + connectorName);
-        }
-        Map<String, String> argumentName2Value
-                = isLaunch
-                        ? launchArgs(port, String.join(" ", remoteVMOptions))
-                        : new HashMap<>();
-        if (host != null && !isLaunch) {
-            argumentName2Value.put("localAddress", host);
-        }
-        this.connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
-        this.vm = isLaunch
-                ? launchTarget()
-                : listenTarget(port, remoteVMOptions);
-
-    }
-
-    /**
-     * Returns the resulting {@code VirtualMachine} instance.
-     *
-     * @return the virtual machine
-     */
-    public VirtualMachine vm() {
-        return vm;
-    }
-
-    /**
-     * Returns the launched process.
-     *
-     * @return the remote agent process
-     */
-    public Process process() {
-        return process;
-    }
-
-    /* launch child target vm */
-    private VirtualMachine launchTarget() {
-        LaunchingConnector launcher = (LaunchingConnector) connector;
-        try {
-            VirtualMachine new_vm = launcher.launch(connectorArgs);
-            process = new_vm.process();
-            return new_vm;
-        } catch (Exception ex) {
-            reportLaunchFail(ex, "launch");
-        }
-        return null;
-    }
-
-    /**
-     * Directly launch the remote agent and connect JDI to it with a
-     * ListeningConnector.
-     */
-    private VirtualMachine listenTarget(int port, List<String> remoteVMOptions) {
-        ListeningConnector listener = (ListeningConnector) connector;
-        try {
-            // Start listening, get the JDI connection address
-            String addr = listener.startListening(connectorArgs);
-            debug("Listening at address: " + addr);
-
-            // Launch the RemoteAgent requesting a connection on that address
-            String javaHome = System.getProperty("java.home");
-            List<String> args = new ArrayList<>();
-            args.add(javaHome == null
-                    ? "java"
-                    : javaHome + File.separator + "bin" + File.separator + "java");
-            args.add("-agentlib:jdwp=transport=" + connector.transport().name() +
-                    ",address=" + addr);
-            args.addAll(remoteVMOptions);
-            args.add(remoteAgent);
-            args.add("" + port);
-            ProcessBuilder pb = new ProcessBuilder(args);
-            process = pb.start();
-
-            // Forward out, err, and in
-            // Accept the connection from the remote agent
-            vm = listener.accept(connectorArgs);
-            listener.stopListening(connectorArgs);
-            return vm;
-        } catch (Exception ex) {
-            reportLaunchFail(ex, "listen");
-        }
-        return null;
-    }
-
-    private Connector findConnector(String name) {
-        for (Connector cntor
-                : Bootstrap.virtualMachineManager().allConnectors()) {
-            if (cntor.name().equals(name)) {
-                return cntor;
-            }
-        }
-        return null;
-    }
-
-    private Map<String, Connector.Argument> mergeConnectorArgs(Connector connector, Map<String, String> argumentName2Value) {
-        Map<String, Connector.Argument> arguments = connector.defaultArguments();
-
-        for (Entry<String, String> argumentEntry : argumentName2Value.entrySet()) {
-            String name = argumentEntry.getKey();
-            String value = argumentEntry.getValue();
-            Connector.Argument argument = arguments.get(name);
-
-            if (argument == null) {
-                throw new IllegalArgumentException("Argument is not defined for connector:" +
-                        name + " -- " + connector.name());
-            }
-
-            argument.setValue(value);
-        }
-
-        return arguments;
-    }
-
-    /**
-     * The JShell specific Connector args for the LaunchingConnector.
-     *
-     * @param portthe socket port for (non-JDI) commands
-     * @param remoteVMOptions any user requested VM options
-     * @return the argument map
-     */
-    private Map<String, String> launchArgs(int port, String remoteVMOptions) {
-        Map<String, String> argumentName2Value = new HashMap<>();
-        argumentName2Value.put("main", remoteAgent + " " + port);
-        argumentName2Value.put("options", remoteVMOptions);
-        return argumentName2Value;
-    }
-
-    private void reportLaunchFail(Exception ex, String context) {
-        throw new InternalError("Failed remote " + context + ": " + connector +
-                " -- " + connectorArgs, ex);
-    }
-
-    /**
-     * Log debugging information. Arguments as for {@code printf}.
-     *
-     * @param format a format string as described in Format string syntax
-     * @param args arguments referenced by the format specifiers in the format
-     * string.
-     */
-    private void debug(String format, Object... args) {
-        // Reserved for future logging
-    }
-
-    /**
-     * Log a serious unexpected internal exception.
-     *
-     * @param ex the exception
-     * @param where a description of the context of the exception
-     */
-    private void debug(Throwable ex, String where) {
-        // Reserved for future logging
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,282 @@
+/*
+ * 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.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.BooleanValue;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.Field;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionEnv;
+import static jdk.jshell.execution.Util.remoteInputOutput;
+
+/**
+ * The implementation of {@link jdk.jshell.spi.ExecutionControl} that the
+ * JShell-core uses by default.
+ * Launches a remote process -- the "remote agent".
+ * Interfaces to the remote agent over a socket and via JDI.
+ * Designed to work with {@link RemoteExecutionControl}.
+ *
+ * @author Robert Field
+ * @author Jan Lahoda
+ */
+public class JdiDefaultExecutionControl extends JdiExecutionControl {
+
+    private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
+
+    private VirtualMachine vm;
+    private Process process;
+
+    private final Object STOP_LOCK = new Object();
+    private boolean userCodeRunning = false;
+
+    /**
+     * Creates an ExecutionControl instance based on a JDI
+     * {@code LaunchingConnector}.
+     *
+     * @return the generator
+     */
+    public static ExecutionControl.Generator launch() {
+        return env -> create(env, true, null);
+    }
+
+    /**
+     * Creates an ExecutionControl instance based on a JDI
+     * {@code ListeningConnector}.
+     *
+     * @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 env -> create(env, false, host);
+    }
+
+    /**
+     * 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) }
+     * @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)
+     * @return the channel
+     * @throws IOException if there are errors in set-up
+     */
+    private static ExecutionControl create(ExecutionEnv env,
+            boolean isLaunch, String host) throws IOException {
+        try (final ServerSocket listener = new ServerSocket(0)) {
+            // timeout after 60 seconds
+            listener.setSoTimeout(60000);
+            int port = listener.getLocalPort();
+
+            // Set-up the JDI connection
+            JdiInitiator jdii = new JdiInitiator(port,
+                    env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch, host);
+            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());
+            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));
+        }
+    }
+
+    /**
+     * Create an instance.
+     *
+     * @param cmdout the output for commands
+     * @param cmdin the input for responses
+     */
+    private JdiDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
+            VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
+        super(cmdout, cmdin);
+        this.vm = vm;
+        this.process = process;
+        deathListeners.add(s -> disposeVM());
+    }
+
+    @Override
+    public String invoke(String classname, String methodname)
+            throws RunException,
+            EngineTerminationException, InternalException {
+        String res;
+        synchronized (STOP_LOCK) {
+            userCodeRunning = true;
+        }
+        try {
+            res = super.invoke(classname, methodname);
+        } finally {
+            synchronized (STOP_LOCK) {
+                userCodeRunning = false;
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Interrupts a running remote invoke by manipulating remote variables
+     * and sending a stop via JDI.
+     *
+     * @throws EngineTerminationException the execution engine has terminated
+     * @throws InternalException an internal problem occurred
+     */
+    @Override
+    public void stop() throws EngineTerminationException, InternalException {
+        synchronized (STOP_LOCK) {
+            if (!userCodeRunning) {
+                return;
+            }
+
+            vm().suspend();
+            try {
+                OUTER:
+                for (ThreadReference thread : vm().allThreads()) {
+                    // could also tag the thread (e.g. using name), to find it easier
+                    for (StackFrame frame : thread.frames()) {
+                        if (REMOTE_AGENT.equals(frame.location().declaringType().name()) &&
+                                (    "invoke".equals(frame.location().method().name())
+                                || "varValue".equals(frame.location().method().name()))) {
+                            ObjectReference thiz = frame.thisObject();
+                            Field inClientCode = thiz.referenceType().fieldByName("inClientCode");
+                            Field expectingStop = thiz.referenceType().fieldByName("expectingStop");
+                            Field stopException = thiz.referenceType().fieldByName("stopException");
+                            if (((BooleanValue) thiz.getValue(inClientCode)).value()) {
+                                thiz.setValue(expectingStop, vm().mirrorOf(true));
+                                ObjectReference stopInstance = (ObjectReference) thiz.getValue(stopException);
+
+                                vm().resume();
+                                debug("Attempting to stop the client code...\n");
+                                thread.stop(stopInstance);
+                                thiz.setValue(expectingStop, vm().mirrorOf(false));
+                            }
+
+                            break OUTER;
+                        }
+                    }
+                }
+            } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException ex) {
+                throw new InternalException("Exception on remote stop: " + ex);
+            } finally {
+                vm().resume();
+            }
+        }
+    }
+
+    @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 ex) {
+            debug(ex, "disposeVM");
+        } 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;
+        }
+    }
+
+    /**
+     * Log debugging information. Arguments as for {@code printf}.
+     *
+     * @param format a format string as described in Format string syntax
+     * @param args arguments referenced by the format specifiers in the format
+     * string.
+     */
+    private static void debug(String format, Object... args) {
+        // Reserved for future logging
+    }
+
+    /**
+     * Log a serious unexpected internal exception.
+     *
+     * @param ex the exception
+     * @param where a description of the context of the exception
+     */
+    private static void debug(Throwable ex, String where) {
+        // Reserved for future logging
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiEventHandler.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1998, 2014, 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.function.Consumer;
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+
+/**
+ * Handler of Java Debug Interface events.
+ * Adapted from jdb EventHandler.
+ * Only exit and disconnect events processed.
+ */
+class JdiEventHandler implements Runnable {
+
+    private final Thread thread;
+    private volatile boolean connected = true;
+    private boolean completed = false;
+    private final VirtualMachine vm;
+    private final Consumer<String> reportVMExit;
+
+    /**
+     * Creates an event handler. Start with {@code start()}.
+     *
+     * @param vm the virtual machine for which to handle events
+     * @param reportVMExit callback to report exit/disconnect
+     * (passed true if the VM has died)
+     */
+    JdiEventHandler(VirtualMachine vm, Consumer<String> reportVMExit) {
+        this.vm = vm;
+        this.reportVMExit = reportVMExit;
+        this.thread = new Thread(this, "event-handler");
+        this.thread.setDaemon(true);
+    }
+
+    /**
+     * Starts the event handler.
+     */
+    void start() {
+        thread.start();
+    }
+
+    synchronized void shutdown() {
+        connected = false;  // force run() loop termination
+        thread.interrupt();
+        while (!completed) {
+            try {wait();} catch (InterruptedException exc) {}
+        }
+    }
+
+    @Override
+    public void run() {
+        EventQueue queue = vm.eventQueue();
+        while (connected) {
+            try {
+                EventSet eventSet = queue.remove();
+                boolean resumeStoppedApp = false;
+                EventIterator it = eventSet.eventIterator();
+                while (it.hasNext()) {
+                    resumeStoppedApp |= handleEvent(it.nextEvent());
+                }
+
+                if (resumeStoppedApp) {
+                    eventSet.resume();
+                }
+            } catch (InterruptedException exc) {
+                // Do nothing. Any changes will be seen at top of loop.
+            } catch (VMDisconnectedException discExc) {
+                handleDisconnectedException();
+                break;
+            }
+        }
+        synchronized (this) {
+            completed = true;
+            notifyAll();
+        }
+    }
+
+    private boolean handleEvent(Event event) {
+        handleExitEvent(event);
+        return true;
+    }
+
+    private void handleExitEvent(Event event) {
+        if (event instanceof VMDeathEvent) {
+            reportVMExit.accept("VM Died");
+        } else if (event instanceof VMDisconnectEvent) {
+            connected = false;
+            reportVMExit.accept("VM Disconnected");
+        } else {
+            // ignore everything else
+        }
+    }
+
+    private synchronized void handleDisconnectedException() {
+        /*
+         * A VMDisconnectedException has happened while dealing with
+         * another event. We need to flush the event queue, dealing only
+         * with exit events (VMDeath, VMDisconnect) so that we terminate
+         * correctly.
+         */
+        EventQueue queue = vm.eventQueue();
+        while (connected) {
+            try {
+                EventSet eventSet = queue.remove();
+                EventIterator iter = eventSet.eventIterator();
+                while (iter.hasNext()) {
+                    handleExitEvent(iter.next());
+                }
+            } catch (InterruptedException exc) {
+                // ignore
+            } catch (InternalError exc) {
+                // ignore
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControl.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014, 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.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.spi.ExecutionControl;
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * Abstract JDI implementation of {@link jdk.jshell.spi.ExecutionControl}
+ */
+public abstract class JdiExecutionControl extends StreamingExecutionControl implements ExecutionControl {
+
+    /**
+     * Mapping from class names to JDI {@link ReferenceType}.
+     */
+    private final Map<String, ReferenceType> toReferenceType = new HashMap<>();
+
+    /**
+     * Create an instance.
+     * @param out the output from the remote agent
+     * @param in the input to the remote agent
+     */
+    protected JdiExecutionControl(ObjectOutput out, ObjectInput in) {
+        super(out, in);
+    }
+
+    /**
+     * Returns the JDI {@link VirtualMachine} instance.
+     *
+     * @return the virtual machine
+     * @throws EngineTerminationException if the VM is dead/disconnected
+     */
+    protected abstract VirtualMachine vm() throws EngineTerminationException;
+
+    /**
+     * Redefine the specified classes. Where 'redefine' is, as in JDI and JVMTI,
+     * an in-place replacement of the classes (preserving class identity) --
+     * that is, existing references to the class do not need to be recompiled.
+     * This implementation uses JDI
+     * {@link com.sun.jdi.VirtualMachine#redefineClasses(java.util.Map) }.
+     * It will be unsuccessful if
+     * the signature of the class has changed (see the JDI spec). The
+     * JShell-core is designed to adapt to unsuccessful redefine.
+     */
+    @Override
+    public void redefine(ClassBytecodes[] cbcs)
+            throws ClassInstallException, EngineTerminationException {
+        try {
+            // Convert to the JDI ReferenceType to class bytes map form needed
+            // by JDI.
+            VirtualMachine vm = vm();
+            Map<ReferenceType, byte[]> rmp = Stream.of(cbcs)
+                    .collect(toMap(
+                            cbc -> referenceType(vm, cbc.name()),
+                            cbc -> cbc.bytecodes()));
+            // Attempt redefine.  Throws exceptions on failure.
+            vm().redefineClasses(rmp);
+        } catch (EngineTerminationException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ClassInstallException("redefine: " + ex.getMessage(), new boolean[cbcs.length]);
+        }
+    }
+
+    /**
+     * Returns the JDI {@link ReferenceType} corresponding to the specified
+     * class name.
+     *
+     * @param vm the current JDI {@link VirtualMachine} as returned by
+     * {@code vm()}
+     * @param name the class name to look-up
+     * @return the corresponding {@link ReferenceType}
+     */
+    protected ReferenceType referenceType(VirtualMachine vm, String name) {
+        return toReferenceType.computeIfAbsent(name, n -> nameToRef(vm, n));
+    }
+
+    private static ReferenceType nameToRef(VirtualMachine vm, String name) {
+        List<ReferenceType> rtl = vm.classesByName(name);
+        if (rtl.size() != 1) {
+            return null;
+        }
+        return rtl.get(0);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,223 @@
+/*
+ * 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.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.LaunchingConnector;
+import com.sun.jdi.connect.ListeningConnector;
+
+/**
+ * Sets up a JDI connection, providing the resulting JDI {@link VirtualMachine}
+ * and the {@link Process} the remote agent is running in.
+ */
+public class JdiInitiator {
+
+    private VirtualMachine vm;
+    private Process process = null;
+    private final Connector connector;
+    private final String remoteAgent;
+    private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
+
+    /**
+     * Start the remote agent and establish a JDI connection to it.
+     *
+     * @param port the socket port for (non-JDI) commands
+     * @param remoteVMOptions any user requested VM options
+     * @param remoteAgent full class name of 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)
+     */
+    public JdiInitiator(int port, List<String> remoteVMOptions, String remoteAgent,
+            boolean isLaunch, String host) {
+        this.remoteAgent = remoteAgent;
+        String connectorName
+                = isLaunch
+                        ? "com.sun.jdi.CommandLineLaunch"
+                        : "com.sun.jdi.SocketListen";
+        this.connector = findConnector(connectorName);
+        if (connector == null) {
+            throw new IllegalArgumentException("No connector named: " + connectorName);
+        }
+        Map<String, String> argumentName2Value
+                = isLaunch
+                        ? launchArgs(port, String.join(" ", remoteVMOptions))
+                        : new HashMap<>();
+        if (host != null && !isLaunch) {
+            argumentName2Value.put("localAddress", host);
+        }
+        this.connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
+        this.vm = isLaunch
+                ? launchTarget()
+                : listenTarget(port, remoteVMOptions);
+
+    }
+
+    /**
+     * Returns the resulting {@code VirtualMachine} instance.
+     *
+     * @return the virtual machine
+     */
+    public VirtualMachine vm() {
+        return vm;
+    }
+
+    /**
+     * Returns the launched process.
+     *
+     * @return the remote agent process
+     */
+    public Process process() {
+        return process;
+    }
+
+    /* launch child target vm */
+    private VirtualMachine launchTarget() {
+        LaunchingConnector launcher = (LaunchingConnector) connector;
+        try {
+            VirtualMachine new_vm = launcher.launch(connectorArgs);
+            process = new_vm.process();
+            return new_vm;
+        } catch (Exception ex) {
+            reportLaunchFail(ex, "launch");
+        }
+        return null;
+    }
+
+    /**
+     * Directly launch the remote agent and connect JDI to it with a
+     * ListeningConnector.
+     */
+    private VirtualMachine listenTarget(int port, List<String> remoteVMOptions) {
+        ListeningConnector listener = (ListeningConnector) connector;
+        try {
+            // Start listening, get the JDI connection address
+            String addr = listener.startListening(connectorArgs);
+            debug("Listening at address: " + addr);
+
+            // Launch the RemoteAgent requesting a connection on that address
+            String javaHome = System.getProperty("java.home");
+            List<String> args = new ArrayList<>();
+            args.add(javaHome == null
+                    ? "java"
+                    : javaHome + File.separator + "bin" + File.separator + "java");
+            args.add("-agentlib:jdwp=transport=" + connector.transport().name() +
+                    ",address=" + addr);
+            args.addAll(remoteVMOptions);
+            args.add(remoteAgent);
+            args.add("" + port);
+            ProcessBuilder pb = new ProcessBuilder(args);
+            process = pb.start();
+
+            // Forward out, err, and in
+            // Accept the connection from the remote agent
+            vm = listener.accept(connectorArgs);
+            listener.stopListening(connectorArgs);
+            return vm;
+        } catch (Exception ex) {
+            reportLaunchFail(ex, "listen");
+        }
+        return null;
+    }
+
+    private Connector findConnector(String name) {
+        for (Connector cntor
+                : Bootstrap.virtualMachineManager().allConnectors()) {
+            if (cntor.name().equals(name)) {
+                return cntor;
+            }
+        }
+        return null;
+    }
+
+    private Map<String, Connector.Argument> mergeConnectorArgs(Connector connector, Map<String, String> argumentName2Value) {
+        Map<String, Connector.Argument> arguments = connector.defaultArguments();
+
+        for (Entry<String, String> argumentEntry : argumentName2Value.entrySet()) {
+            String name = argumentEntry.getKey();
+            String value = argumentEntry.getValue();
+            Connector.Argument argument = arguments.get(name);
+
+            if (argument == null) {
+                throw new IllegalArgumentException("Argument is not defined for connector:" +
+                        name + " -- " + connector.name());
+            }
+
+            argument.setValue(value);
+        }
+
+        return arguments;
+    }
+
+    /**
+     * The JShell specific Connector args for the LaunchingConnector.
+     *
+     * @param portthe socket port for (non-JDI) commands
+     * @param remoteVMOptions any user requested VM options
+     * @return the argument map
+     */
+    private Map<String, String> launchArgs(int port, String remoteVMOptions) {
+        Map<String, String> argumentName2Value = new HashMap<>();
+        argumentName2Value.put("main", remoteAgent + " " + port);
+        argumentName2Value.put("options", remoteVMOptions);
+        return argumentName2Value;
+    }
+
+    private void reportLaunchFail(Exception ex, String context) {
+        throw new InternalError("Failed remote " + context + ": " + connector +
+                " -- " + connectorArgs, ex);
+    }
+
+    /**
+     * Log debugging information. Arguments as for {@code printf}.
+     *
+     * @param format a format string as described in Format string syntax
+     * @param args arguments referenced by the format specifiers in the format
+     * string.
+     */
+    private void debug(String format, Object... args) {
+        // Reserved for future logging
+    }
+
+    /**
+     * Log a serious unexpected internal exception.
+     *
+     * @param ex the exception
+     * @param where a description of the context of the exception
+     */
+    private void debug(Throwable ex, String where) {
+        // Reserved for future logging
+    }
+
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java	Fri Nov 04 14:47:25 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java	Sun Nov 06 22:50:46 2016 -0800
@@ -41,7 +41,7 @@
  * process). This agent loads code over a socket from the main JShell process,
  * executes the code, and other misc, Specialization of
  * {@link DirectExecutionControl} which adds stop support controlled by
- * an external process. Designed to work with {@link JDIDefaultExecutionControl}.
+ * an external process. Designed to work with {@link JdiDefaultExecutionControl}.
  *
  * @author Jan Lahoda
  * @author Robert Field
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java	Fri Nov 04 14:47:25 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java	Sun Nov 06 22:50:46 2016 -0800
@@ -239,9 +239,9 @@
      * @param unbiddenExitHandler the handler, which will accept the exit
      * information
      */
-    public static void detectJDIExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) {
+    public static void detectJdiExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) {
         if (vm.canBeModified()) {
-            new JDIEventHandler(vm, unbiddenExitHandler).start();
+            new JdiEventHandler(vm, unbiddenExitHandler).start();
         }
     }
 
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java	Fri Nov 04 14:47:25 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java	Sun Nov 06 22:50:46 2016 -0800
@@ -44,12 +44,13 @@
  * To install an {@code ExecutionControl}, its {@code Generator} is passed to
  * {@link jdk.jshell.JShell.Builder#executionEngine(ExecutionControl.Generator)  }.
  */
-public interface ExecutionControl {
+public interface ExecutionControl extends AutoCloseable {
 
     /**
      * Defines a functional interface for creating {@link ExecutionControl}
      * instances.
      */
+    @FunctionalInterface
     public interface Generator {
 
         /**
--- a/langtools/test/jdk/jshell/FailOverExecutionControlTest.java	Fri Nov 04 14:47:25 2016 -0700
+++ b/langtools/test/jdk/jshell/FailOverExecutionControlTest.java	Sun Nov 06 22:50:46 2016 -0800
@@ -33,7 +33,7 @@
 
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JDIDefaultExecutionControl;
+import jdk.jshell.execution.JdiDefaultExecutionControl;
 import jdk.jshell.spi.ExecutionControl;
 import jdk.jshell.spi.ExecutionEnv;
 import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
@@ -47,7 +47,7 @@
         setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
                 new AlwaysFailingGenerator(),
                 new AlwaysFailingGenerator(),
-                JDIDefaultExecutionControl.launch())));
+                JdiDefaultExecutionControl.launch())));
     }
 
     class AlwaysFailingGenerator implements ExecutionControl.Generator {
--- a/langtools/test/jdk/jshell/JDILaunchingExecutionControlTest.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * 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 8164518
- * @summary Tests for standard JDI connector (without failover) -- launching
- * @modules jdk.jshell/jdk.jshell.execution
- * @build KullaTesting ExecutionControlTestBase
- * @run testng JDILaunchingExecutionControlTest
- */
-
-
-import org.testng.annotations.Test;
-import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JDIDefaultExecutionControl;
-
-@Test
-public class JDILaunchingExecutionControlTest extends ExecutionControlTestBase {
-
-    @BeforeMethod
-    @Override
-    public void setUp() {
-        setUp(builder -> builder.executionEngine(JDIDefaultExecutionControl.launch()));
-    }
-}
--- a/langtools/test/jdk/jshell/JDIListeningExecutionControlTest.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * 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 8131029 8159935 8160127 8164518
- * @summary Tests for alternate JDI connector -- listening
- * @modules jdk.jshell/jdk.jshell.execution
- * @build KullaTesting ExecutionControlTestBase
- * @run testng JDIListeningExecutionControlTest
- */
-
-
-import org.testng.annotations.Test;
-import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JDIDefaultExecutionControl;
-
-@Test
-public class JDIListeningExecutionControlTest extends ExecutionControlTestBase {
-
-    @BeforeMethod
-    @Override
-    public void setUp() {
-        setUp(builder -> builder.executionEngine(JDIDefaultExecutionControl.listen(null)));
-    }
-}
--- a/langtools/test/jdk/jshell/JDIListeningLocalhostExecutionControlTest.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * 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 8164518
- * @summary Tests for alternate JDI connector -- listening to "localhost"
- * @modules jdk.jshell/jdk.jshell.execution
- * @build KullaTesting ExecutionControlTestBase
- * @run testng JDIListeningLocalhostExecutionControlTest
- */
-
-
-import org.testng.annotations.Test;
-import org.testng.annotations.BeforeMethod;
-import jdk.jshell.execution.JDIDefaultExecutionControl;
-
-@Test
-public class JDIListeningLocalhostExecutionControlTest extends ExecutionControlTestBase {
-
-    @BeforeMethod
-    @Override
-    public void setUp() {
-        setUp(builder -> builder.executionEngine(JDIDefaultExecutionControl.listen("localhost")));
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/JdiLaunchingExecutionControlTest.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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 8164518
+ * @summary Tests for standard JDI connector (without failover) -- launching
+ * @modules jdk.jshell/jdk.jshell.execution
+ * @build KullaTesting ExecutionControlTestBase
+ * @run testng JdiLaunchingExecutionControlTest
+ */
+
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.execution.JdiDefaultExecutionControl;
+
+@Test
+public class JdiLaunchingExecutionControlTest extends ExecutionControlTestBase {
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.launch()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/JdiListeningExecutionControlTest.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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 8131029 8159935 8160127 8164518
+ * @summary Tests for alternate JDI connector -- listening
+ * @modules jdk.jshell/jdk.jshell.execution
+ * @build KullaTesting ExecutionControlTestBase
+ * @run testng JdiListeningExecutionControlTest
+ */
+
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.execution.JdiDefaultExecutionControl;
+
+@Test
+public class JdiListeningExecutionControlTest extends ExecutionControlTestBase {
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.listen(null)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/JdiListeningLocalhostExecutionControlTest.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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 8164518
+ * @summary Tests for alternate JDI connector -- listening to "localhost"
+ * @modules jdk.jshell/jdk.jshell.execution
+ * @build KullaTesting ExecutionControlTestBase
+ * @run testng JdiListeningLocalhostExecutionControlTest
+ */
+
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.execution.JdiDefaultExecutionControl;
+
+@Test
+public class JdiListeningLocalhostExecutionControlTest extends ExecutionControlTestBase {
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        setUp(builder -> builder.executionEngine(JdiDefaultExecutionControl.listen("localhost")));
+    }
+}
--- a/langtools/test/jdk/jshell/UserJDIUserRemoteTest.java	Fri Nov 04 14:47:25 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/*
- * 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 8160128 8159935
- * @summary Tests for Aux channel, custom remote agents, custom JDI implementations.
- * @build KullaTesting ExecutionControlTestBase
- * @run testng UserJDIUserRemoteTest
- */
-import java.io.ByteArrayOutputStream;
-import org.testng.annotations.Test;
-import org.testng.annotations.BeforeMethod;
-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 {
-
-    ExecutionControl currentEC;
-    ByteArrayOutputStream auxStream;
-
-    @BeforeMethod
-    @Override
-    public void setUp() {
-        auxStream = new ByteArrayOutputStream();
-        setUp(builder -> builder.executionEngine(MyExecutionControl.create(this)));
-    }
-
-    public void testVarValue() {
-        VarSnippet dv = varKey(assertEval("double aDouble = 1.5;"));
-        String vd = getState().varValue(dv);
-        assertEquals(vd, "1.5");
-        assertEquals(auxStream.toString(), "aDouble");
-    }
-
-    public void testExtension() throws ExecutionControlException {
-        assertEval("42;");
-        Object res = currentEC.extensionCommand("FROG", "test");
-        assertEquals(res, "ribbit");
-    }
-
-    public void testRedefine() {
-        Snippet vx = varKey(assertEval("int x;"));
-        Snippet mu = methodKey(assertEval("int mu() { return x * 4; }"));
-        Snippet c = classKey(assertEval("class C { String v() { return \"#\" + mu(); } }"));
-        assertEval("C c0  = new C();");
-        assertEval("c0.v();", "\"#0\"");
-        assertEval("int x = 10;", "10",
-                ste(MAIN_SNIPPET, VALID, VALID, false, null),
-                ste(vx, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
-        assertEval("c0.v();", "\"#40\"");
-        assertEval("C c = new C();");
-        assertEval("c.v();", "\"#40\"");
-        assertEval("int mu() { return x * 3; }",
-                ste(MAIN_SNIPPET, VALID, VALID, false, null),
-                ste(mu, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
-        assertEval("c.v();", "\"#30\"");
-        assertEval("class C { String v() { return \"@\" + mu(); } }",
-                ste(MAIN_SNIPPET, VALID, VALID, false, null),
-                ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
-        assertEval("c0.v();", "\"@30\"");
-        assertEval("c = new C();");
-        assertEval("c.v();", "\"@30\"");
-        assertActiveKeys();
-    }
-}
-
-class MyExecutionControl extends JDIExecutionControl {
-
-    private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName();
-
-    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 after 60 seconds
-            listener.setSoTimeout(60000);
-            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);
-            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/langtools/test/jdk/jshell/UserJdiUserRemoteTest.java	Sun Nov 06 22:50:46 2016 -0800
@@ -0,0 +1,286 @@
+/*
+ * 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 8160128 8159935
+ * @summary Tests for Aux channel, custom remote agents, custom JDI implementations.
+ * @build KullaTesting ExecutionControlTestBase
+ * @run testng UserJdiUserRemoteTest
+ */
+import java.io.ByteArrayOutputStream;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+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 {
+
+    ExecutionControl currentEC;
+    ByteArrayOutputStream auxStream;
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        auxStream = new ByteArrayOutputStream();
+        setUp(builder -> builder.executionEngine(MyExecutionControl.create(this)));
+    }
+
+    public void testVarValue() {
+        VarSnippet dv = varKey(assertEval("double aDouble = 1.5;"));
+        String vd = getState().varValue(dv);
+        assertEquals(vd, "1.5");
+        assertEquals(auxStream.toString(), "aDouble");
+    }
+
+    public void testExtension() throws ExecutionControlException {
+        assertEval("42;");
+        Object res = currentEC.extensionCommand("FROG", "test");
+        assertEquals(res, "ribbit");
+    }
+
+    public void testRedefine() {
+        Snippet vx = varKey(assertEval("int x;"));
+        Snippet mu = methodKey(assertEval("int mu() { return x * 4; }"));
+        Snippet c = classKey(assertEval("class C { String v() { return \"#\" + mu(); } }"));
+        assertEval("C c0  = new C();");
+        assertEval("c0.v();", "\"#0\"");
+        assertEval("int x = 10;", "10",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(vx, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c0.v();", "\"#40\"");
+        assertEval("C c = new C();");
+        assertEval("c.v();", "\"#40\"");
+        assertEval("int mu() { return x * 3; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(mu, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c.v();", "\"#30\"");
+        assertEval("class C { String v() { return \"@\" + mu(); } }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c0.v();", "\"@30\"");
+        assertEval("c = new C();");
+        assertEval("c.v();", "\"@30\"");
+        assertActiveKeys();
+    }
+}
+
+class MyExecutionControl extends JdiExecutionControl {
+
+    private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName();
+
+    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 after 60 seconds
+            listener.setSoTimeout(60000);
+            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);
+            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";
+    }
+
+}