ART: Add IsInterface and IsArrayClass support

Add support for these two required calls. Add a test.

Bug: 31684578
Test: m test-art-host-run-test-912-classes
Change-Id: I2f9e5c62dd4c3d7f29aaf3dd08f1297aa3b2fd8b
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5f97b60..f46c25c 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -585,13 +585,13 @@
   }
 
   static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::IsInterface(env, klass, is_interface_ptr);
   }
 
   static jvmtiError IsArrayClass(jvmtiEnv* env,
                                  jclass klass,
                                  jboolean* is_array_class_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr);
   }
 
   static jvmtiError IsModifiableClass(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index de2076a..fec8447 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -70,4 +70,38 @@
   return ERR(NONE);
 }
 
+template <typename T>
+static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_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 (is_t_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  *is_t_ptr = test(klass) ? JNI_TRUE : JNI_FALSE;
+  return ERR(NONE);
+}
+
+jvmtiError ClassUtil::IsInterface(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                  jclass jklass,
+                                  jboolean* is_interface_ptr) {
+  auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return klass->IsInterface();
+  };
+  return ClassIsT(jklass, test, is_interface_ptr);
+}
+
+jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                   jclass jklass,
+                                   jboolean* is_array_class_ptr) {
+  auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return klass->IsArrayClass();
+  };
+  return ClassIsT(jklass, test, is_array_class_ptr);
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index caa77d4..63c9a87 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -43,6 +43,9 @@
                                       jclass klass,
                                       char** signature_ptr,
                                       char** generic_ptr);
+
+  static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
+  static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
 };
 
 }  // namespace openjdkjvmti
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 838a92a..5a265b8 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -61,6 +61,32 @@
   return ret;
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jboolean is_interface = JNI_FALSE;
+  jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsInterface: %s\n", err);
+    return JNI_FALSE;
+  }
+  return is_interface;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jboolean is_array_class = JNI_FALSE;
+  jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsArrayClass: %s\n", err);
+    return JNI_FALSE;
+  }
+  return is_array_class;
+}
+
 // 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 71b22f4..909d47d 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -5,3 +5,10 @@
 [L$Proxy0;, null]
 [I, null]
 [[D, null]
+int interface=false array=false
+$Proxy0 interface=false array=false
+java.lang.Runnable interface=true array=false
+java.lang.String interface=false array=false
+[I interface=false array=true
+[Ljava.lang.Runnable; interface=false array=true
+[Ljava.lang.String; interface=false array=true
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 025584e..a2dc7e1 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -34,6 +34,15 @@
 
     testClass(int.class);
     testClass(double[].class);
+
+    testClassType(int.class);
+    testClassType(getProxyClass());
+    testClassType(Runnable.class);
+    testClassType(String.class);
+
+    testClassType(int[].class);
+    testClassType(Runnable[].class);
+    testClassType(String[].class);
   }
 
   private static Class<?> proxyClass = null;
@@ -57,5 +66,14 @@
     System.out.println(Arrays.toString(result));
   }
 
+  private static void testClassType(Class<?> c) throws Exception {
+    boolean isInterface = isInterface(c);
+    boolean isArray = isArrayClass(c);
+    System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray);
+  }
+
   private static native String[] getClassSignature(Class<?> c);
+
+  private static native boolean isInterface(Class<?> c);
+  private static native boolean isArrayClass(Class<?> c);
 }