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"); |