1 /*
  2  * Copyright (c) 2019, 2020, 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.internal.reflect;
 26 
 27 import java.lang.invoke.MethodHandle;
 28 import java.lang.invoke.MethodHandles;
 29 import java.lang.reflect.Constructor;
 30 import java.lang.reflect.InvocationTargetException;
 31 import java.lang.reflect.Method;
 32 import java.lang.reflect.Modifier;
 33 import static java.lang.invoke.MethodType.methodType;
 34 import jdk.internal.access.JavaLangInvokeAccess;
 35 import jdk.internal.access.SharedSecrets;
 36 
 37 /**
 38  * Factory to create MethodAccessor and ConstructorAccessor implementations
 39  * based on method handles.
 40  */
 41 class NewAccessorImplFactory {
 42     private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
 43     private static final MethodHandle WRAP;
 44     static {
 45         try {
 46             var methodType = methodType(void.class, Throwable.class);
 47             WRAP = JLIA.privilegedFindStatic(NewAccessorImplFactory.class, "wrap", methodType);
 48         } catch (Exception e) {
 49             throw new InternalError(e);
 50         }
 51     }
 52 
 53     private NewAccessorImplFactory() { }
 54 
 55     /**
 56      * Creates a MethodAccessorImpl to invoke the given method. The method should
 57      * not be caller sensitive.
 58      */
 59     static MethodAccessorImpl newMethodAccessorImpl(Method method) {
 60         assert !Reflection.isCallerSensitive(method);
 61 
 62         MethodHandle target = JLIA.privilegedUnreflect(method);
 63 
 64         // adapt to run with exception handler that throws InvocationTargetException
 65         target = adaptToWrapException(target);
 66 
 67         int paramCount = method.getParameterCount();
 68         if (Modifier.isStatic(method.getModifiers())) {
 69             MethodHandle spreader = target.asSpreader(Object[].class, paramCount);
 70             spreader = MethodHandles.dropArguments(spreader, 0, Object.class);
 71             target = spreader.asType(methodType(Object.class, Object.class, Object[].class));
 72         } else {
 73             // instance method
 74             MethodHandle spreader = target.asSpreader(Object[].class, paramCount);
 75             target = spreader.asType(methodType(Object.class, Object.class, Object[].class));
 76         }
 77 
 78         MethodHandle targetMethod = target;
 79         return new MethodAccessorImpl() {
 80             @Override
 81             public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
 82                 try {
 83                     return targetMethod.invokeExact(obj, args);
 84                 } catch (IllegalArgumentException | InvocationTargetException e) {
 85                     throw e;
 86                 } catch (ClassCastException | NullPointerException e) {
 87                     throw new IllegalArgumentException(e.getMessage());
 88                 } catch (Throwable e) {
 89                     throw new InvocationTargetException(e);
 90                 }
 91             }
 92         };
 93     }
 94 
 95     /**
 96      * Creates a ConstructorAccessorImpl to construct an object with the given
 97      * constructor.
 98      */
 99     static ConstructorAccessorImpl newConstructorAccessorImpl(Constructor<?> ctor) {
100         MethodHandle target = JLIA.privilegedUnreflect(ctor);
101 
102         // adapt to run with exception handler that throws InvocationTargetException
103         target = adaptToWrapException(target);
104 
105         int paramCount = ctor.getParameterCount();
106         MethodHandle spreader = target.asSpreader(Object[].class, paramCount);
107         target = spreader.asType(methodType(Object.class, Object[].class));
108 
109         MethodHandle targetMethod = target;
110         return new ConstructorAccessorImpl() {
111             @Override
112             public Object newInstance(Object[] args) throws InvocationTargetException {
113                 try {
114                     return targetMethod.invokeExact(args);
115                 } catch (IllegalArgumentException | InvocationTargetException e) {
116                     throw e;
117                 } catch (ClassCastException | NullPointerException e) {
118                     throw new IllegalArgumentException(e.getMessage());
119                 } catch (Throwable e) {
120                     throw new InvocationTargetException(e);
121                 }
122             }
123         };
124     }
125 
126     /**
127      * Creates a method that adapts the given method handle to run into
128      * inside an exception handler.
129      */
130     private static MethodHandle adaptToWrapException(MethodHandle target) {
131         MethodHandle wrapper = WRAP.asType(methodType(target.type().returnType(), Throwable.class));
132         return MethodHandles.catchException(target, Throwable.class, wrapper);
133     }
134 
135     /**
136      * Throws InvocationTargetException with the given exception or error
137      * as cause.
138      */
139     private static void wrap(Throwable e) throws InvocationTargetException {
140         throw new InvocationTargetException(e);
141     }
142 }