langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java
changeset 42272 82e273c4f2b3
parent 41995 1ac75bf2dc3a
child 42969 a48d4f74d322
equal deleted inserted replaced
42271:2537564a3031 42272:82e273c4f2b3
    61  * @author Robert Field
    61  * @author Robert Field
    62  * @author Jan Lahoda
    62  * @author Jan Lahoda
    63  */
    63  */
    64 public class JdiDefaultExecutionControl extends JdiExecutionControl {
    64 public class JdiDefaultExecutionControl extends JdiExecutionControl {
    65 
    65 
    66     private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
    66     /**
       
    67      * Default time-out expressed in milliseconds.
       
    68      */
       
    69     private static final int DEFAULT_TIMEOUT = 5000;
    67 
    70 
    68     private VirtualMachine vm;
    71     private VirtualMachine vm;
    69     private Process process;
    72     private Process process;
    70 
    73 
    71     private final Object STOP_LOCK = new Object();
    74     private final Object STOP_LOCK = new Object();
    72     private boolean userCodeRunning = false;
    75     private boolean userCodeRunning = false;
    73 
    76 
    74     /**
    77     /**
    75      * Creates an ExecutionControl instance based on a JDI
    78      * Creates an ExecutionControl instance based on a JDI
    76      * {@code LaunchingConnector}.
    79      * {@code LaunchingConnector}. Same as
       
    80      * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), true, null, defaultTimeout())}.
    77      *
    81      *
    78      * @return the generator
    82      * @return the generator
    79      */
    83      */
    80     public static ExecutionControl.Generator launch() {
    84     public static ExecutionControl.Generator launch() {
    81         return env -> create(env, true, null);
    85         return create(defaultRemoteAgent(), true, null, defaultTimeout());
    82     }
    86     }
    83 
    87 
    84     /**
    88     /**
    85      * Creates an ExecutionControl instance based on a JDI
    89      * Creates an ExecutionControl instance based on a JDI
    86      * {@code ListeningConnector}.
    90      * {@code ListeningConnector}. Same as
       
    91      * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), false, host, defaultTimeout())}.
    87      *
    92      *
    88      * @param host explicit hostname to use, if null use discovered
    93      * @param host explicit hostname to use, if null use discovered
    89      * hostname, applies to listening only (!isLaunch)
    94      * hostname, applies to listening only (!isLaunch)
    90      * @return the generator
    95      * @return the generator
    91      */
    96      */
    92     public static ExecutionControl.Generator listen(String host) {
    97     public static ExecutionControl.Generator listen(String host) {
    93         return env -> create(env, false, host);
    98         return create(defaultRemoteAgent(), false, host, defaultTimeout());
       
    99     }
       
   100 
       
   101     /**
       
   102      * Creates a JDI based ExecutionControl instance.
       
   103      *
       
   104      * @param remoteAgent the remote agent to launch
       
   105      * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
       
   106      * otherwise we start explicitly and use ListeningConnector
       
   107      * @param host explicit hostname to use, if null use discovered
       
   108      * hostname, applies to listening only (!isLaunch)
       
   109      * @param timeout the start-up time-out in milliseconds
       
   110      * @return the generator
       
   111      */
       
   112     public static ExecutionControl.Generator create(String remoteAgent,
       
   113             boolean isLaunch, String host, int timeout) {
       
   114         return env -> create(env, remoteAgent, isLaunch, host, timeout);
       
   115     }
       
   116 
       
   117     /**
       
   118      * Default remote agent.
       
   119      *
       
   120      * @return the name of the standard remote agent
       
   121      */
       
   122     public static String defaultRemoteAgent() {
       
   123         return RemoteExecutionControl.class.getName();
       
   124     }
       
   125 
       
   126     /**
       
   127      * Default remote connection time-out
       
   128      *
       
   129      * @return time to wait for connection before failing, expressed in milliseconds.
       
   130      */
       
   131     public static int defaultTimeout() {
       
   132         return DEFAULT_TIMEOUT;
    94     }
   133     }
    95 
   134 
    96     /**
   135     /**
    97      * Creates an ExecutionControl instance based on a JDI
   136      * Creates an ExecutionControl instance based on a JDI
    98      * {@code ListeningConnector} or {@code LaunchingConnector}.
   137      * {@code ListeningConnector} or {@code LaunchingConnector}.
   101      * commands and results. This socket also transports the user
   140      * commands and results. This socket also transports the user
   102      * input/output/error.
   141      * input/output/error.
   103      *
   142      *
   104      * @param env the context passed by
   143      * @param env the context passed by
   105      * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
   144      * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
       
   145      * @param remoteAgent the remote agent to launch
   106      * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
   146      * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
   107      * otherwise we start explicitly and use ListeningConnector
   147      * otherwise we start explicitly and use ListeningConnector
   108      * @param host explicit hostname to use, if null use discovered
   148      * @param host explicit hostname to use, if null use discovered
   109      * hostname, applies to listening only (!isLaunch)
   149      * hostname, applies to listening only (!isLaunch)
   110      * @return the channel
   150      * @return the channel
   111      * @throws IOException if there are errors in set-up
   151      * @throws IOException if there are errors in set-up
   112      */
   152      */
   113     private static ExecutionControl create(ExecutionEnv env,
   153     private static ExecutionControl create(ExecutionEnv env, String remoteAgent,
   114             boolean isLaunch, String host) throws IOException {
   154             boolean isLaunch, String host, int timeout) throws IOException {
   115         try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
   155         try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
   116             // timeout after 60 seconds
   156             // timeout on I/O-socket
   117             listener.setSoTimeout(60000);
   157             listener.setSoTimeout(timeout);
   118             int port = listener.getLocalPort();
   158             int port = listener.getLocalPort();
   119 
   159 
   120             // Set-up the JDI connection
   160             // Set-up the JDI connection
   121             JdiInitiator jdii = new JdiInitiator(port,
   161             JdiInitiator jdii = new JdiInitiator(port,
   122                     env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch, host);
   162                     env.extraRemoteVMOptions(), remoteAgent, isLaunch, host, timeout);
   123             VirtualMachine vm = jdii.vm();
   163             VirtualMachine vm = jdii.vm();
   124             Process process = jdii.process();
   164             Process process = jdii.process();
   125 
   165 
   126             List<Consumer<String>> deathListeners = new ArrayList<>();
   166             List<Consumer<String>> deathListeners = new ArrayList<>();
   127             deathListeners.add(s -> env.closeDown());
   167             deathListeners.add(s -> env.closeDown());
   195             try {
   235             try {
   196                 OUTER:
   236                 OUTER:
   197                 for (ThreadReference thread : vm().allThreads()) {
   237                 for (ThreadReference thread : vm().allThreads()) {
   198                     // could also tag the thread (e.g. using name), to find it easier
   238                     // could also tag the thread (e.g. using name), to find it easier
   199                     for (StackFrame frame : thread.frames()) {
   239                     for (StackFrame frame : thread.frames()) {
   200                         if (REMOTE_AGENT.equals(frame.location().declaringType().name()) &&
   240                         if (defaultRemoteAgent().equals(frame.location().declaringType().name()) &&
   201                                 (    "invoke".equals(frame.location().method().name())
   241                                 (    "invoke".equals(frame.location().method().name())
   202                                 || "varValue".equals(frame.location().method().name()))) {
   242                                 || "varValue".equals(frame.location().method().name()))) {
   203                             ObjectReference thiz = frame.thisObject();
   243                             ObjectReference thiz = frame.thisObject();
   204                             Field inClientCode = thiz.referenceType().fieldByName("inClientCode");
   244                             Field inClientCode = thiz.referenceType().fieldByName("inClientCode");
   205                             Field expectingStop = thiz.referenceType().fieldByName("expectingStop");
   245                             Field expectingStop = thiz.referenceType().fieldByName("expectingStop");