8357987: [JVMCI] Add support for retrieving all methods of a ResolvedJavaType

Reviewed-by: dnsimon, yzheng, never
This commit is contained in:
Tom Shull 2025-06-03 19:38:58 +00:00 committed by Doug Simon
parent a44a470052
commit e235b61a8b
11 changed files with 103 additions and 1 deletions

View File

@ -2224,6 +2224,26 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredMethods, (JNIEnv* env, jobject, ARGUME
return JVMCIENV->get_jobjectArray(methods);
C2V_END
C2V_VMENTRY_NULL(jobjectArray, getAllMethods, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass)))
Klass* klass = UNPACK_PAIR(Klass, klass);
if (klass == nullptr) {
JVMCI_THROW_NULL(NullPointerException);
}
if (!klass->is_instance_klass()) {
JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(0, JVMCI_CHECK_NULL);
return JVMCIENV->get_jobjectArray(methods);
}
InstanceKlass* iklass = InstanceKlass::cast(klass);
JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(iklass->methods()->length(), JVMCI_CHECK_NULL);
for (int i = 0; i < iklass->methods()->length(); i++) {
methodHandle mh(THREAD, iklass->methods()->at(i));
JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
JVMCIENV->put_object_at(methods, i, method);
}
return JVMCIENV->get_jobjectArray(methods);
C2V_END
C2V_VMENTRY_NULL(jobjectArray, getDeclaredFieldsInfo, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass)))
Klass* klass = UNPACK_PAIR(Klass, klass);
if (klass == nullptr) {
@ -3359,6 +3379,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "boxPrimitive", CC "(" OBJECT ")" OBJECTCONSTANT, FN_PTR(boxPrimitive)},
{CC "getDeclaredConstructors", CC "(" HS_KLASS2 ")[" RESOLVED_METHOD, FN_PTR(getDeclaredConstructors)},
{CC "getDeclaredMethods", CC "(" HS_KLASS2 ")[" RESOLVED_METHOD, FN_PTR(getDeclaredMethods)},
{CC "getAllMethods", CC "(" HS_KLASS2 ")[" RESOLVED_METHOD, FN_PTR(getAllMethods)},
{CC "getDeclaredFieldsInfo", CC "(" HS_KLASS2 ")[" FIELDINFO, FN_PTR(getDeclaredFieldsInfo)},
{CC "readStaticFieldValue", CC "(" HS_KLASS2 "JC)" JAVACONSTANT, FN_PTR(readStaticFieldValue)},
{CC "readFieldValue", CC "(" OBJECTCONSTANT HS_KLASS2 "JC)" JAVACONSTANT, FN_PTR(readFieldValue)},

View File

@ -693,6 +693,7 @@
declare_constant(ConstMethodFlags::_misc_reserved_stack_access) \
declare_constant(ConstMethodFlags::_misc_changes_current_thread) \
declare_constant(ConstMethodFlags::_misc_is_scoped) \
declare_constant(ConstMethodFlags::_misc_is_overpass) \
\
declare_constant(CounterData::count_off) \
\

View File

@ -1148,7 +1148,8 @@ final class CompilerToVM {
native ResolvedJavaMethod[] getDeclaredConstructors(HotSpotResolvedObjectTypeImpl klass, long klassPointer);
/**
* Gets the {@link ResolvedJavaMethod}s for all the non-constructor methods of {@code klass}.
* Gets the {@link ResolvedJavaMethod}s for all non-overpass and non-initializer
* methods of {@code klass}.
*/
ResolvedJavaMethod[] getDeclaredMethods(HotSpotResolvedObjectTypeImpl klass) {
return getDeclaredMethods(klass, klass.getKlassPointer());
@ -1156,6 +1157,15 @@ final class CompilerToVM {
native ResolvedJavaMethod[] getDeclaredMethods(HotSpotResolvedObjectTypeImpl klass, long klassPointer);
/**
* Gets the {@link ResolvedJavaMethod}s for all methods of {@code klass}.
*/
ResolvedJavaMethod[] getAllMethods(HotSpotResolvedObjectTypeImpl klass) {
return getAllMethods(klass, klass.getKlassPointer());
}
native ResolvedJavaMethod[] getAllMethods(HotSpotResolvedObjectTypeImpl klass, long klassPointer);
HotSpotResolvedObjectTypeImpl.FieldInfo[] getDeclaredFieldsInfo(HotSpotResolvedObjectTypeImpl klass) {
return getDeclaredFieldsInfo(klass, klass.getKlassPointer());
}

View File

@ -573,6 +573,19 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
}
/*
* Currently in hotspot a method can either be a "normal" or an "overpass"
* method. Overpass methods are instance methods which are created when
* otherwise a valid candidate for method resolution would not be found.
*/
@Override
public boolean isDeclared() {
if (isConstructor() || isClassInitializer()) {
return false;
}
return (getConstMethodFlags() & config().constMethodFlagsIsOverpass) == 0;
}
@Override
public Type[] getGenericParameterTypes() {
if (isClassInitializer()) {

View File

@ -1067,6 +1067,14 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem
return runtime().compilerToVm.getDeclaredMethods(this);
}
@Override
public List<ResolvedJavaMethod> getAllMethods(boolean forceLink) {
if (forceLink) {
link();
}
return List.of(runtime().compilerToVm.getAllMethods(this));
}
@Override
public ResolvedJavaMethod getClassInitializer() {
if (!isArray()) {

View File

@ -296,6 +296,11 @@ public final class HotSpotResolvedPrimitiveType extends HotSpotResolvedJavaType
return new ResolvedJavaMethod[0];
}
@Override
public List<ResolvedJavaMethod> getAllMethods(boolean forceLink) {
return List.of();
}
@Override
public ResolvedJavaMethod getClassInitializer() {
return null;

View File

@ -196,6 +196,7 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
final int constMethodFlagsCallerSensitive = getConstant("ConstMethodFlags::_misc_caller_sensitive", Integer.class);
final int constMethodFlagsIntrinsicCandidate = getConstant("ConstMethodFlags::_misc_intrinsic_candidate", Integer.class);
final int constMethodFlagsIsScoped = getConstant("ConstMethodFlags::_misc_is_scoped", Integer.class);
final int constMethodFlagsIsOverpass = getConstant("ConstMethodFlags::_misc_is_overpass", Integer.class);
final int constMethodHasLineNumberTable = getConstant("ConstMethodFlags::_misc_has_linenumber_table", Integer.class);
final int constMethodHasLocalVariableTable = getConstant("ConstMethodFlags::_misc_has_localvariable_table", Integer.class);
final int constMethodHasMethodAnnotations = getConstant("ConstMethodFlags::_misc_has_method_annotations", Integer.class);

View File

@ -114,6 +114,12 @@ public interface ResolvedJavaMethod extends JavaMethod, InvokeTarget, ModifiersP
*/
boolean isDefault();
/**
* Returns {@code true} if this method is contained in the array returned by
* {@code getDeclaringClass().getDeclaredMethods()}
*/
boolean isDeclared();
/**
* Checks whether this method is a class initializer.
*

View File

@ -23,6 +23,7 @@
package jdk.vm.ci.meta;
import java.lang.reflect.AnnotatedElement;
import java.util.List;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
@ -365,6 +366,17 @@ public interface ResolvedJavaType extends JavaType, ModifiersProvider, Annotated
throw new UnsupportedOperationException();
}
/**
* Returns a list containing all methods present within this type. This list can
* include methods implicitly created and used by the VM that are not present in
* {@link #getDeclaredMethods}. The returned List is unmodifiable; calls to any
* mutator method will always cause {@code UnsupportedOperationException} to be
* thrown.
*
* @param forceLink if {@code true}, forces this type to be {@link #link linked}
*/
List<ResolvedJavaMethod> getAllMethods(boolean forceLink);
/**
* Returns the {@code <clinit>} method for this class if there is one.
*/

View File

@ -425,6 +425,19 @@ public class TestResolvedJavaMethod extends MethodUniverse {
}
}
@Test
public void isDeclaredTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
boolean expectedDeclared = Arrays.stream(m.getDeclaringClass().getDeclaredMethods()).anyMatch(i -> i.equals(m));
assertEquals(expectedDeclared, m.isDeclared());
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertFalse(m.isDeclared());
}
}
@Test
public void hasReceiverTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {

View File

@ -1019,6 +1019,18 @@ public class TestResolvedJavaType extends TypeUniverse {
}
}
@Test
public void getAllMethodsTest() {
for (Class<?> c : classes) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
Set<ResolvedJavaMethod> allMethods = new HashSet<>(type.getAllMethods(true));
Stream<ResolvedJavaMethod> allKnownMethods = Stream.concat(Arrays.stream(type.getDeclaredMethods()), Arrays.stream(type.getDeclaredConstructors()));
allKnownMethods = Stream.concat(allKnownMethods, Stream.ofNullable(type.getClassInitializer()));
List<ResolvedJavaMethod> missingMethods = allKnownMethods.filter(m -> !allMethods.contains(m)).toList();
assertTrue(missingMethods.toString(), missingMethods.isEmpty());
}
}
static class A {
static String name = "foo";
}