11/*
2- * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2828import java .lang .invoke .MethodHandle ;
2929import java .lang .invoke .MethodHandles ;
3030import java .lang .invoke .MethodType ;
31+ import java .lang .invoke .VarHandle ;
3132import java .lang .reflect .Constructor ;
3233import java .lang .reflect .Executable ;
3334import java .lang .reflect .Field ;
@@ -169,7 +170,7 @@ static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
169170 }
170171
171172 private static MethodHandle getDirectMethod (Method method , boolean callerSensitive ) throws IllegalAccessException {
172- var mtype = methodType (method .getReturnType (), method . getParameterTypes ( ));
173+ var mtype = methodType (method .getReturnType (), reflectionFactory . getExecutableSharedParameterTypes ( method ));
173174 var isStatic = Modifier .isStatic (method .getModifiers ());
174175 var dmh = isStatic ? JLIA .findStatic (method .getDeclaringClass (), method .getName (), mtype )
175176 : JLIA .findVirtual (method .getDeclaringClass (), method .getName (), mtype );
@@ -191,7 +192,7 @@ private static MethodHandle getDirectMethod(Method method, boolean callerSensiti
191192 private static MethodHandle findCallerSensitiveAdapter (Method method ) throws IllegalAccessException {
192193 String name = method .getName ();
193194 // append a Class parameter
194- MethodType mtype = methodType (method .getReturnType (), method . getParameterTypes ( ))
195+ MethodType mtype = methodType (method .getReturnType (), reflectionFactory . getExecutableSharedParameterTypes ( method ))
195196 .appendParameterTypes (Class .class );
196197 boolean isStatic = Modifier .isStatic (method .getModifiers ());
197198
@@ -303,29 +304,43 @@ static void ensureClassInitialized(Class<?> defc) {
303304
304305 /*
305306 * Returns true if NativeAccessor should be used.
307+ *
308+ * Native accessor, i.e. VM reflection implementation, is used if one of
309+ * the following conditions is met:
310+ * 1. during VM early startup before method handle support is fully initialized
311+ * 2. -Djdk.reflect.useNativeAccessorOnly=true is set
312+ * 3. a signature polymorphic method
313+ * 4. the member takes a variable number of arguments and the last parameter
314+ * is not an array (see details below)
315+ * 5. the member's method type has an arity >= 255
316+ *
317+ * Conditions 3-5 are due to the restrictions of method handles.
318+ * Otherwise, direct invocation of method handles is used.
306319 */
307320 private static boolean useNativeAccessor (Executable member ) {
308321 if (!VM .isJavaLangInvokeInited ())
309322 return true ;
310323
311- if (Modifier . isNative ( member . getModifiers ()))
324+ if (ReflectionFactory . useNativeAccessorOnly ()) // for testing only
312325 return true ;
313326
314- if (ReflectionFactory .useNativeAccessorOnly ()) // for testing only
327+ // java.lang.invoke cannot find the underlying native stubs of signature
328+ // polymorphic methods that core reflection must invoke.
329+ // Fall back to use the native implementation instead.
330+ if (member instanceof Method method && isSignaturePolymorphicMethod (method ))
315331 return true ;
316332
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.
333+ // For members with ACC_VARARGS bit set, MethodHandles produced by lookup
334+ // always have variable arity set and hence the last parameter of the member
335+ // must be an array type. Such restriction does not exist in core reflection
336+ // and the JVM, which always use fixed-arity invocations. Fall back to use
337+ // the native implementation instead.
324338 int paramCount = member .getParameterCount ();
325339 if (member .isVarArgs () &&
326- (paramCount == 0 || !(member . getParameterTypes ( )[paramCount -1 ].isArray ()))) {
340+ (paramCount == 0 || !(reflectionFactory . getExecutableSharedParameterTypes ( member )[paramCount -1 ].isArray ()))) {
327341 return true ;
328342 }
343+
329344 // A method handle cannot be created if its type has an arity >= 255
330345 // as the method handle's invoke method consumes an extra argument
331346 // of the method handle itself. Fall back to use the native implementation.
@@ -345,7 +360,7 @@ private static boolean useNativeAccessor(Executable member) {
345360 */
346361 private static int slotCount (Executable member ) {
347362 int slots = 0 ;
348- Class <?>[] ptypes = member . getParameterTypes ( );
363+ Class <?>[] ptypes = reflectionFactory . getExecutableSharedParameterTypes ( member );
349364 for (Class <?> ptype : ptypes ) {
350365 if (ptype == double .class || ptype == long .class ) {
351366 slots ++;
@@ -355,6 +370,31 @@ private static int slotCount(Executable member) {
355370 (Modifier .isStatic (member .getModifiers ()) ? 0 : 1 );
356371 }
357372
373+ /**
374+ * Signature-polymorphic methods. Lookup has special rules for these methods,
375+ * but core reflection must observe them as they are declared, and reflective
376+ * invocation must invoke the native method stubs that throw UOE.
377+ *
378+ * @param method the method to check
379+ * @return {@code true} if this method is signature polymorphic
380+ * @jls 15.12.3 Compile-Time Step 3: Is the Chosen Method Appropriate?
381+ * @jvms 2.9.3 Signature Polymorphic Methods
382+ */
383+ public static boolean isSignaturePolymorphicMethod (Method method ) {
384+ // ACC_NATIVE and ACC_VARARGS
385+ if (!method .isVarArgs () || !Modifier .isNative (method .getModifiers ())) {
386+ return false ;
387+ }
388+ // Declared in MethodHandle or VarHandle
389+ var declaringClass = method .getDeclaringClass ();
390+ if (declaringClass != MethodHandle .class && declaringClass != VarHandle .class ) {
391+ return false ;
392+ }
393+ // Single parameter of declared type Object[]
394+ Class <?>[] parameters = reflectionFactory .getExecutableSharedParameterTypes (method );
395+ return parameters .length == 1 && parameters [0 ] == Object [].class ;
396+ }
397+
358398 /*
359399 * Delay initializing these static fields until java.lang.invoke is fully initialized.
360400 */
@@ -363,4 +403,5 @@ static class LazyStaticHolder {
363403 }
364404
365405 private static final Unsafe UNSAFE = Unsafe .getUnsafe ();
406+ private static final ReflectionFactory reflectionFactory = ReflectionFactory .getReflectionFactory ();
366407}
0 commit comments