ART: Add GetClassFields support
Add GetClassFields support. Add a test.
Bug: 31684578
Test: m test-art-host-run-test-912-classes
Change-Id: Id7509ef10ec9c19b54b7db8637729556b91273e5
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index ddfa96a..b8f1500 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -560,7 +560,7 @@
jclass klass,
jint* field_count_ptr,
jfieldID** fields_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr);
}
static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index fec8447..eea317a 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -32,11 +32,54 @@
#include "ti_class.h"
#include "art_jvmti.h"
+#include "jni_internal.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
namespace openjdkjvmti {
+jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
+ jclass jklass,
+ jint* field_count_ptr,
+ jfieldID** fields_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 (field_count_ptr == nullptr || fields_ptr == nullptr) {
+ 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();
+
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jfieldID), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jfieldID* field_array = reinterpret_cast<jfieldID*>(out_ptr);
+
+ size_t array_idx = 0;
+ for (art::ArtField& field : sfields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+ for (art::ArtField& field : ifields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+
+ *field_count_ptr = static_cast<jint>(array_size);
+ *fields_ptr = field_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 63c9a87..34edb16 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -39,6 +39,11 @@
class ClassUtil {
public:
+ static jvmtiError GetClassFields(jvmtiEnv* env,
+ jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_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 5a265b8..bad48a4 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -87,6 +87,30 @@
return is_array_class;
}
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jfieldID* fields = nullptr;
+ jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassFields: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ jint modifiers;
+ // Ignore any errors for simplicity.
+ jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedField(klass,
+ fields[i],
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ };
+ return CreateObjectArray(env, count, "java/lang/Object", callback);
+}
+
// Don't do anything
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 909d47d..bf591c7 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -12,3 +12,6 @@
[I interface=false array=true
[Ljava.lang.Runnable; interface=false array=true
[Ljava.lang.String; interface=false array=true
+[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]
+[]
+[]
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index a2dc7e1..a7f3b3f 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -43,6 +43,10 @@
testClassType(int[].class);
testClassType(Runnable[].class);
testClassType(String[].class);
+
+ testClassFields(Integer.class);
+ testClassFields(int.class);
+ testClassFields(String[].class);
}
private static Class<?> proxyClass = null;
@@ -72,8 +76,14 @@
System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray);
}
+ private static void testClassFields(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassFields(c)));
+ }
+
private static native String[] getClassSignature(Class<?> c);
private static native boolean isInterface(Class<?> c);
private static native boolean isArrayClass(Class<?> c);
+
+ private static native Object[] getClassFields(Class<?> c);
}