1 /*
  2  * Copyright (c) 2021, 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 
 26 package jdk.internal.reflect;
 27 
 28 import java.lang.invoke.MethodHandle;
 29 import java.lang.invoke.MethodHandles;
 30 import java.lang.invoke.MethodType;
 31 import java.lang.reflect.Constructor;
 32 import java.lang.reflect.Executable;
 33 import java.lang.reflect.Field;
 34 import java.lang.reflect.Method;
 35 import java.lang.reflect.Modifier;
 36 
 37 import jdk.internal.access.JavaLangInvokeAccess;
 38 import jdk.internal.access.SharedSecrets;
 39 import jdk.internal.misc.Unsafe;
 40 import jdk.internal.misc.VM;
 41 
 42 import static java.lang.invoke.MethodType.genericMethodType;
 43 import static java.lang.invoke.MethodType.methodType;
 44 import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.*;
 45 
 46 final class MethodHandleAccessorFactory {
 47     /**
 48      * Creates a MethodAccessor for the given reflected method.
 49      *
 50      * If the given method is called before the java.lang.invoke initialization
 51      * or the given method is a native method, it will use the native VM reflection
 52      * support.
 53      *
 54      * If the given method is a caller-sensitive method and the corresponding
 55      * caller-sensitive adapter with the caller class parameter is present,
 56      * it will use the method handle of the caller-sensitive adapter.
 57      *
 58      * Otherwise, it will use the direct method handle of the given method.
 59      *
 60      * @see CallerSensitive
 61      * @see CallerSensitiveAdapter
 62      */
 63     static MethodAccessorImpl newMethodAccessor(Method method, boolean callerSensitive) {
 64         if (useNativeAccessor(method)) {
 65             return DirectMethodHandleAccessor.nativeAccessor(method, callerSensitive);
 66         }
 67 
 68         // ExceptionInInitializerError may be thrown during class initialization
 69         // Ensure class initialized outside the invocation of method handle
 70         // so that EIIE is propagated (not wrapped with ITE)
 71         ensureClassInitialized(method.getDeclaringClass());
 72 
 73         try {
 74             if (callerSensitive) {
 75                 var dmh = findCallerSensitiveAdapter(method);
 76                 if (dmh != null) {
 77                     return DirectMethodHandleAccessor.callerSensitiveAdapter(method, dmh);
 78                 }
 79             }
 80             var dmh = getDirectMethod(method, callerSensitive);
 81             return DirectMethodHandleAccessor.methodAccessor(method, dmh);
 82         } catch (IllegalAccessException e) {
 83             throw new InternalError(e);
 84         }
 85     }
 86 
 87     /**
 88      * Creates a ConstructorAccessor for the given reflected constructor.
 89      *
 90      * If a given constructor is called before the java.lang.invoke initialization,
 91      * it will use the native VM reflection support.
 92      *
 93      * Otherwise, it will use the direct method handle of the given constructor.
 94      */
 95     static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
 96         if (useNativeAccessor(ctor)) {
 97             return DirectConstructorHandleAccessor.nativeAccessor(ctor);
 98         }
 99 
100         // ExceptionInInitializerError may be thrown during class initialization
101         // Ensure class initialized outside the invocation of method handle
102         // so that EIIE is propagated (not wrapped with ITE)
103         ensureClassInitialized(ctor.getDeclaringClass());
104 
105         try {
106             MethodHandle mh = JLIA.unreflectConstructor(ctor);
107             int paramCount = mh.type().parameterCount();
108             MethodHandle target = mh.asFixedArity();
109             MethodType mtype = specializedMethodTypeForConstructor(paramCount);
110             if (paramCount > SPECIALIZED_PARAM_COUNT) {
111                 // spread the parameters only for the non-specialized case
112                 target = target.asSpreader(Object[].class, paramCount);
113             }
114             target = target.asType(mtype);
115             return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
116         } catch (IllegalAccessException e) {
117             throw new InternalError(e);
118         }
119     }
120 
121     /**
122      * Creates a FieldAccessor for the given reflected field.
123      *
124      * Limitation: Field access via core reflection is only supported after
125      * java.lang.invoke completes initialization.
126      * java.lang.invoke initialization starts soon after System::initPhase1
127      * and method handles are ready for use when initPhase2 begins.
128      * During early VM startup (initPhase1), fields can be accessed directly
129      * from the VM or through JNI.
130      */
131     static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
132         if (!VM.isJavaLangInvokeInited()) {
133             throw new InternalError(field.getDeclaringClass().getName() + "::" + field.getName() +
134                     " cannot be accessed reflectively before java.lang.invoke is initialized");
135         }
136 
137         // ExceptionInInitializerError may be thrown during class initialization
138         // Ensure class initialized outside the invocation of method handle
139         // so that EIIE is propagated (not wrapped with ITE)
140         ensureClassInitialized(field.getDeclaringClass());
141 
142         try {
143             // the declaring class of the field has been initialized
144             var getter = JLIA.unreflectField(field, false);
145             var setter = isReadOnly ? null : JLIA.unreflectField(field, true);
146             Class<?> type = field.getType();
147             if (type == Boolean.TYPE) {
148                 return MethodHandleBooleanFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
149             } else if (type == Byte.TYPE) {
150                 return MethodHandleByteFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
151             } else if (type == Short.TYPE) {
152                 return MethodHandleShortFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
153             } else if (type == Character.TYPE) {
154                 return MethodHandleCharacterFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
155             } else if (type == Integer.TYPE) {
156                 return MethodHandleIntegerFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
157             } else if (type == Long.TYPE) {
158                 return MethodHandleLongFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
159             } else if (type == Float.TYPE) {
160                 return MethodHandleFloatFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
161             } else if (type == Double.TYPE) {
162                 return MethodHandleDoubleFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
163             } else {
164                 return MethodHandleObjectFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
165             }
166         } catch (IllegalAccessException e) {
167             throw new InternalError(e);
168         }
169     }
170 
171     private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException {
172         var mtype = methodType(method.getReturnType(), method.getParameterTypes());
173         var isStatic = Modifier.isStatic(method.getModifiers());
174         var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype)
175                                         : JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype);
176         if (callerSensitive) {
177             // the reflectiveInvoker for caller-sensitive method expects the same signature
178             // as Method::invoke i.e. (Object, Object[])Object
179             return makeTarget(dmh, isStatic, false);
180         }
181         return makeSpecializedTarget(dmh, isStatic, false);
182     }
183 
184     /**
185      * Finds the method handle of a caller-sensitive adapter for the given
186      * caller-sensitive method.  It has the same name as the given method
187      * with a trailing caller class parameter.
188      *
189      * @see CallerSensitiveAdapter
190      */
191     private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException {
192         String name = method.getName();
193         // append a Class parameter
194         MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes())
195                                 .appendParameterTypes(Class.class);
196         boolean isStatic = Modifier.isStatic(method.getModifiers());
197 
198         MethodHandle dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), name, mtype)
199                                     : JLIA.findVirtual(method.getDeclaringClass(), name, mtype);
200         return dmh != null ? makeSpecializedTarget(dmh, isStatic, true) : null;
201     }
202 
203     /**
204      * Transform the given dmh to a specialized target method handle.
205      *
206      * If {@code hasCallerParameter} parameter is true, transform the method handle
207      * of this method type: {@code (Object, Object[], Class)Object} for the default
208      * case.
209      *
210      * If {@code hasCallerParameter} parameter is false, transform the method handle
211      * of this method type: {@code (Object, Object[])Object} for the default case.
212      *
213      * If the number of formal arguments is small, use a method type specialized
214      * the number of formal arguments is 0, 1, and 2, for example, the method type
215      * of a static method with one argument can be: {@code (Object)Object}
216      *
217      * If it's a static method, there is no leading Object parameter.
218      *
219      * @apiNote
220      * This implementation avoids using MethodHandles::catchException to help
221      * cold startup performance since this combination is very costly to setup.
222      *
223      * @param dmh DirectMethodHandle
224      * @param isStatic whether given dmh represents static method or not
225      * @param hasCallerParameter whether given dmh represents a method with an
226      *                         additional caller Class parameter
227      * @return transformed dmh to be used as a target in direct method accessors
228      */
229     static MethodHandle makeSpecializedTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
230         MethodHandle target = dmh.asFixedArity();
231 
232         // number of formal arguments to the original method (not the adapter)
233         // If it is a non-static method, it has a leading `this` argument.
234         // Also do not count the caller class argument
235         int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
236         MethodType mtype = specializedMethodType(isStatic, hasCallerParameter, paramCount);
237         if (paramCount > SPECIALIZED_PARAM_COUNT) {
238             int spreadArgPos = isStatic ? 0 : 1;
239             target = target.asSpreader(spreadArgPos, Object[].class, paramCount);
240         }
241         if (isStatic) {
242             // add leading 'this' parameter to static method which is then ignored
243             target = MethodHandles.dropArguments(target, 0, Object.class);
244         }
245         return target.asType(mtype);
246     }
247 
248     // specialize for number of formal arguments <= 3 to avoid spreader
249     static final int SPECIALIZED_PARAM_COUNT = 3;
250     static MethodType specializedMethodType(boolean isStatic, boolean hasCallerParameter, int paramCount) {
251         return switch (paramCount) {
252             case 0 -> hasCallerParameter ? methodType(Object.class, Object.class, Class.class)
253                                          : genericMethodType(1);
254             case 1 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Class.class)
255                                          : genericMethodType(2);
256             case 2 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Class.class)
257                                          : genericMethodType(3);
258             case 3 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Object.class, Class.class)
259                                          : genericMethodType(4);
260             default -> hasCallerParameter ? methodType(Object.class, Object.class, Object[].class, Class.class)
261                                           : genericMethodType(1, true);
262         };
263     }
264 
265     static MethodType specializedMethodTypeForConstructor(int paramCount) {
266         return switch (paramCount) {
267             case 0 ->  genericMethodType(0);
268             case 1 ->  genericMethodType(1);
269             case 2 ->  genericMethodType(2);
270             case 3 ->  genericMethodType(3);
271             default -> genericMethodType(0, true);
272         };
273     }
274 
275     /**
276      * Transforms the given dmh into a target method handle with the method type
277      * {@code (Object, Object[])Object} or {@code (Object, Class, Object[])Object}
278      */
279     static MethodHandle makeTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
280         MethodType mtype = hasCallerParameter
281                                 ? methodType(Object.class, Object.class, Object[].class, Class.class)
282                                 : genericMethodType(1, true);
283         // number of formal arguments
284         int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
285         int spreadArgPos = isStatic ? 0 : 1;
286         MethodHandle target = dmh.asFixedArity().asSpreader(spreadArgPos, Object[].class, paramCount);
287         if (isStatic) {
288             // add leading 'this' parameter to static method which is then ignored
289             target = MethodHandles.dropArguments(target, 0, Object.class);
290         }
291         return target.asType(mtype);
292     }
293 
294     /**
295      * Ensures the given class is initialized.  If this is called from <clinit>,
296      * this method returns but defc's class initialization is not completed.
297      */
298     static void ensureClassInitialized(Class<?> defc) {
299         if (UNSAFE.shouldBeInitialized(defc)) {
300             UNSAFE.ensureClassInitialized(defc);
301         }
302     }
303 
304     /*
305      * Returns true if NativeAccessor should be used.
306      */
307     private static boolean useNativeAccessor(Executable member) {
308         if (!VM.isJavaLangInvokeInited())
309             return true;
310 
311         if (Modifier.isNative(member.getModifiers()))
312             return true;
313 
314         if (ReflectionFactory.useNativeAccessorOnly())  // for testing only
315             return true;
316 
317         // MethodHandle::withVarargs on a member with varargs modifier bit set
318         // verifies that the last parameter of the member must be an array type.
319         // The JVMS does not require the last parameter descriptor of the method descriptor
320         // is an array type if the ACC_VARARGS flag is set in the access_flags item.
321         // Hence the reflection implementation does not check the last parameter type
322         // if ACC_VARARGS flag is set.  Workaround this by invoking through
323         // the native accessor.
324         int paramCount = member.getParameterCount();
325         if (member.isVarArgs() &&
326                 (paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) {
327             return true;
328         }
329         // A method handle cannot be created if its type has an arity >= 255
330         // as the method handle's invoke method consumes an extra argument
331         // of the method handle itself. Fall back to use the native implementation.
332         if (slotCount(member) >= MAX_JVM_ARITY) {
333             return true;
334         }
335         return false;
336     }
337 
338     private static final int MAX_JVM_ARITY = 255;  // this is mandated by the JVM spec.
339     /*
340      * Return number of slots of the given member.
341      * - long/double args counts for two argument slots
342      * - A non-static method consumes an extra argument for the object on which
343      *   the method is called.
344      * - A constructor consumes an extra argument for the object which is being constructed.
345      */
346     private static int slotCount(Executable member) {
347         int slots = 0;
348         Class<?>[] ptypes = member.getParameterTypes();
349         for (Class<?> ptype : ptypes) {
350             if (ptype == double.class || ptype == long.class) {
351                 slots++;
352             }
353         }
354         return ptypes.length + slots +
355                 (Modifier.isStatic(member.getModifiers()) ? 0 : 1);
356     }
357 
358     /*
359      * Delay initializing these static fields until java.lang.invoke is fully initialized.
360      */
361     static class LazyStaticHolder {
362         static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
363     }
364 
365     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
366 }