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 }