1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.jshell.spi;
  26 
  27 import java.io.Serializable;
  28 
  29 /**
  30  * This interface specifies the functionality that must provided to implement a
  31  * pluggable JShell execution engine.
  32  * <p>
  33  * The audience for this Service Provider Interface is engineers wishing to
  34  * implement their own version of the execution engine in support of the JShell
  35  * API.
  36  * <p>
  37  * A Snippet is compiled into code wrapped in a 'wrapper class'. The execution
  38  * engine is used by the core JShell implementation to load and, for executable
  39  * Snippets, execute the Snippet.
  40  * <p>
  41  * Methods defined in this interface should only be called by the core JShell
  42  * implementation.
  43  * <p>
  44  * To install an {@code ExecutionControl}, its {@code Generator} is passed to
  45  * {@link jdk.jshell.JShell.Builder#executionEngine(ExecutionControl.Generator)  }.
  46  */
  47 public interface ExecutionControl {
  48 
  49     /**
  50      * Defines a functional interface for creating {@link ExecutionControl}
  51      * instances.
  52      */
  53     public interface Generator {
  54 
  55         /**
  56          * Generates an execution engine, given an execution environment.
  57          *
  58          * @param env the context in which the {@link ExecutionControl} is to
  59          * be created
  60          * @return the created instance
  61          * @throws Throwable if problems occurred
  62          */
  63         ExecutionControl generate(ExecutionEnv env) throws Throwable;
  64     }
  65 
  66     /**
  67      * Attempts to load new classes.
  68      *
  69      * @param cbcs the class name and bytecodes to load
  70      * @throws ClassInstallException exception occurred loading the classes,
  71      * some or all were not loaded
  72      * @throws NotImplementedException if not implemented
  73      * @throws EngineTerminationException the execution engine has terminated
  74      */
  75     void load(ClassBytecodes[] cbcs)
  76             throws ClassInstallException, NotImplementedException, EngineTerminationException;
  77 
  78     /**
  79      * Attempts to redefine previously loaded classes.
  80      *
  81      * @param cbcs the class name and bytecodes to redefine
  82      * @throws ClassInstallException exception occurred redefining the classes,
  83      * some or all were not redefined
  84      * @throws NotImplementedException if not implemented
  85      * @throws EngineTerminationException the execution engine has terminated
  86      */
  87     void redefine(ClassBytecodes[] cbcs)
  88             throws ClassInstallException, NotImplementedException, EngineTerminationException;
  89 
  90     /**
  91      * Invokes an executable Snippet by calling a method on the specified
  92      * wrapper class. The method must have no arguments and return String.
  93      *
  94      * @param className the class whose method should be invoked
  95      * @param methodName the name of method to invoke
  96      * @return the result of the execution or null if no result
  97      * @throws UserException the invoke raised a user exception
  98      * @throws ResolutionException the invoke attempted to directly or
  99      * indirectly invoke an unresolved snippet
 100      * @throws StoppedException if the {@code invoke()} was canceled by
 101      * {@link ExecutionControl#stop}
 102      * @throws EngineTerminationException the execution engine has terminated
 103      * @throws InternalException an internal problem occurred
 104      */
 105     String invoke(String className, String methodName)
 106             throws RunException, EngineTerminationException, InternalException;
 107 
 108     /**
 109      * Returns the value of a variable.
 110      *
 111      * @param className the name of the wrapper class of the variable
 112      * @param varName the name of the variable
 113      * @return the value of the variable
 114      * @throws UserException formatting the value raised a user exception
 115      * @throws ResolutionException formatting the value attempted to directly or
 116      * indirectly invoke an unresolved snippet
 117      * @throws StoppedException if the formatting the value was canceled by
 118      * {@link ExecutionControl#stop}
 119      * @throws EngineTerminationException the execution engine has terminated
 120      * @throws InternalException an internal problem occurred
 121      */
 122     String varValue(String className, String varName)
 123             throws RunException, EngineTerminationException, InternalException;
 124 
 125     /**
 126      * Adds the path to the execution class path.
 127      *
 128      * @param path the path to add
 129      * @throws EngineTerminationException the execution engine has terminated
 130      * @throws InternalException an internal problem occurred
 131      */
 132     void addToClasspath(String path)
 133             throws EngineTerminationException, InternalException;
 134 
 135     /**
 136      * Sets the execution class path to the specified path.
 137      *
 138      * @param path the path to add
 139      * @throws EngineTerminationException the execution engine has terminated
 140      * @throws InternalException an internal problem occurred
 141      */
 142     void setClasspath(String path)
 143             throws EngineTerminationException, InternalException;
 144 
 145     /**
 146      * Interrupts a running invoke.
 147      *
 148      * @throws EngineTerminationException the execution engine has terminated
 149      * @throws InternalException an internal problem occurred
 150      */
 151     void stop()
 152             throws EngineTerminationException, InternalException;
 153 
 154     /**
 155      * Run a non-standard command (or a standard command from a newer version).
 156      *
 157      * @param command the non-standard command
 158      * @param arg the commands argument
 159      * @return the commands return value
 160      * @throws UserException the command raised a user exception
 161      * @throws ResolutionException the command attempted to directly or
 162      * indirectly invoke an unresolved snippet
 163      * @throws StoppedException if the command was canceled by
 164      * {@link ExecutionControl#stop}
 165      * @throws EngineTerminationException the execution engine has terminated
 166      * @throws NotImplementedException if not implemented
 167      * @throws InternalException an internal problem occurred
 168      */
 169     Object extensionCommand(String command, Object arg)
 170             throws RunException, EngineTerminationException, InternalException;
 171 
 172     /**
 173      * Shuts down this execution engine. Implementation should free all
 174      * resources held by this execution engine.
 175      * <p>
 176      * No calls to methods on this interface should be made after close.
 177      */
 178     void close();
 179 
 180     /**
 181      * Bundles class name with class bytecodes.
 182      */
 183     public static final class ClassBytecodes implements Serializable {
 184 
 185         private static final long serialVersionUID = 0xC1A55B47EC0DE5L;
 186         private final String name;
 187         private final byte[] bytecodes;
 188 
 189         /**
 190          * Creates a name/bytecode pair.
 191          * @param name the class name
 192          * @param bytecodes the class bytecodes
 193          */
 194         public ClassBytecodes(String name, byte[] bytecodes) {
 195             this.name = name;
 196             this.bytecodes = bytecodes;
 197         }
 198 
 199         /**
 200          * The bytecodes for the class.
 201          *
 202          * @return the bytecodes
 203          */
 204         public byte[] bytecodes() {
 205             return bytecodes;
 206         }
 207 
 208         /**
 209          * The class name.
 210          *
 211          * @return the class name
 212          */
 213         public String name() {
 214             return name;
 215         }
 216     }
 217 
 218     /**
 219      * The abstract base of all {@code ExecutionControl} exceptions.
 220      */
 221     public static abstract class ExecutionControlException extends Exception {
 222 
 223         private static final long serialVersionUID = 1L;
 224 
 225         public ExecutionControlException(String message) {
 226             super(message);
 227         }
 228     }
 229 
 230     /**
 231      * Unbidden execution engine termination has occurred.
 232      */
 233     public static class EngineTerminationException extends ExecutionControlException {
 234 
 235         private static final long serialVersionUID = 1L;
 236 
 237         public EngineTerminationException(String message) {
 238             super(message);
 239         }
 240     }
 241 
 242     /**
 243      * The command is not implemented.
 244      */
 245     public static class NotImplementedException extends InternalException {
 246 
 247         private static final long serialVersionUID = 1L;
 248 
 249         public NotImplementedException(String message) {
 250             super(message);
 251         }
 252     }
 253 
 254     /**
 255      * An internal problem has occurred.
 256      */
 257     public static class InternalException extends ExecutionControlException {
 258 
 259         private static final long serialVersionUID = 1L;
 260 
 261         public InternalException(String message) {
 262             super(message);
 263         }
 264     }
 265 
 266     /**
 267      * A class install (load or redefine) encountered a problem.
 268      */
 269     public static class ClassInstallException extends ExecutionControlException {
 270 
 271         private static final long serialVersionUID = 1L;
 272 
 273         private final boolean[] installed;
 274 
 275         public ClassInstallException(String message, boolean[] installed) {
 276             super(message);
 277             this.installed = installed;
 278         }
 279 
 280         /**
 281          * Indicates which of the passed classes were successfully
 282          * loaded/redefined.
 283          * @return a one-to-one array with the {@link ClassBytecodes}{@code[]}
 284          * array -- {@code true} if installed
 285          */
 286         public boolean[] installed() {
 287             return installed;
 288         }
 289     }
 290 
 291     /**
 292      * The abstract base of of exceptions specific to running user code.
 293      */
 294     public static abstract class RunException extends ExecutionControlException {
 295 
 296         private static final long serialVersionUID = 1L;
 297 
 298         private RunException(String message) {
 299             super(message);
 300         }
 301     }
 302 
 303     /**
 304      * A 'normal' user exception occurred.
 305      */
 306     public static class UserException extends RunException {
 307 
 308         private static final long serialVersionUID = 1L;
 309 
 310         private final String causeExceptionClass;
 311 
 312         public UserException(String message, String causeExceptionClass, StackTraceElement[] stackElements) {
 313             super(message);
 314             this.causeExceptionClass = causeExceptionClass;
 315             this.setStackTrace(stackElements);
 316         }
 317 
 318         /**
 319          * Returns the class of the user exception.
 320          * @return the name of the user exception class
 321          */
 322         public String causeExceptionClass() {
 323             return causeExceptionClass;
 324         }
 325     }
 326 
 327     /**
 328      * An exception indicating that a {@code DeclarationSnippet} with unresolved
 329      * references has been encountered.
 330      * <p>
 331      * Contrast this with the initiating {@link SPIResolutionException}
 332      * (a {@code RuntimeException}) which is embedded in generated corralled
 333      * code.  Also, contrast this with
 334      * {@link jdk.jshell.UnresolvedReferenceException} the high-level
 335      * exception (with {@code DeclarationSnippet} reference) provided in the
 336      * main API.
 337      */
 338     public static class ResolutionException extends RunException {
 339 
 340         private static final long serialVersionUID = 1L;
 341 
 342         private final int id;
 343 
 344         /**
 345          * Constructs an exception indicating that a {@code DeclarationSnippet}
 346          * with unresolved references has been encountered.
 347          *
 348          * @param id An internal identifier of the specific method
 349          * @param stackElements the stack trace
 350          */
 351         public ResolutionException(int id, StackTraceElement[] stackElements) {
 352             super("resolution exception: " + id);
 353             this.id = id;
 354             this.setStackTrace(stackElements);
 355         }
 356 
 357         /**
 358          * Retrieves the internal identifier of the unresolved identifier.
 359          *
 360          * @return the internal identifier
 361          */
 362         public int id() {
 363             return id;
 364         }
 365     }
 366 
 367     /**
 368      * An exception indicating that an
 369      * {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
 370      * (or theoretically a
 371      * {@link ExecutionControl#varValue(java.lang.String, java.lang.String) })
 372      * has been interrupted by a {@link ExecutionControl#stop() }.
 373      */
 374     public static class StoppedException extends RunException {
 375 
 376         private static final long serialVersionUID = 1L;
 377 
 378         public StoppedException() {
 379             super("stopped by stop()");
 380         }
 381     }
 382 
 383 }