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.execution;
  26 
  27 import java.lang.reflect.Field;
  28 import java.lang.reflect.InvocationTargetException;
  29 import java.lang.reflect.Method;
  30 import jdk.jshell.spi.ExecutionControl;
  31 import jdk.jshell.spi.SPIResolutionException;
  32 
  33 /**
  34  * An {@link ExecutionControl} implementation that runs in the current process.
  35  * May be used directly, or over a channel with
  36  * {@link Util#forwardExecutionControl(ExecutionControl, java.io.ObjectInput, java.io.ObjectOutput) }.
  37  *
  38  * @author Robert Field
  39  * @author Jan Lahoda
  40  */
  41 public class DirectExecutionControl implements ExecutionControl {
  42 
  43     private final LoaderDelegate loaderDelegate;
  44 
  45     /**
  46      * Creates an instance, delegating loader operations to the specified
  47      * delegate.
  48      *
  49      * @param loaderDelegate the delegate to handle loading classes
  50      */
  51     public DirectExecutionControl(LoaderDelegate loaderDelegate) {
  52         this.loaderDelegate = loaderDelegate;
  53     }
  54 
  55     /**
  56      * Create an instance using the default class loading.
  57      */
  58     public DirectExecutionControl() {
  59         this(new DefaultLoaderDelegate());
  60     }
  61 
  62     @Override
  63     public void load(ClassBytecodes[] cbcs)
  64             throws ClassInstallException, NotImplementedException, EngineTerminationException {
  65         loaderDelegate.load(cbcs);
  66     }
  67 
  68     @Override
  69     public void redefine(ClassBytecodes[] cbcs)
  70             throws ClassInstallException, NotImplementedException, EngineTerminationException {
  71         throw new NotImplementedException("redefine not supported");
  72     }
  73 
  74     @Override
  75     public String invoke(String className, String methodName)
  76             throws RunException, InternalException, EngineTerminationException {
  77         Method doitMethod;
  78         try {
  79             Class<?> klass = findClass(className);
  80             doitMethod = klass.getDeclaredMethod(methodName, new Class<?>[0]);
  81             doitMethod.setAccessible(true);
  82         } catch (Throwable ex) {
  83             throw new InternalException(ex.toString());
  84         }
  85 
  86         try {
  87             clientCodeEnter();
  88             String result = invoke(doitMethod);
  89             System.out.flush();
  90             return result;
  91         } catch (RunException | InternalException | EngineTerminationException ex) {
  92             throw ex;
  93         } catch (SPIResolutionException ex) {
  94             return throwConvertedInvocationException(ex);
  95         } catch (InvocationTargetException ex) {
  96             return throwConvertedInvocationException(ex.getCause());
  97         } catch (Throwable ex) {
  98             return throwConvertedOtherException(ex);
  99         } finally {
 100             clientCodeLeave();
 101         }
 102     }
 103 
 104     @Override
 105     public String varValue(String className, String varName)
 106             throws RunException, EngineTerminationException, InternalException {
 107         Object val;
 108         try {
 109             Class<?> klass = findClass(className);
 110             Field var = klass.getDeclaredField(varName);
 111             var.setAccessible(true);
 112             val = var.get(null);
 113         } catch (Throwable ex) {
 114             throw new InternalException(ex.toString());
 115         }
 116 
 117         try {
 118             clientCodeEnter();
 119             return valueString(val);
 120         } catch (Throwable ex) {
 121             return throwConvertedInvocationException(ex);
 122         } finally {
 123             clientCodeLeave();
 124         }
 125     }
 126 
 127     @Override
 128     public void addToClasspath(String cp)
 129             throws EngineTerminationException, InternalException {
 130         loaderDelegate.addToClasspath(cp);
 131     }
 132 
 133     @Override
 134     public void setClasspath(String path)
 135             throws EngineTerminationException, InternalException {
 136         loaderDelegate.setClasspath(path);
 137     }
 138 
 139     /**
 140      * {@inheritDoc}
 141      * <p>
 142      * Not supported.
 143      */
 144     @Override
 145     public void stop()
 146             throws EngineTerminationException, InternalException {
 147         throw new NotImplementedException("stop: Not supported.");
 148     }
 149 
 150     @Override
 151     public Object extensionCommand(String command, Object arg)
 152             throws RunException, EngineTerminationException, InternalException {
 153         throw new NotImplementedException("Unknown command: " + command);
 154     }
 155 
 156     @Override
 157     public void close() {
 158     }
 159 
 160     /**
 161      * Finds the class with the specified binary name.
 162      *
 163      * @param name the binary name of the class
 164      * @return the Class Object
 165      * @throws ClassNotFoundException if the class could not be found
 166      */
 167     protected Class<?> findClass(String name) throws ClassNotFoundException {
 168         return loaderDelegate.findClass(name);
 169     }
 170 
 171     /**
 172      * Invoke the specified "doit-method", a static method with no parameters.
 173      * The {@link DirectExecutionControl#invoke(java.lang.String, java.lang.String) }
 174      * in this class will call this to invoke.
 175      *
 176      * @param doitMethod the Method to invoke
 177      * @return the value or null
 178      * @throws Exception any exceptions thrown by
 179      * {@link java.lang.reflect.Method#invoke(Object, Object...) }
 180      * or any {@link ExecutionControl.ExecutionControlException}
 181      * to pass-through.
 182      */
 183     protected String invoke(Method doitMethod) throws Exception {
 184         Object res = doitMethod.invoke(null, new Object[0]);
 185         return valueString(res);
 186     }
 187 
 188     /**
 189      * Converts the {@code Object} value from
 190      * {@link ExecutionControl#invoke(String, String)  } or
 191      * {@link ExecutionControl#varValue(String, String)   } to {@code String}.
 192      *
 193      * @param value the value to convert
 194      * @return the {@code String} representation
 195      */
 196     protected static String valueString(Object value) {
 197         if (value == null) {
 198             return "null";
 199         } else if (value instanceof String) {
 200             return "\"" + (String) value + "\"";
 201         } else if (value instanceof Character) {
 202             return "'" + value + "'";
 203         } else {
 204             return value.toString();
 205         }
 206     }
 207 
 208     /**
 209      * Converts incoming exceptions in user code into instances of subtypes of
 210      * {@link ExecutionControl.ExecutionControlException} and throws the
 211      * converted exception.
 212      *
 213      * @param cause the exception to convert
 214      * @return never returns as it always throws
 215      * @throws ExecutionControl.RunException for normal exception occurrences
 216      * @throws ExecutionControl.InternalException for internal problems
 217      */
 218     protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException {
 219         if (cause instanceof SPIResolutionException) {
 220             SPIResolutionException spire = (SPIResolutionException) cause;
 221             throw new ResolutionException(spire.id(), spire.getStackTrace());
 222         } else {
 223             throw new UserException(cause.getMessage(), cause.getClass().getName(), cause.getStackTrace());
 224         }
 225     }
 226 
 227     /**
 228      * Converts incoming exceptions in agent code into instances of subtypes of
 229      * {@link ExecutionControl.ExecutionControlException} and throws the
 230      * converted exception.
 231      *
 232      * @param ex the exception to convert
 233      * @return never returns as it always throws
 234      * @throws ExecutionControl.RunException for normal exception occurrences
 235      * @throws ExecutionControl.InternalException for internal problems
 236      */
 237     protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException {
 238         throw new InternalException(ex.toString());
 239     }
 240 
 241     /**
 242      * Marks entry into user code.
 243      *
 244      * @throws ExecutionControl.InternalException in unexpected failure cases
 245      */
 246     protected void clientCodeEnter() throws InternalException {
 247     }
 248 
 249     /**
 250      * Marks departure from user code.
 251      *
 252      * @throws ExecutionControl.InternalException in unexpected failure cases
 253      */
 254     protected void clientCodeLeave() throws InternalException {
 255     }
 256 
 257 }