src/jdk.jshell/share/classes/jdk/jshell/JShell.java

Print this page




  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)