--- old/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java 2016-07-12 22:46:52.129867717 -0700 +++ new/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java 2016-07-12 22:46:52.043865505 -0700 @@ -22,133 +22,247 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.jshell.spi; -import java.util.Collection; -import jdk.jshell.JShellException; +import java.io.Serializable; /** - * This interface specifies the functionality that must provided to implement - * a pluggable JShell execution engine. + * This interface specifies the functionality that must provided to implement a + * pluggable JShell execution engine. *

- * The audience for this Service Provider Interface is engineers - * wishing to implement their own version of the execution engine in support - * of the JShell API. This is NOT a part of the JShell API. + * The audience for this Service Provider Interface is engineers wishing to + * implement their own version of the execution engine in support of the JShell + * API. *

- * A Snippet is compiled into code wrapped in a 'wrapper class'. The execution - * engine is used by the core JShell implementation to load and, for - * executable Snippets, execute the Snippet. + * A Snippet is compiled into code wrapped in a 'wrapper class'. The execution + * engine is used by the core JShell implementation to load and, for executable + * Snippets, execute the Snippet. *

* Methods defined in this interface should only be called by the core JShell * implementation. *

- * To install an instance of ExecutionControl, it is passed to - * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl) }. + * To install an {@code ExecutionControl}, it's {@code Generator} is passed to + * {@link jdk.jshell.JShell.Builder#executionEngine(ExecutionControl.Generator) }. */ public interface ExecutionControl { + + public interface Generator { + ExecutionControl generate(ExecutionEnv env) throws Throwable; + } /** - * Represents the current status of a class in the execution engine. - */ - public enum ClassStatus { - /** - * Class is not known to the execution engine (not loaded). - */ - UNKNOWN, - - /** - * Class is loaded, but the loaded/redefined bytes do not match those - * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }. - */ - NOT_CURRENT, - - /** - * Class is loaded and loaded/redefined bytes match those - * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }. - */ - CURRENT - }; - - /** - * Initializes the instance. No methods in this interface can be called - * before this. + * Attempts to load new classes. * - * @param env the execution environment information provided by JShell - * @throws Exception if the instance is unable to initialize + * @param cbcs the class name and bytecodes to load + * @throws ClassInstallException exception occurred loading the classes, + * some or all were not loaded + * @throws EngineTerminationException the execution engine has terminated */ - void start(ExecutionEnv env) throws Exception; + void load(ClassBytecodes[] cbcs) + throws ClassInstallException, EngineTerminationException; /** - * Shuts down this execution engine. Implementation should free all - * resources held by this execution engine. - *

- * No calls to methods on this interface should be made after close. - */ - void close(); - - /** - * Adds the path to the execution class path. + * Attempts to redefine previously loaded classes. * - * @param path the path to add - * @return true if successful + * @param cbcs the class name and bytecodes to redefine + * @throws ClassInstallException exception occurred redefining the classes, + * some or all were not redefined + * @throws EngineTerminationException the execution engine has terminated */ - boolean addToClasspath(String path); + void redefine(ClassBytecodes[] cbcs) + throws ClassInstallException, EngineTerminationException; /** * Invokes an executable Snippet by calling a method on the specified * wrapper class. The method must have no arguments and return String. * - * @param classname the class whose method should be invoked - * @param methodname the name of method to invoke + * @param className the class whose method should be invoked + * @param methodName the name of method to invoke * @return the result of the execution or null if no result - * @throws JShellException if a user exception if thrown, - * {@link jdk.jshell.EvalException EvalException} will be thrown; if an - * unresolved reference is encountered, - * {@link jdk.jshell.UnresolvedReferenceException UnresolvedReferenceException} - * will be thrown - */ - String invoke(String classname, String methodname) throws JShellException; + * @throws UserException the invoke raised a user exception + * @throws ResolutionException the invoke attempted to directly or + * indirectly invoke an unresolved snippet + * @throws StoppedException if the {@code invoke()} was canceled by + * {@link ExecutionControl#stop} + * @throws EngineTerminationException the execution engine has terminated + * @throws InternalException an internal problem occurred + */ + String invoke(String className, String methodName) + throws RunException, + EngineTerminationException, InternalException; + + /** + * Returns the value of a variable. + * + * @param className the name of the wrapper class of the variable + * @param varName the name of the variable + * @return the value of the variable + * @throws UserException formatting the value raised a user exception + * @throws ResolutionException formatting the value attempted to directly or + * indirectly invoke an unresolved snippet + * @throws StoppedException if the formatting the value was canceled by + * {@link ExecutionControl#stop} + * @throws EngineTerminationException the execution engine has terminated + * @throws InternalException an internal problem occurred + */ + String varValue(String className, String varName) + throws RunException, + EngineTerminationException, InternalException; /** - * Attempts to load new classes. Class bytes are retrieved from - * {@link ExecutionEnv#getClassBytes(java.lang.String) } + * Adds the path to the execution class path. * - * @param classes list of class names to load - * @return true if load succeeded + * @param path the path to add + * @throws EngineTerminationException the execution engine has terminated + * @throws InternalException an internal problem occurred */ - boolean load(Collection classes); + void addToClasspath(String path) + throws EngineTerminationException, InternalException; /** - * Attempts to redefine previously loaded classes. Class bytes are retrieved - * from {@link ExecutionEnv#getClassBytes(java.lang.String) } + * Sets the execution class path to the specified path. * - * @param classes list of class names to redefine - * @return true if redefine succeeded + * @param path the path to add + * @throws EngineTerminationException the execution engine has terminated + * @throws InternalException an internal problem occurred */ - boolean redefine(Collection classes); + void setClasspath(String path) + throws EngineTerminationException, InternalException; /** - * Queries if the class is loaded and the class bytes are current. + * Interrupts a running invoke. * - * @param classname name of the wrapper class to query - * @return {@code UNKNOWN} if the class is not loaded; {@code CURRENT} if - * the loaded/redefined bytes are equal to the most recent bytes for this - * wrapper class; otherwise {@code NOT_CURRENT} + * @throws EngineTerminationException the execution engine has terminated + * @throws InternalException an internal problem occurred */ - ClassStatus getClassStatus(String classname); + void stop() + throws EngineTerminationException, InternalException; /** - * Interrupt a running invoke. + * Shuts down this execution engine. Implementation should free all + * resources held by this execution engine. + *

+ * No calls to methods on this interface should be made after close. */ - void stop(); + void close(); + + public static final class ClassBytecodes implements Serializable { + + private static final long serialVersionUID = 1L; + + public ClassBytecodes(String name, byte[] bytecodes) { + this.name = name; + this.bytecodes = bytecodes; + } + public final String name; + public final byte[] bytecodes; + } + + @SuppressWarnings("serial") + public static abstract class ExecutionControlException extends Exception { + + public ExecutionControlException(String message) { + super(message); + } + } /** - * Returns the value of a variable. - * - * @param classname the name of the wrapper class of the variable - * @param varname the name of the variable - * @return the value of the variable + * Unbidden execution engine termination has occurred. */ - String varValue(String classname, String varname); + @SuppressWarnings("serial") + public static class EngineTerminationException extends ExecutionControlException { + + public EngineTerminationException(String message) { + super(message); + System.err.printf("EngineTerminationException: %s\n", message); + } + } + + @SuppressWarnings("serial") + public static class InternalException extends ExecutionControlException { + + public InternalException(String message) { + super(message); + } + } + + @SuppressWarnings("serial") + public static class ClassInstallException extends ExecutionControlException { + + private final boolean[] installed; + + public ClassInstallException(String message, boolean[] installed) { + super(message); + this.installed = installed; + } + + public boolean[] installed() { + return installed; + } + } + + @SuppressWarnings("serial") // serialVersionUID intentionally omitted + public static abstract class RunException extends ExecutionControlException { + + private RunException(String message) { + super(message); + } + } + + @SuppressWarnings("serial") // serialVersionUID intentionally omitted + public static class UserException extends RunException { + + private final String causeExceptionClass; + + public UserException(String message, String causeExceptionClass, StackTraceElement[] stackElements) { + super(message); + this.causeExceptionClass = causeExceptionClass; + this.setStackTrace(stackElements); + } + + public String causeExceptionClass() { + return causeExceptionClass; + } + } + + @SuppressWarnings("serial") // serialVersionUID intentionally omitted + public static class ResolutionException extends RunException { + + private final int id; + + /** + * Constructs an exception indicating that a + * {@code DeclarationSnippet} with unresolved references has been + * encountered. The throw of this exception is generated into the body + * of a + * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED} + * method. + * + * @param id An internal identifier of the specific method + * @param stackElements the stack trace + */ + public ResolutionException(int id, StackTraceElement[] stackElements) { + super("resolution exception: " + id); + this.id = id; + this.setStackTrace(stackElements); + } + + /** + * Retrieves the internal identifier of the unresolved identifier. + * + * @return the internal identifier + */ + public int id() { + return id; + } + } + + @SuppressWarnings("serial") // serialVersionUID intentionally omitted + public static class StoppedException extends RunException { + + public StoppedException() { + super("stopped by stop()"); + } + } + }