JDK 11 jdk.jshell.jmod - JShell Tool

JDK 11 jdk.jshell.jmod is the JMOD file for JDK 11 JShell tool, which can be invoked by the "jshell" command.

JDK 11 JShell tool compiled class files are stored in \fyicenter\jdk-11.0.1\jmods\jdk.jshell.jmod.

JDK 11 JShell tool compiled class files are also linked and stored in the \fyicenter\jdk-11.0.1\lib\modules JImage file.

JDK 11 JShell tool source code files are stored in \fyicenter\jdk-11.0.1\lib\src.zip\jdk.jshell.

You can click and view the content of each source code file in the list below.

✍: FYIcenter

jdk/jshell/JShell.java

/*
 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package jdk.jshell;

import jdk.jshell.spi.ExecutionControl;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.tools.StandardJavaFileManager;
import jdk.internal.jshell.debug.InternalDebugControl;
import jdk.jshell.Snippet.Status;
import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
import jdk.jshell.spi.ExecutionControlProvider;
import jdk.jshell.spi.ExecutionEnv;
import static jdk.jshell.Util.expunge;

/**
 * The JShell evaluation state engine.  This is the central class in the JShell
 * API.  A {@code JShell} instance holds the evolving compilation and
 * execution state.  The state is changed with the instance methods
 * {@link jdk.jshell.JShell#eval(java.lang.String) eval(String)},
 * {@link jdk.jshell.JShell#drop(jdk.jshell.Snippet) drop(Snippet)} and
 * {@link jdk.jshell.JShell#addToClasspath(java.lang.String) addToClasspath(String)}.
 * The majority of methods query the state.
 * A {@code JShell} instance also allows registering for events with
 * {@link jdk.jshell.JShell#onSnippetEvent(java.util.function.Consumer) onSnippetEvent(Consumer)}
 * and {@link jdk.jshell.JShell#onShutdown(java.util.function.Consumer) onShutdown(Consumer)}, which
 * are unregistered with
 * {@link jdk.jshell.JShell#unsubscribe(jdk.jshell.JShell.Subscription) unsubscribe(Subscription)}.
 * Access to the source analysis utilities is via
 * {@link jdk.jshell.JShell#sourceCodeAnalysis()}.
 * When complete the instance should be closed to free resources --
 * {@link jdk.jshell.JShell#close()}.
 * <p>
 * An instance of {@code JShell} is created with
 * {@code JShell.create()}.
 * <p>
 * This class is not thread safe, except as noted, all access should be through
 * a single thread.
 *
 * @author Robert Field
 * @since 9
 */
public class JShell implements AutoCloseable {

    final SnippetMaps maps;
    final KeyMap keyMap;
    final OuterWrapMap outerMap;
    final TaskFactory taskFactory;
    final InputStream in;
    final PrintStream out;
    final PrintStream err;
    final Supplier<String> tempVariableNameGenerator;
    final BiFunction<Snippet, Integer, String> idGenerator;
    final List<String> extraRemoteVMOptions;
    final List<String> extraCompilerOptions;
    final Function<StandardJavaFileManager, StandardJavaFileManager> fileManagerMapping;

    private int nextKeyIndex = 1;

    final Eval eval;
    final ClassTracker classTracker;
    private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
    private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
    private boolean closed = false;

    private final ExecutionControl executionControl;
    private SourceCodeAnalysisImpl sourceCodeAnalysis = null;

    private static final String L10N_RB_NAME    = "jdk.jshell.resources.l10n";
    private static ResourceBundle outputRB  = null;

    JShell(Builder b) throws IllegalStateException {
        this.in = b.in;
        this.out = b.out;
        this.err = b.err;
        this.tempVariableNameGenerator = b.tempVariableNameGenerator;
        this.idGenerator = b.idGenerator;
        this.extraRemoteVMOptions = b.extraRemoteVMOptions;
        this.extraCompilerOptions = b.extraCompilerOptions;
        this.fileManagerMapping = b.fileManagerMapping;
        try {
            if (b.executionControlProvider != null) {
                executionControl = b.executionControlProvider.generate(new ExecutionEnvImpl(),
                        b.executionControlParameters == null
                                ? b.executionControlProvider.defaultParameters()
                                : b.executionControlParameters);
            } else {
                String loopback = InetAddress.getLoopbackAddress().getHostAddress();
                String spec = b.executionControlSpec == null
                        ? "failover:0(jdi:hostname(" + loopback + ")),"
                          + "1(jdi:launch(true)), 2(jdi)"
                        : b.executionControlSpec;
                executionControl = ExecutionControl.generate(new ExecutionEnvImpl(), spec);
            }
        } catch (Throwable ex) {
            throw new IllegalStateException("Launching JShell execution engine threw: " + ex.getMessage(), ex);
        }

        this.maps = new SnippetMaps(this);
        this.keyMap = new KeyMap(this);
        this.outerMap = new OuterWrapMap(this);
        this.taskFactory = new TaskFactory(this);
        this.eval = new Eval(this);
        this.classTracker = new ClassTracker();
    }

    /**
     * Builder for {@code JShell} instances.
     * Create custom instances of {@code JShell} by using the setter
     * methods on this class.  After zero or more of these, use the
     * {@link #build()} method to create a {@code JShell} instance.
     * These can all be chained. For example, setting the remote output and
     * error streams:
     * <pre>
     * {@code
     *     JShell myShell =
     *       JShell.builder()
     *         .out(myOutStream)
     *         .err(myErrStream)
     *         .build(); } </pre>
     * If no special set-up is needed, just use
     * {@code JShell.builder().build()} or the short-cut equivalent
     * {@code JShell.create()}.
     */
    public static class Builder {

        InputStream in = new ByteArrayInputStream(new byte[0]);
        PrintStream out = System.out;
        PrintStream err = System.err;
        Supplier<String> tempVariableNameGenerator = null;
        BiFunction<Snippet, Integer, String> idGenerator = null;
        List<String> extraRemoteVMOptions = new ArrayList<>();
        List<String> extraCompilerOptions = new ArrayList<>();
        ExecutionControlProvider executionControlProvider;
        Map<String,String> executionControlParameters;
        String executionControlSpec;
        Function<StandardJavaFileManager, StandardJavaFileManager> fileManagerMapping;

        Builder() { }

        /**
         * Sets the input for the running evaluation (it's {@code System.in}). Note:
         * applications that use {@code System.in} for snippet or other
         * user input cannot use {@code System.in} as the input stream for
         * the remote process.
         * <p>
         * The {@code read} method of the {@code InputStream} may throw the {@link InterruptedIOException}
         * to signal the user canceled the input. The currently running snippet will be automatically
         * {@link JShell#stop() stopped}.
         * <p>
         * The default, if this is not set, is to provide an empty input stream
         * -- {@code new ByteArrayInputStream(new byte[0])}.
         *
         * @param in the {@code InputStream} to be channelled to
         * {@code System.in} in the remote execution process
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder in(InputStream in) {
            this.in = in;
            return this;
        }

        /**
         * Sets the output for the running evaluation (it's {@code System.out}).
         * The controlling process and
         * the remote process can share {@code System.out}.
         * <p>
         * The default, if this is not set, is {@code System.out}.
         *
         * @param out the {@code PrintStream} to be channelled to
         * {@code System.out} in the remote execution process
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder out(PrintStream out) {
            this.out = out;
            return this;
        }

        /**
         * Sets the error output for the running evaluation (it's
         * {@code System.err}). The controlling process and the remote
         * process can share {@code System.err}.
         * <p>
         * The default, if this is not set, is {@code System.err}.
         *
         * @param err the {@code PrintStream} to be channelled to
         * {@code System.err} in the remote execution process
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder err(PrintStream err) {
            this.err = err;
            return this;
        }

        /**
         * Sets a generator of temp variable names for
         * {@link jdk.jshell.VarSnippet} of
         * {@link jdk.jshell.Snippet.SubKind#TEMP_VAR_EXPRESSION_SUBKIND}.
         * <p>
         * Do not use this method unless you have explicit need for it.
         * <p>
         * The generator will be used for newly created VarSnippet
         * instances. The name of a variable is queried with
         * {@link jdk.jshell.VarSnippet#name()}.
         * <p>
         * The callback is sent during the processing of the snippet, the
         * JShell state is not stable. No calls whatsoever on the
         * {@code JShell} instance may be made from the callback.
         * <p>
         * The generated name must be unique within active snippets.
         * <p>
         * The default behavior (if this is not set or {@code generator}
         * is null) is to generate the name as a sequential number with a
         * prefixing dollar sign ("$").
         *
         * @param generator the {@code Supplier} to generate the temporary
         * variable name string or {@code null}
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder tempVariableNameGenerator(Supplier<String> generator) {
            this.tempVariableNameGenerator = generator;
            return this;
        }

        /**
         * Sets the generator of identifying names for Snippets.
         * <p>
         * Do not use this method unless you have explicit need for it.
         * <p>
         * The generator will be used for newly created Snippet instances. The
         * identifying name (id) is accessed with
         * {@link jdk.jshell.Snippet#id()} and can be seen in the
         * {@code StackTraceElement.getFileName()} for a
         * {@link jdk.jshell.EvalException} and
         * {@link jdk.jshell.UnresolvedReferenceException}.
         * <p>
         * The inputs to the generator are the {@link jdk.jshell.Snippet} and an
         * integer. The integer will be the same for two Snippets which would
         * overwrite one-another, but otherwise is unique.
         * <p>
         * The callback is sent during the processing of the snippet and the
         * Snippet and the state as a whole are not stable. No calls to change
         * system state (including Snippet state) should be made. Queries of
         * Snippet may be made except to {@link jdk.jshell.Snippet#id()}. No
         * calls on the {@code JShell} instance may be made from the
         * callback, except to
         * {@link #status(jdk.jshell.Snippet) status(Snippet)}.
         * <p>
         * The default behavior (if this is not set or {@code generator}
         * is null) is to generate the id as the integer converted to a string.
         *
         * @param generator the {@code BiFunction} to generate the id
         * string or {@code null}
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder idGenerator(BiFunction<Snippet, Integer, String> generator) {
            this.idGenerator = generator;
            return this;
        }

        /**
         * Sets additional VM options for launching the VM.
         *
         * @param options The options for the remote VM
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder remoteVMOptions(String... options) {
            this.extraRemoteVMOptions.addAll(Arrays.asList(options));
            return this;
        }

        /**
         * Adds compiler options.  These additional options will be used on
         * parsing, analysis, and code generation calls to the compiler.
         * Options which interfere with results are not supported and have
         * undefined effects on JShell's operation.
         *
         * @param options the addition options for compiler invocations
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder compilerOptions(String... options) {
            this.extraCompilerOptions.addAll(Arrays.asList(options));
            return this;
        }

        /**
         * Sets the custom engine for execution. Snippet execution will be
         * provided by the {@link ExecutionControl} instance selected by the
         * specified execution control spec.
         * Use, at most, one of these overloaded {@code executionEngine} builder
         * methods.
         *
         * @param executionControlSpec the execution control spec,
         * which is documented in the {@link jdk.jshell.spi}
         * package documentation.
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder executionEngine(String executionControlSpec) {
            this.executionControlSpec = executionControlSpec;
            return this;
        }

        /**
         * Sets the custom engine for execution. Snippet execution will be
         * provided by the specified {@link ExecutionControl} instance.
         * Use, at most, one of these overloaded {@code executionEngine} builder
         * methods.
         *
         * @param executionControlProvider the provider to supply the execution
         * engine
         * @param executionControlParameters the parameters to the provider, or
         * {@code null} for default parameters
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder executionEngine(ExecutionControlProvider executionControlProvider,
                Map<String,String> executionControlParameters) {
            this.executionControlProvider = executionControlProvider;
            this.executionControlParameters = executionControlParameters;
            return this;
        }

        /**
         * Configure the {@code FileManager} to be used by compilation and
         * source analysis.
         * If not set or passed null, the compiler's standard file manager will
         * be used (identity mapping).
         * For use in special applications where the compiler's normal file
         * handling needs to be overridden.  See the file manager APIs for more
         * information.
         * The file manager input enables forwarding file managers, if this
         * is not needed, the incoming file manager can be ignored (constant
         * function).
         *
         * @param mapping a function that given the compiler's standard file
         * manager, returns a file manager to use
         * @return the {@code Builder} instance (for use in chained
         * initialization)
         */
        public Builder fileManager(Function<StandardJavaFileManager, StandardJavaFileManager> mapping) {
            this.fileManagerMapping = mapping;
            return this;
        }

        /**
         * Builds a JShell state engine. This is the entry-point to all JShell
         * functionality. This creates a remote process for execution. It is
         * thus important to close the returned instance.
         *
         * @throws IllegalStateException if the {@code JShell} instance could not be created.
         * @return the state engine
         */
        public JShell build() throws IllegalStateException {
            return new JShell(this);
        }
    }

    // --- public API ---

    /**
     * Create a new JShell state engine.
     * That is, create an instance of {@code JShell}.
     * <p>
     * Equivalent to {@link JShell#builder() JShell.builder()}{@link JShell.Builder#build() .build()}.
     * @throws IllegalStateException if the {@code JShell} instance could not be created.
     * @return an instance of {@code JShell}.
     */
    public static JShell create() throws IllegalStateException {
        return builder().build();
    }

    /**
     * Factory method for {@code JShell.Builder} which, in-turn, is used
     * for creating instances of {@code JShell}.
     * Create a default instance of {@code JShell} with
     * {@code JShell.builder().build()}. For more construction options
     * see {@link jdk.jshell.JShell.Builder}.
     * @return an instance of {@code Builder}.
     * @see jdk.jshell.JShell.Builder
     */
    public static Builder builder() {
        return new Builder();
    }

    /**
     * Access to source code analysis functionality.
     * An instance of {@code JShell} will always return the same
     * {@code SourceCodeAnalysis} instance from
     * {@code sourceCodeAnalysis()}.
     * @return an instance of {@link SourceCodeAnalysis SourceCodeAnalysis}
     * which can be used for source analysis such as completion detection and
     * completion suggestions.
     */
    public SourceCodeAnalysis sourceCodeAnalysis() {
        if (sourceCodeAnalysis == null) {
            sourceCodeAnalysis = new SourceCodeAnalysisImpl(this);
        }
        return sourceCodeAnalysis;
    }

    /**
     * Evaluate the input String, including definition and/or execution, if
     * applicable. The input is checked for errors, unless the errors can be
     * deferred (as is the case with some unresolvedDependencies references),
     * errors will abort evaluation.
     * <p>
     * The input should be
     * exactly one complete snippet of source code, that is, one expression,
     * statement, variable declaration, method declaration, class declaration,
     * or import.
     * To break arbitrary input into individual complete snippets, use
     * {@link SourceCodeAnalysis#analyzeCompletion(String)}.
     * <p>
     * For imports, the import is added.  Classes, interfaces. methods,
     * and variables are defined.  The initializer of variables, statements,
     * and expressions are executed.
     * The modifiers public, protected, private, static, and final are not
     * allowed on op-level declarations and are ignored with a warning.
     * Synchronized, native, abstract, and default top-level methods are not
     * allowed and are errors.
     * If a previous definition of a declaration is overwritten then there will
     * be an event showing its status changed to OVERWRITTEN, this will not
     * occur for dropped, rejected, or already overwritten declarations.
     * <p>
     * If execution environment is out of process, as is the default case, then
     * if the evaluated code
     * causes the execution environment to terminate, this {@code JShell}
     * instance will be closed but the calling process and VM remain valid.
     * @param input The input String to evaluate
     * @return the list of events directly or indirectly caused by this evaluation.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @see SourceCodeAnalysis#analyzeCompletion(String)
     * @see JShell#onShutdown(java.util.function.Consumer)
     */
    public List<SnippetEvent> eval(String input) throws IllegalStateException {
        SourceCodeAnalysisImpl a = sourceCodeAnalysis;
        if (a != null) {
            a.suspendIndexing();
        }
        try {
            checkIfAlive();
            List<SnippetEvent> events = eval.eval(input);
            events.forEach(this::notifyKeyStatusEvent);
            return Collections.unmodifiableList(events);
        } finally {
            if (a != null) {
                a.resumeIndexing();
            }
        }
    }

    /**
     * Remove a declaration from the state.  That is, if the snippet is an
     * {@linkplain jdk.jshell.Snippet.Status#isActive() active}
     * {@linkplain jdk.jshell.PersistentSnippet persistent} snippet, remove the
     * snippet and update the JShell evaluation state accordingly.
     * For all active snippets, change the {@linkplain #status status} to
     * {@link jdk.jshell.Snippet.Status#DROPPED DROPPED}.
     * @param snippet The snippet to remove
     * @return The list of events from updating declarations dependent on the
     * dropped snippet.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @throws IllegalArgumentException if the snippet is not associated with
     * this {@code JShell} instance.
     */
    public List<SnippetEvent> drop(Snippet snippet) throws IllegalStateException {
        checkIfAlive();
        checkValidSnippet(snippet);
        List<SnippetEvent> events = eval.drop(snippet);
        events.forEach(this::notifyKeyStatusEvent);
        return Collections.unmodifiableList(events);
    }

    /**
     * The specified path is added to the end of the classpath used in eval().
     * Note that the unnamed package is not accessible from the package in which
     * {@link JShell#eval(String)} code is placed.
     * @param path the path to add to the classpath.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     */
    public void addToClasspath(String path) {
        checkIfAlive();
        // Compiler
        taskFactory.addToClasspath(path);
        // Runtime
        try {
            executionControl().addToClasspath(path);
        } catch (ExecutionControlException ex) {
            debug(ex, "on addToClasspath(" + path + ")");
        }
        if (sourceCodeAnalysis != null) {
            sourceCodeAnalysis.classpathChanged();
        }
    }

    /**
     * Attempt to stop currently running evaluation. When called while
     * the {@link #eval(java.lang.String) } method is running and the
     * user's code being executed, an attempt will be made to stop user's code.
     * Note that typically this method needs to be called from a different thread
     * than the one running the {@code eval} method.
     * <p>
     * If the {@link #eval(java.lang.String) } method is not running, does nothing.
     * <p>
     * The attempt to stop the user's code may fail in some case, which may include
     * when the execution is blocked on an I/O operation, or when the user's code is
     * catching the {@link ThreadDeath} exception.
     */
    public void stop() {
        if (executionControl != null) {
            try {
                executionControl.stop();
            } catch (ExecutionControlException ex) {
                debug(ex, "on stop()");
            }
        }
    }

    /**
     * Close this state engine. Frees resources. Should be called when this
     * state engine is no longer needed.
     */
    @Override
    public void close() {
        closeDown();
    }

    /**
     * Return all snippets.
     * @return the snippets for all current snippets in id order.
     */
    public Stream<Snippet> snippets() {
        return maps.snippetList().stream();
    }

    /**
     * Returns the active variable snippets.
     * This convenience method is equivalent to {@code snippets()} filtered for
     * {@link jdk.jshell.Snippet.Status#isActive() status(snippet).isActive()}
     * {@code && snippet.kind() == Kind.VARIABLE}
     * and cast to {@code VarSnippet}.
     * @return the active declared variables.
     */
    public Stream<VarSnippet> variables() {
        return snippets()
                     .filter(sn -> status(sn).isActive() && sn.kind() == Snippet.Kind.VAR)
                     .map(sn -> (VarSnippet) sn);
    }

    /**
     * Returns the active method snippets.
     * This convenience method is equivalent to {@code snippets()} filtered for
     * {@link jdk.jshell.Snippet.Status#isActive() status(snippet).isActive()}
     * {@code && snippet.kind() == Kind.METHOD}
     * and cast to MethodSnippet.
     * @return the active declared methods.
     */
    public Stream<MethodSnippet> methods() {
        return snippets()
                     .filter(sn -> status(sn).isActive() && sn.kind() == Snippet.Kind.METHOD)
                     .map(sn -> (MethodSnippet)sn);
    }

    /**
     * Returns the active type declaration (class, interface, annotation type, and enum) snippets.
     * This convenience method is equivalent to {@code snippets()} filtered for
     * {@link jdk.jshell.Snippet.Status#isActive() status(snippet).isActive()}
     * {@code && snippet.kind() == Kind.TYPE_DECL}
     * and cast to TypeDeclSnippet.
     * @return the active declared type declarations.
     */
    public Stream<TypeDeclSnippet> types() {
        return snippets()
                .filter(sn -> status(sn).isActive() && sn.kind() == Snippet.Kind.TYPE_DECL)
                .map(sn -> (TypeDeclSnippet) sn);
    }

    /**
     * Returns the active import snippets.
     * This convenience method is equivalent to {@code snippets()} filtered for
     * {@link jdk.jshell.Snippet.Status#isActive() status(snippet).isActive()}
     * {@code && snippet.kind() == Kind.IMPORT}
     * and cast to ImportSnippet.
     * @return the active declared import declarations.
     */
    public Stream<ImportSnippet> imports() {
        return snippets()
                .filter(sn -> status(sn).isActive() && sn.kind() == Snippet.Kind.IMPORT)
                .map(sn -> (ImportSnippet) sn);
    }

    /**
     * Return the status of the snippet.
     * This is updated either because of an explicit {@code eval()} call or
     * an automatic update triggered by a dependency.
     * @param snippet the {@code Snippet} to look up
     * @return the status corresponding to this snippet
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @throws IllegalArgumentException if the snippet is not associated with
     * this {@code JShell} instance.
     */
    public Status status(Snippet snippet) {
        return checkValidSnippet(snippet).status();
    }

    /**
     * Return the diagnostics of the most recent evaluation of the snippet.
     * The evaluation can either because of an explicit {@code eval()} call or
     * an automatic update triggered by a dependency.
     * @param snippet the {@code Snippet} to look up
     * @return the diagnostics corresponding to this snippet.  This does not
     * include unresolvedDependencies references reported in {@code unresolvedDependencies()}.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @throws IllegalArgumentException if the snippet is not associated with
     * this {@code JShell} instance.
     */
    public Stream<Diag> diagnostics(Snippet snippet) {
        return checkValidSnippet(snippet).diagnostics().stream();
    }

    /**
     * For {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED} or
     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_NOT_DEFINED RECOVERABLE_NOT_DEFINED}
     * declarations, the names of current unresolved dependencies for
     * the snippet.
     * The returned value of this method, for a given method may change when an
     * {@code eval()} or {@code drop()} of another snippet causes
     * an update of a dependency.
     * @param snippet the declaration {@code Snippet} to look up
     * @return a stream of symbol names that are currently unresolvedDependencies.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @throws IllegalArgumentException if the snippet is not associated with
     * this {@code JShell} instance.
     */
    public Stream<String> unresolvedDependencies(DeclarationSnippet snippet) {
        return checkValidSnippet(snippet).unresolved().stream();
    }

    /**
     * Get the current value of a variable.
     * @param snippet the variable Snippet whose value is queried.
     * @return the current value of the variable referenced by snippet.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     * @throws IllegalArgumentException if the snippet is not associated with
     * this {@code JShell} instance.
     * @throws IllegalArgumentException if the variable's status is anything but
     * {@link jdk.jshell.Snippet.Status#VALID}.
     */
    public String varValue(VarSnippet snippet) throws IllegalStateException {
        checkIfAlive();
        checkValidSnippet(snippet);
        if (snippet.status() != Status.VALID) {
            throw new IllegalArgumentException(
                    messageFormat("jshell.exc.var.not.valid",  snippet, snippet.status()));
        }
        String value;
        try {
            value = executionControl().varValue(snippet.classFullName(), snippet.name());
        } catch (EngineTerminationException ex) {
            throw new IllegalStateException(ex.getMessage());
        } catch (ExecutionControlException ex) {
            debug(ex, "In varValue()");
            return "[" + ex.getMessage() + "]";
        }
        return expunge(value);
    }

    /**
     * Register a callback to be called when the Status of a snippet changes.
     * Each call adds a new subscription.
     * @param listener Action to perform when the Status changes.
     * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
     * @throws IllegalStateException if this {@code JShell} instance is closed.
     */
    public Subscription onSnippetEvent(Consumer<SnippetEvent> listener)
            throws IllegalStateException {
        return onX(keyStatusListeners, listener);
    }

    /**
     * Register a callback to be called when this JShell instance terminates.
     * This occurs either because the client process has ended (e.g. called System.exit(0))
     * or the connection has been shutdown, as by close().
     * Each call adds a new subscription.
     * @param listener Action to perform when the state terminates.
     * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
     * @throws IllegalStateException if this JShell instance is closed
     */
    public Subscription onShutdown(Consumer<JShell> listener)
            throws IllegalStateException {
        return onX(shutdownListeners, listener);
    }

    /**
     * Cancel a callback subscription.
     * @param token The token corresponding to the subscription to be unsubscribed.
     */
    public void unsubscribe(Subscription token) {
        synchronized (this) {
            token.remover.accept(token);
        }
    }

    /**
     * Subscription is a token for referring to subscriptions so they can
     * be {@linkplain JShell#unsubscribe unsubscribed}.
     */
    public class Subscription {

        Consumer<Subscription> remover;

        Subscription(Consumer<Subscription> remover) {
            this.remover = remover;
        }
    }

    /**
     * Provide the environment for a execution engine.
     */
    class ExecutionEnvImpl implements ExecutionEnv {

        @Override
        public InputStream userIn() {
            return in;
        }

        @Override
        public PrintStream userOut() {
            return out;
        }

        @Override
        public PrintStream userErr() {
            return err;
        }

        @Override
        public List<String> extraRemoteVMOptions() {
            return extraRemoteVMOptions;
        }

        @Override
        public void closeDown() {
            JShell.this.closeDown();
        }

    }

    // --- private / package-private implementation support ---

    ExecutionControl executionControl() {
        return executionControl;
    }

    void debug(int flags, String format, Object... args) {
        InternalDebugControl.debug(this, err, flags, format, args);
    }

    void debug(Throwable ex, String where) {
        InternalDebugControl.debug(this, err, ex, where);
    }

    /**
     * Generate the next key index, indicating a unique snippet signature.
     *
     * @return the next key index
     */
    int nextKeyIndex() {
        return nextKeyIndex++;
    }

    private synchronized <T> Subscription onX(Map<Subscription, Consumer<T>> map, Consumer<T> listener)
            throws IllegalStateException {
        Objects.requireNonNull(listener);
        checkIfAlive();
        Subscription token = new Subscription(map::remove);
        map.put(token, listener);
        return token;
    }

    private synchronized void notifyKeyStatusEvent(SnippetEvent event) {
        keyStatusListeners.values().forEach(l -> l.accept(event));
    }

    private synchronized void notifyShutdownEvent(JShell state) {
        shutdownListeners.values().forEach(l -> l.accept(state));
    }

    void closeDown() {
        if (!closed) {
            // Send only once
            closed = true;
            try {
                notifyShutdownEvent(this);
            } catch (Throwable thr) {
                // Don't care about dying exceptions
            }
            try {
                executionControl().close();
            } catch (Throwable ex) {
                // don't care about exceptions on close
            }
            if (sourceCodeAnalysis != null) {
                sourceCodeAnalysis.close();
            }
            InternalDebugControl.release(this);
        }
    }

    /**
     * Check if this JShell has been closed
     * @throws IllegalStateException if it is closed
     */
    void checkIfAlive()  throws IllegalStateException {
        if (closed) {
            throw new IllegalStateException(messageFormat("jshell.exc.closed", this));
        }
    }

    /**
     * Check a Snippet parameter coming from the API user
     * @param sn the Snippet to check
     * @throws NullPointerException if Snippet parameter is null
     * @throws IllegalArgumentException if Snippet is not from this JShell
     * @return the input Snippet (for chained calls)
     */
    private Snippet checkValidSnippet(Snippet sn) {
        if (sn == null) {
            throw new NullPointerException(messageFormat("jshell.exc.null"));
        } else {
            if (sn.key().state() != this || sn.id() == Snippet.UNASSOCIATED_ID) {
                throw new IllegalArgumentException(messageFormat("jshell.exc.alien", sn.toString()));
            }
            return sn;
        }
    }

    /**
     * Format using resource bundle look-up using MessageFormat
     *
     * @param key the resource key
     * @param args
     */
    String messageFormat(String key, Object... args) {
        if (outputRB == null) {
            try {
                outputRB = ResourceBundle.getBundle(L10N_RB_NAME);
            } catch (MissingResourceException mre) {
                throw new InternalError("Cannot find ResourceBundle: " + L10N_RB_NAME);
            }
        }
        String s;
        try {
            s = outputRB.getString(key);
        } catch (MissingResourceException mre) {
            throw new InternalError("Missing resource: " + key + " in " + L10N_RB_NAME);
        }
        return MessageFormat.format(s, args);
    }

}

jdk/jshell/JShell.java

 

JDK 11 jdk.jsobject.jmod - JS Object Module

JDK 11 jdk.jlink.jmod - JLink Tool

Download and Use JDK 11

⇑⇑ FAQ for JDK (Java Development Kit)

2018-11-10, 3831👍, 0💬