27
28 import jdk.jshell.spi.ExecutionControl;
29 import java.io.ByteArrayInputStream;
30 import java.io.InputStream;
31 import java.io.PrintStream;
32 import java.text.MessageFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.MissingResourceException;
40 import java.util.Objects;
41 import java.util.ResourceBundle;
42 import java.util.function.BiFunction;
43 import java.util.function.Consumer;
44
45 import java.util.function.Supplier;
46 import jdk.internal.jshell.debug.InternalDebugControl;
47 import jdk.internal.jshell.jdi.FailOverExecutionControl;
48 import static java.util.stream.Collectors.collectingAndThen;
49 import static java.util.stream.Collectors.toList;
50 import static jdk.jshell.Util.expunge;
51 import jdk.jshell.Snippet.Status;
52 import jdk.internal.jshell.jdi.JDIExecutionControl;
53 import jdk.jshell.spi.ExecutionEnv;
54
55 /**
56 * The JShell evaluation state engine. This is the central class in the JShell
57 * API. A {@code JShell} instance holds the evolving compilation and
58 * execution state. The state is changed with the instance methods
59 * {@link jdk.jshell.JShell#eval(java.lang.String) eval(String)},
60 * {@link jdk.jshell.JShell#drop(jdk.jshell.PersistentSnippet) drop(PersistentSnippet)} and
61 * {@link jdk.jshell.JShell#addToClasspath(java.lang.String) addToClasspath(String)}.
62 * The majority of methods query the state.
63 * A {@code JShell} instance also allows registering for events with
64 * {@link jdk.jshell.JShell#onSnippetEvent(java.util.function.Consumer) onSnippetEvent(Consumer)}
65 * and {@link jdk.jshell.JShell#onShutdown(java.util.function.Consumer) onShutdown(Consumer)}, which
66 * are unregistered with
67 * {@link jdk.jshell.JShell#unsubscribe(jdk.jshell.JShell.Subscription) unsubscribe(Subscription)}.
68 * Access to the source analysis utilities is via
69 * {@link jdk.jshell.JShell#sourceCodeAnalysis()}.
70 * When complete the instance should be closed to free resources --
71 * {@link jdk.jshell.JShell#close()}.
72 * <p>
73 * An instance of {@code JShell} is created with
74 * {@code JShell.create()}.
75 * <p>
76 * This class is not thread safe, except as noted, all access should be through
77 * a single thread.
78 * @author Robert Field
79 */
80 public class JShell implements AutoCloseable {
81
82 final SnippetMaps maps;
83 final KeyMap keyMap;
84 final OuterWrapMap outerMap;
85 final TaskFactory taskFactory;
86 final InputStream in;
87 final PrintStream out;
88 final PrintStream err;
89 final Supplier<String> tempVariableNameGenerator;
90 final BiFunction<Snippet, Integer, String> idGenerator;
91 final List<String> extraRemoteVMOptions;
92 final List<String> extraCompilerOptions;
93 final ExecutionControl executionControl;
94
95 private int nextKeyIndex = 1;
96
97 final Eval eval;
98 private final Map<String, byte[]> classnameToBytes = new HashMap<>();
99 private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
100 private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
101 private boolean closed = false;
102
103 private boolean executionControlLaunched = false;
104 private SourceCodeAnalysisImpl sourceCodeAnalysis = null;
105
106 private static final String L10N_RB_NAME = "jdk.jshell.resources.l10n";
107 private static ResourceBundle outputRB = null;
108
109 JShell(Builder b) {
110 this.in = b.in;
111 this.out = b.out;
112 this.err = b.err;
113 this.tempVariableNameGenerator = b.tempVariableNameGenerator;
114 this.idGenerator = b.idGenerator;
115 this.extraRemoteVMOptions = b.extraRemoteVMOptions;
116 this.extraCompilerOptions = b.extraCompilerOptions;
117 this.executionControl = b.executionControl==null
118 ? new FailOverExecutionControl(
119 new JDIExecutionControl(),
120 new JDIExecutionControl(false))
121 : b.executionControl;
122
123 this.maps = new SnippetMaps(this);
124 this.keyMap = new KeyMap(this);
125 this.outerMap = new OuterWrapMap(this);
126 this.taskFactory = new TaskFactory(this);
127 this.eval = new Eval(this);
128 }
129
130 /**
131 * Builder for {@code JShell} instances.
132 * Create custom instances of {@code JShell} by using the setter
133 * methods on this class. After zero or more of these, use the
134 * {@link #build()} method to create a {@code JShell} instance.
135 * These can all be chained. For example, setting the remote output and
136 * error streams:
137 * <pre>
138 * {@code
139 * JShell myShell =
140 * JShell.builder()
141 * .out(myOutStream)
142 * .err(myErrStream)
143 * .build(); } </pre>
144 * If no special set-up is needed, just use
145 * {@code JShell.builder().build()} or the short-cut equivalent
146 * {@code JShell.create()}.
147 */
148 public static class Builder {
149
150 InputStream in = new ByteArrayInputStream(new byte[0]);
151 PrintStream out = System.out;
152 PrintStream err = System.err;
153 Supplier<String> tempVariableNameGenerator = null;
154 BiFunction<Snippet, Integer, String> idGenerator = null;
155 List<String> extraRemoteVMOptions = new ArrayList<>();
156 List<String> extraCompilerOptions = new ArrayList<>();
157 ExecutionControl executionControl;
158
159 Builder() { }
160
161 /**
162 * Sets the input for the running evaluation (it's {@code System.in}). Note:
163 * applications that use {@code System.in} for snippet or other
164 * user input cannot use {@code System.in} as the input stream for
165 * the remote process.
166 * <p>
167 * The default, if this is not set, is to provide an empty input stream
168 * -- {@code new ByteArrayInputStream(new byte[0])}.
169 *
170 * @param in the {@code InputStream} to be channelled to
171 * {@code System.in} in the remote execution process
172 * @return the {@code Builder} instance (for use in chained
173 * initialization)
174 */
175 public Builder in(InputStream in) {
176 this.in = in;
177 return this;
293
294 /**
295 * Adds compiler options. These additional options will be used on
296 * parsing, analysis, and code generation calls to the compiler.
297 * Options which interfere with results are not supported and have
298 * undefined effects on JShell's operation.
299 *
300 * @param options the addition options for compiler invocations
301 * @return the {@code Builder} instance (for use in chained
302 * initialization)
303 */
304 public Builder compilerOptions(String... options) {
305 this.extraCompilerOptions.addAll(Arrays.asList(options));
306 return this;
307 }
308
309 /**
310 * Sets the custom engine for execution. Snippet execution will be
311 * provided by the specified {@link ExecutionControl} instance.
312 *
313 * @param execEngine the execution engine
314 * @return the {@code Builder} instance (for use in chained
315 * initialization)
316 */
317 public Builder executionEngine(ExecutionControl execEngine) {
318 this.executionControl = execEngine;
319 return this;
320 }
321
322 /**
323 * Builds a JShell state engine. This is the entry-point to all JShell
324 * functionality. This creates a remote process for execution. It is
325 * thus important to close the returned instance.
326 *
327 * @return the state engine
328 */
329 public JShell build() {
330 return new JShell(this);
331 }
332 }
333
334 // --- public API ---
335
336 /**
337 * Create a new JShell state engine.
338 * That is, create an instance of {@code JShell}.
380 * errors will abort evaluation.
381 * <p>
382 * The input should be
383 * exactly one complete snippet of source code, that is, one expression,
384 * statement, variable declaration, method declaration, class declaration,
385 * or import.
386 * To break arbitrary input into individual complete snippets, use
387 * {@link SourceCodeAnalysis#analyzeCompletion(String)}.
388 * <p>
389 * For imports, the import is added. Classes, interfaces. methods,
390 * and variables are defined. The initializer of variables, statements,
391 * and expressions are executed.
392 * The modifiers public, protected, private, static, and final are not
393 * allowed on op-level declarations and are ignored with a warning.
394 * Synchronized, native, abstract, and default top-level methods are not
395 * allowed and are errors.
396 * If a previous definition of a declaration is overwritten then there will
397 * be an event showing its status changed to OVERWRITTEN, this will not
398 * occur for dropped, rejected, or already overwritten declarations.
399 * <p>
400 * The execution environment is out of process. If the evaluated code
401 * causes the execution environment to terminate, this {@code JShell}
402 * instance will be closed but the calling process and VM remain valid.
403 * @param input The input String to evaluate
404 * @return the list of events directly or indirectly caused by this evaluation.
405 * @throws IllegalStateException if this {@code JShell} instance is closed.
406 * @see SourceCodeAnalysis#analyzeCompletion(String)
407 * @see JShell#onShutdown(java.util.function.Consumer)
408 */
409 public List<SnippetEvent> eval(String input) throws IllegalStateException {
410 SourceCodeAnalysisImpl a = sourceCodeAnalysis;
411 if (a != null) {
412 a.suspendIndexing();
413 }
414 try {
415 checkIfAlive();
416 List<SnippetEvent> events = eval.eval(input);
417 events.forEach(this::notifyKeyStatusEvent);
418 return Collections.unmodifiableList(events);
419 } finally {
420 if (a != null) {
430 * dropped snippet.
431 * @throws IllegalStateException if this {@code JShell} instance is closed.
432 * @throws IllegalArgumentException if the snippet is not associated with
433 * this {@code JShell} instance.
434 */
435 public List<SnippetEvent> drop(PersistentSnippet snippet) throws IllegalStateException {
436 checkIfAlive();
437 checkValidSnippet(snippet);
438 List<SnippetEvent> events = eval.drop(snippet);
439 events.forEach(this::notifyKeyStatusEvent);
440 return Collections.unmodifiableList(events);
441 }
442
443 /**
444 * The specified path is added to the end of the classpath used in eval().
445 * Note that the unnamed package is not accessible from the package in which
446 * {@link JShell#eval(String)} code is placed.
447 * @param path the path to add to the classpath.
448 */
449 public void addToClasspath(String path) {
450 taskFactory.addToClasspath(path); // Compiler
451 executionControl().addToClasspath(path); // Runtime
452 if (sourceCodeAnalysis != null) {
453 sourceCodeAnalysis.classpathChanged();
454 }
455 }
456
457 /**
458 * Attempt to stop currently running evaluation. When called while
459 * the {@link #eval(java.lang.String) } method is running and the
460 * user's code being executed, an attempt will be made to stop user's code.
461 * Note that typically this method needs to be called from a different thread
462 * than the one running the {@code eval} method.
463 * <p>
464 * If the {@link #eval(java.lang.String) } method is not running, does nothing.
465 * <p>
466 * The attempt to stop the user's code may fail in some case, which may include
467 * when the execution is blocked on an I/O operation, or when the user's code is
468 * catching the {@link ThreadDeath} exception.
469 */
470 public void stop() {
471 if (executionControl != null)
472 executionControl.stop();
473 }
474
475 /**
476 * Close this state engine. Frees resources. Should be called when this
477 * state engine is no longer needed.
478 */
479 @Override
480 public void close() {
481 if (!closed) {
482 closeDown();
483 executionControl().close();
484 }
485 }
486
487 /**
488 * Return all snippets.
489 * @return the snippets for all current snippets in id order.
490 * @throws IllegalStateException if this JShell instance is closed.
491 */
492 public List<Snippet> snippets() throws IllegalStateException {
605 return Collections.unmodifiableList(checkValidSnippet(snippet).unresolved());
606 }
607
608 /**
609 * Get the current value of a variable.
610 * @param snippet the variable Snippet whose value is queried.
611 * @return the current value of the variable referenced by snippet.
612 * @throws IllegalStateException if this {@code JShell} instance is closed.
613 * @throws IllegalArgumentException if the snippet is not associated with
614 * this {@code JShell} instance.
615 * @throws IllegalArgumentException if the variable's status is anything but
616 * {@link jdk.jshell.Snippet.Status#VALID}.
617 */
618 public String varValue(VarSnippet snippet) throws IllegalStateException {
619 checkIfAlive();
620 checkValidSnippet(snippet);
621 if (snippet.status() != Status.VALID) {
622 throw new IllegalArgumentException(
623 messageFormat("jshell.exc.var.not.valid", snippet, snippet.status()));
624 }
625 String value = executionControl().varValue(snippet.classFullName(), snippet.name());
626 return expunge(value);
627 }
628
629 /**
630 * Register a callback to be called when the Status of a snippet changes.
631 * Each call adds a new subscription.
632 * @param listener Action to perform when the Status changes.
633 * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
634 * @throws IllegalStateException if this {@code JShell} instance is closed.
635 */
636 public Subscription onSnippetEvent(Consumer<SnippetEvent> listener)
637 throws IllegalStateException {
638 return onX(keyStatusListeners, listener);
639 }
640
641 /**
642 * Register a callback to be called when this JShell instance terminates.
643 * This occurs either because the client process has ended (e.g. called System.exit(0))
644 * or the connection has been shutdown, as by close().
645 * Each call adds a new subscription.
679 * Provide the environment for a execution engine.
680 */
681 class ExecutionEnvImpl implements ExecutionEnv {
682
683 @Override
684 public InputStream userIn() {
685 return in;
686 }
687
688 @Override
689 public PrintStream userOut() {
690 return out;
691 }
692
693 @Override
694 public PrintStream userErr() {
695 return err;
696 }
697
698 @Override
699 public JShell state() {
700 return JShell.this;
701 }
702
703 @Override
704 public List<String> extraRemoteVMOptions() {
705 return extraRemoteVMOptions;
706 }
707
708 @Override
709 public byte[] getClassBytes(String classname) {
710 return classnameToBytes.get(classname);
711 }
712
713 @Override
714 public EvalException createEvalException(String message, String exceptionClass, StackTraceElement[] stackElements) {
715 return new EvalException(message, exceptionClass, stackElements);
716 }
717
718 @Override
719 public UnresolvedReferenceException createUnresolvedReferenceException(int id, StackTraceElement[] stackElements) {
720 DeclarationSnippet sn = (DeclarationSnippet) maps.getSnippetDeadOrAlive(id);
721 return new UnresolvedReferenceException(sn, stackElements);
722 }
723
724 @Override
725 public void closeDown() {
726 JShell.this.closeDown();
727 }
728 }
729
730 // --- private / package-private implementation support ---
731 ExecutionControl executionControl() {
732 if (!executionControlLaunched) {
733 try {
734 executionControlLaunched = true;
735 executionControl.start(new ExecutionEnvImpl());
736 } catch (Throwable ex) {
737 throw new InternalError("Launching execution engine threw: " + ex.getMessage(), ex);
738 }
739 }
740 return executionControl;
741 }
742
743 void setClassnameToBytes(String classname, byte[] bytes) {
744 classnameToBytes.put(classname, bytes);
745 }
746
747 void debug(int flags, String format, Object... args) {
748 InternalDebugControl.debug(this, err, flags, format, args);
749 }
750
751 void debug(Exception ex, String where) {
752 InternalDebugControl.debug(this, err, ex, where);
753 }
754
755 /**
756 * Generate the next key index, indicating a unique snippet signature.
757 *
758 * @return the next key index
759 */
760 int nextKeyIndex() {
761 return nextKeyIndex++;
762 }
763
764 private synchronized <T> Subscription onX(Map<Subscription, Consumer<T>> map, Consumer<T> listener)
|
27
28 import jdk.jshell.spi.ExecutionControl;
29 import java.io.ByteArrayInputStream;
30 import java.io.InputStream;
31 import java.io.PrintStream;
32 import java.text.MessageFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.MissingResourceException;
40 import java.util.Objects;
41 import java.util.ResourceBundle;
42 import java.util.function.BiFunction;
43 import java.util.function.Consumer;
44
45 import java.util.function.Supplier;
46 import jdk.internal.jshell.debug.InternalDebugControl;
47 import jdk.jshell.Snippet.Status;
48 import jdk.jshell.execution.JDIDefaultExecutionControl;
49 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
50 import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
51 import jdk.jshell.spi.ExecutionEnv;
52 import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
53 import static java.util.stream.Collectors.collectingAndThen;
54 import static java.util.stream.Collectors.toList;
55 import static jdk.jshell.Util.expunge;
56
57 /**
58 * The JShell evaluation state engine. This is the central class in the JShell
59 * API. A {@code JShell} instance holds the evolving compilation and
60 * execution state. The state is changed with the instance methods
61 * {@link jdk.jshell.JShell#eval(java.lang.String) eval(String)},
62 * {@link jdk.jshell.JShell#drop(jdk.jshell.PersistentSnippet) drop(PersistentSnippet)} and
63 * {@link jdk.jshell.JShell#addToClasspath(java.lang.String) addToClasspath(String)}.
64 * The majority of methods query the state.
65 * A {@code JShell} instance also allows registering for events with
66 * {@link jdk.jshell.JShell#onSnippetEvent(java.util.function.Consumer) onSnippetEvent(Consumer)}
67 * and {@link jdk.jshell.JShell#onShutdown(java.util.function.Consumer) onShutdown(Consumer)}, which
68 * are unregistered with
69 * {@link jdk.jshell.JShell#unsubscribe(jdk.jshell.JShell.Subscription) unsubscribe(Subscription)}.
70 * Access to the source analysis utilities is via
71 * {@link jdk.jshell.JShell#sourceCodeAnalysis()}.
72 * When complete the instance should be closed to free resources --
73 * {@link jdk.jshell.JShell#close()}.
74 * <p>
75 * An instance of {@code JShell} is created with
76 * {@code JShell.create()}.
77 * <p>
78 * This class is not thread safe, except as noted, all access should be through
79 * a single thread.
80 * @author Robert Field
81 */
82 public class JShell implements AutoCloseable {
83
84 final SnippetMaps maps;
85 final KeyMap keyMap;
86 final OuterWrapMap outerMap;
87 final TaskFactory taskFactory;
88 final InputStream in;
89 final PrintStream out;
90 final PrintStream err;
91 final Supplier<String> tempVariableNameGenerator;
92 final BiFunction<Snippet, Integer, String> idGenerator;
93 final List<String> extraRemoteVMOptions;
94 final List<String> extraCompilerOptions;
95 final ExecutionControl.Generator executionControlGenerator;
96
97 private int nextKeyIndex = 1;
98
99 final Eval eval;
100 final ClassTracker classTracker;
101 private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
102 private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
103 private boolean closed = false;
104
105 private ExecutionControl executionControl = null;
106 private SourceCodeAnalysisImpl sourceCodeAnalysis = null;
107
108 private static final String L10N_RB_NAME = "jdk.jshell.resources.l10n";
109 private static ResourceBundle outputRB = null;
110
111 JShell(Builder b) {
112 this.in = b.in;
113 this.out = b.out;
114 this.err = b.err;
115 this.tempVariableNameGenerator = b.tempVariableNameGenerator;
116 this.idGenerator = b.idGenerator;
117 this.extraRemoteVMOptions = b.extraRemoteVMOptions;
118 this.extraCompilerOptions = b.extraCompilerOptions;
119 this.executionControlGenerator = b.executionControlGenerator==null
120 ? failOverExecutionControlGenerator(
121 JDIDefaultExecutionControl.launch(),
122 JDIDefaultExecutionControl.listen())
123 : b.executionControlGenerator;
124
125 this.maps = new SnippetMaps(this);
126 this.keyMap = new KeyMap(this);
127 this.outerMap = new OuterWrapMap(this);
128 this.taskFactory = new TaskFactory(this);
129 this.eval = new Eval(this);
130 this.classTracker = new ClassTracker();
131 }
132
133 /**
134 * Builder for {@code JShell} instances.
135 * Create custom instances of {@code JShell} by using the setter
136 * methods on this class. After zero or more of these, use the
137 * {@link #build()} method to create a {@code JShell} instance.
138 * These can all be chained. For example, setting the remote output and
139 * error streams:
140 * <pre>
141 * {@code
142 * JShell myShell =
143 * JShell.builder()
144 * .out(myOutStream)
145 * .err(myErrStream)
146 * .build(); } </pre>
147 * If no special set-up is needed, just use
148 * {@code JShell.builder().build()} or the short-cut equivalent
149 * {@code JShell.create()}.
150 */
151 public static class Builder {
152
153 InputStream in = new ByteArrayInputStream(new byte[0]);
154 PrintStream out = System.out;
155 PrintStream err = System.err;
156 Supplier<String> tempVariableNameGenerator = null;
157 BiFunction<Snippet, Integer, String> idGenerator = null;
158 List<String> extraRemoteVMOptions = new ArrayList<>();
159 List<String> extraCompilerOptions = new ArrayList<>();
160 ExecutionControl.Generator executionControlGenerator;
161
162 Builder() { }
163
164 /**
165 * Sets the input for the running evaluation (it's {@code System.in}). Note:
166 * applications that use {@code System.in} for snippet or other
167 * user input cannot use {@code System.in} as the input stream for
168 * the remote process.
169 * <p>
170 * The default, if this is not set, is to provide an empty input stream
171 * -- {@code new ByteArrayInputStream(new byte[0])}.
172 *
173 * @param in the {@code InputStream} to be channelled to
174 * {@code System.in} in the remote execution process
175 * @return the {@code Builder} instance (for use in chained
176 * initialization)
177 */
178 public Builder in(InputStream in) {
179 this.in = in;
180 return this;
296
297 /**
298 * Adds compiler options. These additional options will be used on
299 * parsing, analysis, and code generation calls to the compiler.
300 * Options which interfere with results are not supported and have
301 * undefined effects on JShell's operation.
302 *
303 * @param options the addition options for compiler invocations
304 * @return the {@code Builder} instance (for use in chained
305 * initialization)
306 */
307 public Builder compilerOptions(String... options) {
308 this.extraCompilerOptions.addAll(Arrays.asList(options));
309 return this;
310 }
311
312 /**
313 * Sets the custom engine for execution. Snippet execution will be
314 * provided by the specified {@link ExecutionControl} instance.
315 *
316 * @param executionControlGenerator the execution engine generator
317 * @return the {@code Builder} instance (for use in chained
318 * initialization)
319 */
320 public Builder executionEngine(ExecutionControl.Generator executionControlGenerator) {
321 this.executionControlGenerator = executionControlGenerator;
322 return this;
323 }
324
325 /**
326 * Builds a JShell state engine. This is the entry-point to all JShell
327 * functionality. This creates a remote process for execution. It is
328 * thus important to close the returned instance.
329 *
330 * @return the state engine
331 */
332 public JShell build() {
333 return new JShell(this);
334 }
335 }
336
337 // --- public API ---
338
339 /**
340 * Create a new JShell state engine.
341 * That is, create an instance of {@code JShell}.
383 * errors will abort evaluation.
384 * <p>
385 * The input should be
386 * exactly one complete snippet of source code, that is, one expression,
387 * statement, variable declaration, method declaration, class declaration,
388 * or import.
389 * To break arbitrary input into individual complete snippets, use
390 * {@link SourceCodeAnalysis#analyzeCompletion(String)}.
391 * <p>
392 * For imports, the import is added. Classes, interfaces. methods,
393 * and variables are defined. The initializer of variables, statements,
394 * and expressions are executed.
395 * The modifiers public, protected, private, static, and final are not
396 * allowed on op-level declarations and are ignored with a warning.
397 * Synchronized, native, abstract, and default top-level methods are not
398 * allowed and are errors.
399 * If a previous definition of a declaration is overwritten then there will
400 * be an event showing its status changed to OVERWRITTEN, this will not
401 * occur for dropped, rejected, or already overwritten declarations.
402 * <p>
403 * If execution environment is out of process, as is the default case, then
404 * if the evaluated code
405 * causes the execution environment to terminate, this {@code JShell}
406 * instance will be closed but the calling process and VM remain valid.
407 * @param input The input String to evaluate
408 * @return the list of events directly or indirectly caused by this evaluation.
409 * @throws IllegalStateException if this {@code JShell} instance is closed.
410 * @see SourceCodeAnalysis#analyzeCompletion(String)
411 * @see JShell#onShutdown(java.util.function.Consumer)
412 */
413 public List<SnippetEvent> eval(String input) throws IllegalStateException {
414 SourceCodeAnalysisImpl a = sourceCodeAnalysis;
415 if (a != null) {
416 a.suspendIndexing();
417 }
418 try {
419 checkIfAlive();
420 List<SnippetEvent> events = eval.eval(input);
421 events.forEach(this::notifyKeyStatusEvent);
422 return Collections.unmodifiableList(events);
423 } finally {
424 if (a != null) {
434 * dropped snippet.
435 * @throws IllegalStateException if this {@code JShell} instance is closed.
436 * @throws IllegalArgumentException if the snippet is not associated with
437 * this {@code JShell} instance.
438 */
439 public List<SnippetEvent> drop(PersistentSnippet snippet) throws IllegalStateException {
440 checkIfAlive();
441 checkValidSnippet(snippet);
442 List<SnippetEvent> events = eval.drop(snippet);
443 events.forEach(this::notifyKeyStatusEvent);
444 return Collections.unmodifiableList(events);
445 }
446
447 /**
448 * The specified path is added to the end of the classpath used in eval().
449 * Note that the unnamed package is not accessible from the package in which
450 * {@link JShell#eval(String)} code is placed.
451 * @param path the path to add to the classpath.
452 */
453 public void addToClasspath(String path) {
454 // Compiler
455 taskFactory.addToClasspath(path);
456 // Runtime
457 try {
458 executionControl().addToClasspath(path);
459 } catch (ExecutionControlException ex) {
460 debug(ex, "on addToClasspath(" + path + ")");
461 }
462 if (sourceCodeAnalysis != null) {
463 sourceCodeAnalysis.classpathChanged();
464 }
465 }
466
467 /**
468 * Attempt to stop currently running evaluation. When called while
469 * the {@link #eval(java.lang.String) } method is running and the
470 * user's code being executed, an attempt will be made to stop user's code.
471 * Note that typically this method needs to be called from a different thread
472 * than the one running the {@code eval} method.
473 * <p>
474 * If the {@link #eval(java.lang.String) } method is not running, does nothing.
475 * <p>
476 * The attempt to stop the user's code may fail in some case, which may include
477 * when the execution is blocked on an I/O operation, or when the user's code is
478 * catching the {@link ThreadDeath} exception.
479 */
480 public void stop() {
481 if (executionControl != null) {
482 try {
483 executionControl.stop();
484 } catch (ExecutionControlException ex) {
485 debug(ex, "on stop()");
486 }
487 }
488 }
489
490 /**
491 * Close this state engine. Frees resources. Should be called when this
492 * state engine is no longer needed.
493 */
494 @Override
495 public void close() {
496 if (!closed) {
497 closeDown();
498 executionControl().close();
499 }
500 }
501
502 /**
503 * Return all snippets.
504 * @return the snippets for all current snippets in id order.
505 * @throws IllegalStateException if this JShell instance is closed.
506 */
507 public List<Snippet> snippets() throws IllegalStateException {
620 return Collections.unmodifiableList(checkValidSnippet(snippet).unresolved());
621 }
622
623 /**
624 * Get the current value of a variable.
625 * @param snippet the variable Snippet whose value is queried.
626 * @return the current value of the variable referenced by snippet.
627 * @throws IllegalStateException if this {@code JShell} instance is closed.
628 * @throws IllegalArgumentException if the snippet is not associated with
629 * this {@code JShell} instance.
630 * @throws IllegalArgumentException if the variable's status is anything but
631 * {@link jdk.jshell.Snippet.Status#VALID}.
632 */
633 public String varValue(VarSnippet snippet) throws IllegalStateException {
634 checkIfAlive();
635 checkValidSnippet(snippet);
636 if (snippet.status() != Status.VALID) {
637 throw new IllegalArgumentException(
638 messageFormat("jshell.exc.var.not.valid", snippet, snippet.status()));
639 }
640 String value;
641 try {
642 value = executionControl().varValue(snippet.classFullName(), snippet.name());
643 } catch (EngineTerminationException ex) {
644 throw new IllegalStateException(ex.getMessage());
645 } catch (ExecutionControlException ex) {
646 debug(ex, "In varValue()");
647 return "[" + ex.getMessage() + "]";
648 }
649 return expunge(value);
650 }
651
652 /**
653 * Register a callback to be called when the Status of a snippet changes.
654 * Each call adds a new subscription.
655 * @param listener Action to perform when the Status changes.
656 * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
657 * @throws IllegalStateException if this {@code JShell} instance is closed.
658 */
659 public Subscription onSnippetEvent(Consumer<SnippetEvent> listener)
660 throws IllegalStateException {
661 return onX(keyStatusListeners, listener);
662 }
663
664 /**
665 * Register a callback to be called when this JShell instance terminates.
666 * This occurs either because the client process has ended (e.g. called System.exit(0))
667 * or the connection has been shutdown, as by close().
668 * Each call adds a new subscription.
702 * Provide the environment for a execution engine.
703 */
704 class ExecutionEnvImpl implements ExecutionEnv {
705
706 @Override
707 public InputStream userIn() {
708 return in;
709 }
710
711 @Override
712 public PrintStream userOut() {
713 return out;
714 }
715
716 @Override
717 public PrintStream userErr() {
718 return err;
719 }
720
721 @Override
722 public List<String> extraRemoteVMOptions() {
723 return extraRemoteVMOptions;
724 }
725
726 @Override
727 public void closeDown() {
728 JShell.this.closeDown();
729 }
730
731 }
732
733 // --- private / package-private implementation support ---
734 ExecutionControl executionControl() {
735 if (executionControl == null) {
736 try {
737 executionControl = executionControlGenerator.generate(new ExecutionEnvImpl());
738 } catch (Throwable ex) {
739 throw new InternalError("Launching execution engine threw: " + ex.getMessage(), ex);
740 }
741 }
742 return executionControl;
743 }
744
745 void debug(int flags, String format, Object... args) {
746 InternalDebugControl.debug(this, err, flags, format, args);
747 }
748
749 void debug(Exception ex, String where) {
750 InternalDebugControl.debug(this, err, ex, where);
751 }
752
753 /**
754 * Generate the next key index, indicating a unique snippet signature.
755 *
756 * @return the next key index
757 */
758 int nextKeyIndex() {
759 return nextKeyIndex++;
760 }
761
762 private synchronized <T> Subscription onX(Map<Subscription, Consumer<T>> map, Consumer<T> listener)
|