src/jdk.jshell/share/classes/jdk/jshell/execution/DirectExecutionControl.java
changeset 47216 71c04702a3d5
parent 45044 7c50549b7744
child 49515 083318155ad1
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2016, 2017, 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.Array;
       
    28 import java.lang.reflect.Field;
       
    29 import java.lang.reflect.InvocationTargetException;
       
    30 import java.lang.reflect.Method;
       
    31 import java.util.stream.IntStream;
       
    32 import jdk.jshell.spi.ExecutionControl;
       
    33 import jdk.jshell.spi.SPIResolutionException;
       
    34 
       
    35 /**
       
    36  * An {@link ExecutionControl} implementation that runs in the current process.
       
    37  * May be used directly, or over a channel with
       
    38  * {@link Util#forwardExecutionControl(ExecutionControl, java.io.ObjectInput, java.io.ObjectOutput) }.
       
    39  *
       
    40  * @author Robert Field
       
    41  * @author Jan Lahoda
       
    42  * @since 9
       
    43  */
       
    44 public class DirectExecutionControl implements ExecutionControl {
       
    45 
       
    46     private static final String[] charRep;
       
    47 
       
    48     static {
       
    49         charRep = new String[256];
       
    50         for (int i = 0; i < charRep.length; ++i) {
       
    51             charRep[i] = Character.isISOControl(i)
       
    52                     ? String.format("\\%03o", i)
       
    53                     : "" + (char) i;
       
    54         }
       
    55         charRep['\b'] = "\\b";
       
    56         charRep['\t'] = "\\t";
       
    57         charRep['\n'] = "\\n";
       
    58         charRep['\f'] = "\\f";
       
    59         charRep['\r'] = "\\r";
       
    60         charRep['\\'] = "\\\\";
       
    61     }
       
    62 
       
    63     private final LoaderDelegate loaderDelegate;
       
    64 
       
    65     /**
       
    66      * Creates an instance, delegating loader operations to the specified
       
    67      * delegate.
       
    68      *
       
    69      * @param loaderDelegate the delegate to handle loading classes
       
    70      */
       
    71     public DirectExecutionControl(LoaderDelegate loaderDelegate) {
       
    72         this.loaderDelegate = loaderDelegate;
       
    73     }
       
    74 
       
    75     /**
       
    76      * Create an instance using the default class loading.
       
    77      */
       
    78     public DirectExecutionControl() {
       
    79         this(new DefaultLoaderDelegate());
       
    80     }
       
    81 
       
    82     @Override
       
    83     public void load(ClassBytecodes[] cbcs)
       
    84             throws ClassInstallException, NotImplementedException, EngineTerminationException {
       
    85         loaderDelegate.load(cbcs);
       
    86     }
       
    87 
       
    88     @Override
       
    89     public void redefine(ClassBytecodes[] cbcs)
       
    90             throws ClassInstallException, NotImplementedException, EngineTerminationException {
       
    91         throw new NotImplementedException("redefine not supported");
       
    92     }
       
    93 
       
    94     /**Notify that classes have been redefined.
       
    95      *
       
    96      * @param cbcs the class name and bytecodes to redefine
       
    97      * @throws NotImplementedException if not implemented
       
    98      * @throws EngineTerminationException the execution engine has terminated
       
    99      */
       
   100     protected void classesRedefined(ClassBytecodes[] cbcs)
       
   101             throws NotImplementedException, EngineTerminationException {
       
   102         loaderDelegate.classesRedefined(cbcs);
       
   103     }
       
   104 
       
   105     @Override
       
   106     public String invoke(String className, String methodName)
       
   107             throws RunException, InternalException, EngineTerminationException {
       
   108         Method doitMethod;
       
   109         try {
       
   110             Class<?> klass = findClass(className);
       
   111             doitMethod = klass.getDeclaredMethod(methodName, new Class<?>[0]);
       
   112             doitMethod.setAccessible(true);
       
   113         } catch (Throwable ex) {
       
   114             throw new InternalException(ex.toString());
       
   115         }
       
   116 
       
   117         try {
       
   118             clientCodeEnter();
       
   119             String result = invoke(doitMethod);
       
   120             System.out.flush();
       
   121             return result;
       
   122         } catch (RunException | InternalException | EngineTerminationException ex) {
       
   123             throw ex;
       
   124         } catch (SPIResolutionException ex) {
       
   125             return throwConvertedInvocationException(ex);
       
   126         } catch (InvocationTargetException ex) {
       
   127             return throwConvertedInvocationException(ex.getCause());
       
   128         } catch (Throwable ex) {
       
   129             return throwConvertedOtherException(ex);
       
   130         } finally {
       
   131             clientCodeLeave();
       
   132         }
       
   133     }
       
   134 
       
   135     @Override
       
   136     public String varValue(String className, String varName)
       
   137             throws RunException, EngineTerminationException, InternalException {
       
   138         Object val;
       
   139         try {
       
   140             Class<?> klass = findClass(className);
       
   141             Field var = klass.getDeclaredField(varName);
       
   142             var.setAccessible(true);
       
   143             val = var.get(null);
       
   144         } catch (Throwable ex) {
       
   145             throw new InternalException(ex.toString());
       
   146         }
       
   147 
       
   148         try {
       
   149             clientCodeEnter();
       
   150             return valueString(val);
       
   151         } catch (Throwable ex) {
       
   152             return throwConvertedInvocationException(ex);
       
   153         } finally {
       
   154             clientCodeLeave();
       
   155         }
       
   156     }
       
   157 
       
   158     @Override
       
   159     public void addToClasspath(String cp)
       
   160             throws EngineTerminationException, InternalException {
       
   161         loaderDelegate.addToClasspath(cp);
       
   162     }
       
   163 
       
   164     /**
       
   165      * {@inheritDoc}
       
   166      * <p>
       
   167      * Not supported.
       
   168      */
       
   169     @Override
       
   170     public void stop()
       
   171             throws EngineTerminationException, InternalException {
       
   172         throw new NotImplementedException("stop: Not supported.");
       
   173     }
       
   174 
       
   175     @Override
       
   176     public Object extensionCommand(String command, Object arg)
       
   177             throws RunException, EngineTerminationException, InternalException {
       
   178         throw new NotImplementedException("Unknown command: " + command);
       
   179     }
       
   180 
       
   181     @Override
       
   182     public void close() {
       
   183     }
       
   184 
       
   185     /**
       
   186      * Finds the class with the specified binary name.
       
   187      *
       
   188      * @param name the binary name of the class
       
   189      * @return the Class Object
       
   190      * @throws ClassNotFoundException if the class could not be found
       
   191      */
       
   192     protected Class<?> findClass(String name) throws ClassNotFoundException {
       
   193         return loaderDelegate.findClass(name);
       
   194     }
       
   195 
       
   196     /**
       
   197      * Invoke the specified "doit-method", a static method with no parameters.
       
   198      * The {@link DirectExecutionControl#invoke(java.lang.String, java.lang.String) }
       
   199      * in this class will call this to invoke.
       
   200      *
       
   201      * @param doitMethod the Method to invoke
       
   202      * @return the value or null
       
   203      * @throws Exception any exceptions thrown by
       
   204      * {@link java.lang.reflect.Method#invoke(Object, Object...) }
       
   205      * or any {@link ExecutionControl.ExecutionControlException}
       
   206      * to pass-through.
       
   207      */
       
   208     protected String invoke(Method doitMethod) throws Exception {
       
   209         Object res = doitMethod.invoke(null, new Object[0]);
       
   210         return valueString(res);
       
   211     }
       
   212 
       
   213     /**
       
   214      * Converts the {@code Object} value from
       
   215      * {@link ExecutionControl#invoke(String, String)  } or
       
   216      * {@link ExecutionControl#varValue(String, String)   } to {@code String}.
       
   217      *
       
   218      * @param value the value to convert
       
   219      * @return the {@code String} representation
       
   220      */
       
   221     protected static String valueString(Object value) {
       
   222         if (value == null) {
       
   223             return "null";
       
   224         } else if (value instanceof String) {
       
   225             return "\"" + ((String) value).codePoints()
       
   226                     .flatMap(cp ->
       
   227                         (cp == '"')
       
   228                             ? "\\\"".codePoints()
       
   229                             : (cp < 256)
       
   230                                 ? charRep[cp].codePoints()
       
   231                                 : IntStream.of(cp))
       
   232                     .collect(
       
   233                             StringBuilder::new,
       
   234                             StringBuilder::appendCodePoint,
       
   235                             StringBuilder::append)
       
   236                     .toString() + "\"";
       
   237         } else if (value instanceof Character) {
       
   238             char cp = (char) (Character) value;
       
   239             return "'" + (
       
   240                 (cp == '\'')
       
   241                     ? "\\\'"
       
   242                     : (cp < 256)
       
   243                             ? charRep[cp]
       
   244                             : String.valueOf(cp)) + "'";
       
   245         } else if (value.getClass().isArray()) {
       
   246             int dims = 0;
       
   247             Class<?> t = value.getClass();
       
   248             while (true) {
       
   249                 Class<?> ct = t.getComponentType();
       
   250                 if (ct == null) {
       
   251                     break;
       
   252                 }
       
   253                 ++dims;
       
   254                 t = ct;
       
   255             }
       
   256             String tn = t.getTypeName();
       
   257             int len = Array.getLength(value);
       
   258             StringBuilder sb = new StringBuilder();
       
   259             sb.append(tn.substring(tn.lastIndexOf('.') + 1, tn.length()));
       
   260             sb.append("[");
       
   261             sb.append(len);
       
   262             sb.append("]");
       
   263             for (int i = 1; i < dims; ++i) {
       
   264                 sb.append("[]");
       
   265             }
       
   266             sb.append(" { ");
       
   267             for (int i = 0; i < len; ++i) {
       
   268                 sb.append(valueString(Array.get(value, i)));
       
   269                 if (i < len - 1) {
       
   270                     sb.append(", ");
       
   271                 }
       
   272             }
       
   273             sb.append(" }");
       
   274             return sb.toString();
       
   275         } else {
       
   276             return value.toString();
       
   277         }
       
   278     }
       
   279 
       
   280     /**
       
   281      * Converts incoming exceptions in user code into instances of subtypes of
       
   282      * {@link ExecutionControl.ExecutionControlException} and throws the
       
   283      * converted exception.
       
   284      *
       
   285      * @param cause the exception to convert
       
   286      * @return never returns as it always throws
       
   287      * @throws ExecutionControl.RunException for normal exception occurrences
       
   288      * @throws ExecutionControl.InternalException for internal problems
       
   289      */
       
   290     protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException {
       
   291         if (cause instanceof SPIResolutionException) {
       
   292             SPIResolutionException spire = (SPIResolutionException) cause;
       
   293             throw new ResolutionException(spire.id(), spire.getStackTrace());
       
   294         } else {
       
   295             throw new UserException(cause.getMessage(), cause.getClass().getName(), cause.getStackTrace());
       
   296         }
       
   297     }
       
   298 
       
   299     /**
       
   300      * Converts incoming exceptions in agent code into instances of subtypes of
       
   301      * {@link ExecutionControl.ExecutionControlException} and throws the
       
   302      * converted exception.
       
   303      *
       
   304      * @param ex the exception to convert
       
   305      * @return never returns as it always throws
       
   306      * @throws ExecutionControl.RunException for normal exception occurrences
       
   307      * @throws ExecutionControl.InternalException for internal problems
       
   308      */
       
   309     protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException {
       
   310         throw new InternalException(ex.toString());
       
   311     }
       
   312 
       
   313     /**
       
   314      * Marks entry into user code.
       
   315      *
       
   316      * @throws ExecutionControl.InternalException in unexpected failure cases
       
   317      */
       
   318     protected void clientCodeEnter() throws InternalException {
       
   319     }
       
   320 
       
   321     /**
       
   322      * Marks departure from user code.
       
   323      *
       
   324      * @throws ExecutionControl.InternalException in unexpected failure cases
       
   325      */
       
   326     protected void clientCodeLeave() throws InternalException {
       
   327     }
       
   328 
       
   329 }