Skip to content

Commit 20193ba

Browse files
committed
backport 5958463cadb04560ec85d9af972255bfe6dcc2f2
1 parent 53fdc2d commit 20193ba

File tree

3 files changed

+135
-15
lines changed

3 files changed

+135
-15
lines changed

src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class DirectMethodHandleAccessor extends MethodAccessorImpl {
4646
* Creates a MethodAccessorImpl for a non-native method.
4747
*/
4848
static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) {
49-
assert !Modifier.isNative(method.getModifiers());
49+
assert !MethodHandleAccessorFactory.isSignaturePolymorphicMethod(method);
5050

5151
return new DirectMethodHandleAccessor(method, target, false);
5252
}

src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
@@ -28,6 +28,7 @@
2828
import java.lang.invoke.MethodHandle;
2929
import java.lang.invoke.MethodHandles;
3030
import java.lang.invoke.MethodType;
31+
import java.lang.invoke.VarHandle;
3132
import java.lang.reflect.Constructor;
3233
import java.lang.reflect.Executable;
3334
import 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
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2024, 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package org.openjdk.bench.java.lang.reflect;
24+
25+
import org.openjdk.jmh.annotations.Benchmark;
26+
import org.openjdk.jmh.annotations.BenchmarkMode;
27+
import org.openjdk.jmh.annotations.Fork;
28+
import org.openjdk.jmh.annotations.Measurement;
29+
import org.openjdk.jmh.annotations.Mode;
30+
import org.openjdk.jmh.annotations.OutputTimeUnit;
31+
import org.openjdk.jmh.annotations.Scope;
32+
import org.openjdk.jmh.annotations.Setup;
33+
import org.openjdk.jmh.annotations.State;
34+
import org.openjdk.jmh.annotations.Warmup;
35+
import org.openjdk.jmh.infra.Blackhole;
36+
37+
import java.lang.reflect.Method;
38+
import java.util.concurrent.TimeUnit;
39+
40+
/**
41+
* Benchmark for regression in native method invocation.
42+
*/
43+
@BenchmarkMode(Mode.AverageTime)
44+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
45+
@State(Scope.Thread)
46+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
47+
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
48+
@Fork(3)
49+
public class NativeMethodInvoke {
50+
51+
private Method objectHashCode;
52+
private Method threadCurrentThread;
53+
54+
private Object[] objects;
55+
56+
@Setup
57+
public void setup() throws ReflectiveOperationException {
58+
objects = new Object[]{
59+
1, 5L,
60+
5.6d, 23.11f,
61+
Boolean.TRUE, 'd'
62+
};
63+
64+
objectHashCode = Object.class.getDeclaredMethod("hashCode");
65+
threadCurrentThread = Thread.class.getDeclaredMethod("currentThread");
66+
}
67+
68+
@Benchmark
69+
public void objectHashCode(Blackhole bh) throws ReflectiveOperationException {
70+
for (var obj : objects) {
71+
bh.consume(objectHashCode.invoke(obj));
72+
}
73+
}
74+
75+
@Benchmark
76+
public Object threadCurrentThread() throws ReflectiveOperationException {
77+
return threadCurrentThread.invoke(null);
78+
}
79+
}

0 commit comments

Comments
 (0)