1
1
/*
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.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
28
28
import java .lang .invoke .MethodHandle ;
29
29
import java .lang .invoke .MethodHandles ;
30
30
import java .lang .invoke .MethodType ;
31
+ import java .lang .invoke .VarHandle ;
31
32
import java .lang .reflect .Constructor ;
32
33
import java .lang .reflect .Executable ;
33
34
import java .lang .reflect .Field ;
@@ -347,36 +348,34 @@ static void ensureClassInitialized(Class<?> defc) {
347
348
* Native accessor, i.e. VM reflection implementation, is used if one of
348
349
* the following conditions is met:
349
350
* 1. during VM early startup before method handle support is fully initialized
350
- * 2. a Java native method
351
- * 3. -Djdk.reflect.useNativeAccessorOnly=true is set
351
+ * 2. -Djdk.reflect.useNativeAccessorOnly=true is set
352
+ * 3. a signature polymorphic method
352
353
* 4. the member takes a variable number of arguments and the last parameter
353
354
* is not an array (see details below)
354
355
* 5. the member's method type has an arity >= 255
355
356
*
357
+ * Conditions 3-5 are due to the restrictions of method handles.
356
358
* Otherwise, direct invocation of method handles is used.
357
359
*/
358
360
private static boolean useNativeAccessor (Executable member ) {
359
361
if (!VM .isJavaLangInvokeInited ())
360
362
return true ;
361
363
362
- if (Modifier . isNative ( member . getModifiers ()))
364
+ if (ReflectionFactory . useNativeAccessorOnly ()) // for testing only
363
365
return true ;
364
366
365
- if (ReflectionFactory .useNativeAccessorOnly ()) // for testing only
367
+ // java.lang.invoke cannot find the underlying native stubs of signature
368
+ // polymorphic methods that core reflection must invoke.
369
+ // Fall back to use the native implementation instead.
370
+ if (member instanceof Method method && isSignaturePolymorphicMethod (method ))
366
371
return true ;
367
372
368
- // MethodHandle::withVarargs on a member with varargs modifier bit set
369
- // verifies that the last parameter of the member must be an array type.
370
- // The JVMS does not require the last parameter descriptor of the method descriptor
371
- // is an array type if the ACC_VARARGS flag is set in the access_flags item.
372
- // Hence the reflection implementation does not check the last parameter type
373
- // if ACC_VARARGS flag is set. Workaround this by invoking through
374
- // the native accessor.
375
- int paramCount = member .getParameterCount ();
376
- if (member .isVarArgs () &&
377
- (paramCount == 0 || !(member .getParameterTypes ()[paramCount -1 ].isArray ()))) {
373
+ // java.lang.invoke fails to create MH for bad ACC_VARARGS methods with no
374
+ // trailing array, but core reflection ignores ACC_VARARGS flag like the JVM does.
375
+ // Fall back to use the native implementation instead.
376
+ if (isInvalidVarArgs (member ))
378
377
return true ;
379
- }
378
+
380
379
// A method handle cannot be created if its type has an arity >= 255
381
380
// as the method handle's invoke method consumes an extra argument
382
381
// of the method handle itself. Fall back to use the native implementation.
@@ -406,6 +405,48 @@ private static int slotCount(Executable member) {
406
405
(Modifier .isStatic (member .getModifiers ()) ? 0 : 1 );
407
406
}
408
407
408
+ /**
409
+ * Signature-polymorphic methods. Lookup has special rules for these methods,
410
+ * but core reflection must observe them as they are declared, and reflective
411
+ * invocation must invoke the native method stubs that throw UOE.
412
+ *
413
+ * @param method the method to check
414
+ * @return {@code true} if this method is signature polymorphic
415
+ * @jls 15.12 Method Invocation Expressions
416
+ */
417
+ public static boolean isSignaturePolymorphicMethod (Method method ) {
418
+ // Native; has variable arity parameter
419
+ if (!method .isVarArgs () || !Modifier .isNative (method .getModifiers ())) {
420
+ return false ;
421
+ }
422
+ // Declared in MethodHandle or VarHandle
423
+ var declaringClass = method .getDeclaringClass ();
424
+ if (declaringClass != MethodHandle .class && declaringClass != VarHandle .class ) {
425
+ return false ;
426
+ }
427
+ // Single parameter of declared type Object[]
428
+ Class <?>[] parameters = reflectionFactory .getExecutableSharedParameterTypes (method );
429
+ return parameters .length == 1 && parameters [0 ] == Object [].class ;
430
+ }
431
+
432
+ /**
433
+ * Lookup always calls MethodHandle::setVarargs on a member with varargs modifier
434
+ * bit set, which verifies that the last parameter of the member must be an array type.
435
+ * Thus, Lookup cannot create MethodHandle for such methods or constructors.
436
+ * The JVMS does not require that the last parameter descriptor of the method descriptor
437
+ * is an array type if the ACC_VARARGS flag is set in the access_flags item.
438
+ * Core reflection also has no variable arity support and ignores the ACC_VARARGS flag,
439
+ * treating them as regular arguments.
440
+ */
441
+ private static boolean isInvalidVarArgs (Executable member ) {
442
+ if (!member .isVarArgs ())
443
+ return false ;
444
+
445
+ Class <?>[] parameters = reflectionFactory .getExecutableSharedParameterTypes (member );
446
+ var count = parameters .length ;
447
+ return count == 0 || !parameters [count - 1 ].isArray ();
448
+ }
449
+
409
450
/*
410
451
* Delay initializing these static fields until java.lang.invoke is fully initialized.
411
452
*/
@@ -414,4 +455,5 @@ static class LazyStaticHolder {
414
455
}
415
456
416
457
private static final Unsafe UNSAFE = Unsafe .getUnsafe ();
458
+ private static final ReflectionFactory reflectionFactory = ReflectionFactory .getReflectionFactory ();
417
459
}
0 commit comments