diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 2256a0edc99..4f6d4aaa099 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -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)}, diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 4302c0ce6ad..e26c815946d 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -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) \ \ diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index d7356659296..045b1f0fc12 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -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()); } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java index 59f5320cc54..9440a719dd4 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java @@ -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()) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java index 8d3b9f48b4c..17eede6f490 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java @@ -1067,6 +1067,14 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem return runtime().compilerToVm.getDeclaredMethods(this); } + @Override + public List getAllMethods(boolean forceLink) { + if (forceLink) { + link(); + } + return List.of(runtime().compilerToVm.getAllMethods(this)); + } + @Override public ResolvedJavaMethod getClassInitializer() { if (!isArray()) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java index 4425ade0880..b707906af08 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java @@ -296,6 +296,11 @@ public final class HotSpotResolvedPrimitiveType extends HotSpotResolvedJavaType return new ResolvedJavaMethod[0]; } + @Override + public List getAllMethods(boolean forceLink) { + return List.of(); + } + @Override public ResolvedJavaMethod getClassInitializer() { return null; diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java index e93b98042b6..bc2e121fe90 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -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); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java index 8758dbb3bb6..f401bc30f83 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java @@ -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. * diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java index b5d29733a69..929a4713330 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java @@ -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 getAllMethods(boolean forceLink); + /** * Returns the {@code } method for this class if there is one. */ diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java index 75f074ad09d..6151ae68ce7 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java @@ -425,6 +425,19 @@ public class TestResolvedJavaMethod extends MethodUniverse { } } + @Test + public void isDeclaredTest() { + for (Map.Entry 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, ResolvedJavaMethod> e : constructors.entrySet()) { + ResolvedJavaMethod m = e.getValue(); + assertFalse(m.isDeclared()); + } + } + @Test public void hasReceiverTest() { for (Map.Entry e : methods.entrySet()) { diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java index 456c4a5fd88..ee48724b71d 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java @@ -1019,6 +1019,18 @@ public class TestResolvedJavaType extends TypeUniverse { } } + @Test + public void getAllMethodsTest() { + for (Class c : classes) { + ResolvedJavaType type = metaAccess.lookupJavaType(c); + Set allMethods = new HashSet<>(type.getAllMethods(true)); + Stream allKnownMethods = Stream.concat(Arrays.stream(type.getDeclaredMethods()), Arrays.stream(type.getDeclaredConstructors())); + allKnownMethods = Stream.concat(allKnownMethods, Stream.ofNullable(type.getClassInitializer())); + List missingMethods = allKnownMethods.filter(m -> !allMethods.contains(m)).toList(); + assertTrue(missingMethods.toString(), missingMethods.isEmpty()); + } + } + static class A { static String name = "foo"; }