ART: Add GetClassStatus
Add support for GetClassStatus. Add a test.
Bug: 31684578
Test: m test-art-host-run-test-912-classes
Change-Id: Id8a3c3f4e4855a0c9bd87976a1cc0fad2db13f25
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index b8f1500..98c4c3a 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -538,7 +538,7 @@
}
static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassStatus(env, klass, status_ptr);
}
static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) {
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index eea317a..7b30a9d 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -113,6 +113,41 @@
return ERR(NONE);
}
+jvmtiError ClassUtil::GetClassStatus(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jint* status_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 (status_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ if (klass->IsArrayClass()) {
+ *status_ptr = JVMTI_CLASS_STATUS_ARRAY;
+ } else if (klass->IsPrimitive()) {
+ *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
+ } else {
+ *status_ptr = JVMTI_CLASS_STATUS_VERIFIED; // All loaded classes are structurally verified.
+ // This is finicky. If there's an error, we'll say it wasn't prepared.
+ if (klass->IsResolved()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_PREPARED;
+ }
+ if (klass->IsInitialized()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_INITIALIZED;
+ }
+ // Technically the class may be erroneous for other reasons, but we do not have enough info.
+ if (klass->IsErroneous()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_ERROR;
+ }
+ }
+
+ return ERR(NONE);
+}
+
template <typename T>
static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
art::ScopedObjectAccess soa(art::Thread::Current());
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 34edb16..5ee64be 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -49,6 +49,8 @@
char** signature_ptr,
char** generic_ptr);
+ static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr);
+
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
};
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index bad48a4..28c5931 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -111,6 +111,19 @@
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;
+ jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassStatus: %s\n", err);
+ return JNI_FALSE;
+ }
+ return status;
+}
+
// 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 bf591c7..44fed79 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -15,3 +15,8 @@
[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]
[]
[]
+int 100000
+class [Ljava.lang.String; 10000
+class java.lang.Object 111
+class Main$TestForNonInit 11
+class Main$TestForInitFail 1001
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index a7f3b3f..0b41113 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -47,6 +47,16 @@
testClassFields(Integer.class);
testClassFields(int.class);
testClassFields(String[].class);
+
+ testClassStatus(int.class);
+ testClassStatus(String[].class);
+ testClassStatus(Object.class);
+ testClassStatus(TestForNonInit.class);
+ try {
+ System.out.println(TestForInitFail.dummy);
+ } catch (ExceptionInInitializerError e) {
+ }
+ testClassStatus(TestForInitFail.class);
}
private static Class<?> proxyClass = null;
@@ -80,10 +90,24 @@
System.out.println(Arrays.toString(getClassFields(c)));
}
+ private static void testClassStatus(Class<?> c) {
+ System.out.println(c + " " + Integer.toBinaryString(getClassStatus(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);
+
+ private static native int getClassStatus(Class<?> c);
+
+ private static class TestForNonInit {
+ public static double dummy = Math.random(); // So it can't be compile-time initialized.
+ }
+
+ private static class TestForInitFail {
+ public static int dummy = ((int)Math.random())/0; // So it throws when initializing.
+ }
}