src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java

Print this page

        

@@ -20,135 +20,249 @@
  *
  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  * 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.
  * <p>
- * 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.
  * <p>
  * 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.
+ * engine is used by the core JShell implementation to load and, for executable
+ * Snippets, execute the Snippet.
  * <p>
  * Methods defined in this interface should only be called by the core JShell
  * implementation.
  * <p>
- * 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 {
 
-    /**
-     * 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,
+    public interface Generator {
+        ExecutionControl generate(ExecutionEnv env) throws Throwable;
+    }
 
         /**
-         * Class is loaded, but the loaded/redefined bytes do not match those
-         * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
+     * Attempts to load new classes.
+     *
+     * @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
          */
-        NOT_CURRENT,
+    void load(ClassBytecodes[] cbcs)
+            throws ClassInstallException, EngineTerminationException;
 
         /**
-         * Class is loaded and loaded/redefined bytes match those
-         * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
+     * Attempts to redefine previously loaded classes.
+     *
+     * @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
          */
-        CURRENT
-    };
+    void redefine(ClassBytecodes[] cbcs)
+            throws ClassInstallException, EngineTerminationException;
 
     /**
-     * Initializes the instance. No methods in this interface can be called
-     * before this.
+     * Invokes an executable Snippet by calling a method on the specified
+     * wrapper class. The method must have no arguments and return String.
      *
-     * @param env the execution environment information provided by JShell
-     * @throws Exception if the instance is unable to initialize
-     */
-    void start(ExecutionEnv env) throws Exception;
+     * @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 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;
 
     /**
-     * Shuts down this execution engine. Implementation should free all
-     * resources held by this execution engine.
-     * <p>
-     * No calls to methods on this interface should be made after close.
-     */
-    void close();
+     * 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;
 
     /**
      * Adds the path to the execution class path.
      *
      * @param path the path to add
-     * @return true if successful
+     * @throws EngineTerminationException the execution engine has terminated
+     * @throws InternalException an internal problem occurred
      */
-    boolean addToClasspath(String path);
+    void addToClasspath(String path)
+            throws EngineTerminationException, InternalException;
 
     /**
-     * Invokes an executable Snippet by calling a method on the specified
-     * wrapper class. The method must have no arguments and return String.
+     * Sets the execution class path to the specified path.
      *
-     * @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
+     * @param path the path to add
+     * @throws EngineTerminationException the execution engine has terminated
+     * @throws InternalException an internal problem occurred
      */
-    String invoke(String classname, String methodname) throws JShellException;
+    void setClasspath(String path)
+            throws EngineTerminationException, InternalException;
 
     /**
-     * Attempts to load new classes. Class bytes are retrieved from
-     * {@link ExecutionEnv#getClassBytes(java.lang.String) }
+     * Interrupts a running invoke.
      *
-     * @param classes list of class names to load
-     * @return true if load succeeded
+     * @throws EngineTerminationException the execution engine has terminated
+     * @throws InternalException an internal problem occurred
      */
-    boolean load(Collection<String> classes);
+    void stop()
+            throws EngineTerminationException, InternalException;
 
     /**
-     * Attempts to redefine previously loaded classes. Class bytes are retrieved
-     * from {@link ExecutionEnv#getClassBytes(java.lang.String) }
-     *
-     * @param classes list of class names to redefine
-     * @return true if redefine succeeded
+     * Shuts down this execution engine. Implementation should free all
+     * resources held by this execution engine.
+     * <p>
+     * No calls to methods on this interface should be made after close.
      */
-    boolean redefine(Collection<String> classes);
+    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);
+        }
+    }
 
     /**
-     * Queries if the class is loaded and the class bytes are current.
-     *
-     * @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}
+     * Unbidden execution engine termination has occurred.
      */
-    ClassStatus getClassStatus(String classname);
+    @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;
 
     /**
-     * Interrupt a running invoke.
+         * 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
      */
-    void stop();
+        public ResolutionException(int id, StackTraceElement[] stackElements) {
+            super("resolution exception: " + id);
+            this.id = id;
+            this.setStackTrace(stackElements);
+        }
 
     /**
-     * Returns the value of a variable.
+         * Retrieves the internal identifier of the unresolved identifier.
      *
-     * @param classname the name of the wrapper class of the variable
-     * @param varname the name of the variable
-     * @return the value of the variable
+         * @return the internal identifier
      */
-    String varValue(String classname, String varname);
+        public int id() {
+            return id;
+        }
+    }
+
+    @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+    public static class StoppedException extends RunException {
+
+        public StoppedException() {
+            super("stopped by stop()");
+        }
+    }
+
 }