ART: Add GetClassMethods
Add GetClassMethods support. Add a test.
Bug: 31684578
Test: m test-art-host-run-test-912-classes
Change-Id: I7f063806671db5a5a69f7064e61f950b246f0b86
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 98c4c3a..5283919 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -553,7 +553,7 @@
jclass klass,
jint* method_count_ptr,
jmethodID** methods_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr);
}
static jvmtiError GetClassFields(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 7b30a9d..415e258 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -52,7 +52,6 @@
return ERR(NULL_POINTER);
}
- art::StackHandleScope<1> hs(soa.Self());
art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields();
art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
@@ -80,6 +79,49 @@
return ERR(NONE);
}
+jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env,
+ jclass jklass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (method_count_ptr == nullptr || methods_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ size_t array_size = klass->NumDeclaredVirtualMethods() + klass->NumDirectMethods();
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jmethodID), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jmethodID* method_array = reinterpret_cast<jmethodID*>(out_ptr);
+
+ if (art::kIsDebugBuild) {
+ size_t count = 0;
+ for (auto& m ATTRIBUTE_UNUSED : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ count++;
+ }
+ CHECK_EQ(count, klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods());
+ }
+
+ size_t array_idx = 0;
+ for (auto& m : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ method_array[array_idx] = art::jni::EncodeArtMethod(&m);
+ ++array_idx;
+ }
+
+ *method_count_ptr = static_cast<jint>(array_size);
+ *methods_ptr = method_array;
+
+ return ERR(NONE);
+}
+
+
jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
jclass jklass,
char** signature_ptr,
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 5ee64be..838c94c 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -44,6 +44,11 @@
jint* field_count_ptr,
jfieldID** fields_ptr);
+ static jvmtiError GetClassMethods(jvmtiEnv* env,
+ jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr);
+
static jvmtiError GetClassSignature(jvmtiEnv* env,
jclass klass,
char** signature_ptr,
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 28c5931..6771d71 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -111,6 +111,30 @@
return CreateObjectArray(env, count, "java/lang/Object", callback);
}
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jmethodID* methods = nullptr;
+ jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassMethods: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ jint modifiers;
+ // Ignore any errors for simplicity.
+ jvmti_env->GetMethodModifiers(methods[i], &modifiers);
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedMethod(klass,
+ methods[i],
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ };
+ return CreateObjectArray(env, count, "java/lang/Object", callback);
+}
+
extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus(
JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint status;
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 44fed79..9999903 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -15,6 +15,9 @@
[public static final int java.lang.Integer.BYTES, static final char[] java.lang.Integer.DigitOnes, static final char[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
[]
[]
+[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static int java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static void java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
+[]
+[]
int 100000
class [Ljava.lang.String; 10000
class java.lang.Object 111
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 0b41113..fd9e31a 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -48,6 +48,10 @@
testClassFields(int.class);
testClassFields(String[].class);
+ testClassMethods(Integer.class);
+ testClassMethods(int.class);
+ testClassMethods(String[].class);
+
testClassStatus(int.class);
testClassStatus(String[].class);
testClassStatus(Object.class);
@@ -90,6 +94,10 @@
System.out.println(Arrays.toString(getClassFields(c)));
}
+ private static void testClassMethods(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassMethods(c)));
+ }
+
private static void testClassStatus(Class<?> c) {
System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
}
@@ -100,6 +108,7 @@
private static native boolean isArrayClass(Class<?> c);
private static native Object[] getClassFields(Class<?> c);
+ private static native Object[] getClassMethods(Class<?> c);
private static native int getClassStatus(Class<?> c);