ART: Add GetImplementedInterfaces

Add support for GetImplementedInterfaces. Add a test. Add cleanup
to some existing tests.

Bug: 31684578
Test: m test-art-host-run-test-912-classes
Change-Id: Ic75bf3bba3b568232178d8525501122826b5a430
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 75c314f..d17376b 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -567,7 +567,7 @@
                                              jclass klass,
                                              jint* interface_count_ptr,
                                              jclass** interfaces_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr);
   }
 
   static jvmtiError GetClassVersionNumbers(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 76d070c..ce7e5ce 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -121,6 +121,56 @@
   return ERR(NONE);
 }
 
+jvmtiError ClassUtil::GetImplementedInterfaces(jvmtiEnv* env,
+                                               jclass jklass,
+                                               jint* interface_count_ptr,
+                                               jclass** interfaces_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 (interface_count_ptr == nullptr || interfaces_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  // Need to handle array specifically. Arrays implement Serializable and Cloneable, but the
+  // spec says these should not be reported.
+  if (klass->IsArrayClass()) {
+    *interface_count_ptr = 0;
+    *interfaces_ptr = nullptr;  // TODO: Should we allocate a dummy here?
+    return ERR(NONE);
+  }
+
+  size_t array_size = klass->NumDirectInterfaces();
+  unsigned char* out_ptr;
+  jvmtiError allocError = env->Allocate(array_size * sizeof(jclass), &out_ptr);
+  if (allocError != ERR(NONE)) {
+    return allocError;
+  }
+  jclass* interface_array = reinterpret_cast<jclass*>(out_ptr);
+
+  art::StackHandleScope<1> hs(soa.Self());
+  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
+
+  for (uint32_t idx = 0; idx != array_size; ++idx) {
+    art::ObjPtr<art::mirror::Class> inf_klass =
+        art::mirror::Class::ResolveDirectInterface(soa.Self(), h_klass, idx);
+    if (inf_klass == nullptr) {
+      soa.Self()->ClearException();
+      env->Deallocate(out_ptr);
+      // TODO: What is the right error code here?
+      return ERR(INTERNAL);
+    }
+    interface_array[idx] = soa.AddLocalReference<jclass>(inf_klass);
+  }
+
+  *interface_count_ptr = static_cast<jint>(array_size);
+  *interfaces_ptr = interface_array;
+
+  return ERR(NONE);
+}
 
 jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
                                          jclass jklass,
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 619f50e..722c208 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -49,6 +49,11 @@
                                     jint* method_count_ptr,
                                     jmethodID** methods_ptr);
 
+  static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
+                                             jclass klass,
+                                             jint* interface_count_ptr,
+                                             jclass** interfaces_ptr);
+
   static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr);
 
   static jvmtiError GetClassSignature(jvmtiEnv* env,
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 3383d04..5674e7b 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -121,7 +121,11 @@
                                  fields[i],
                                  (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
   };
-  return CreateObjectArray(env, count, "java/lang/Object", callback);
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+  if (fields != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+  }
+  return ret;
 }
 
 extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
@@ -145,7 +149,33 @@
                                   methods[i],
                                   (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
   };
-  return CreateObjectArray(env, count, "java/lang/Object", callback);
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+  if (methods != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods));
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint count = 0;
+  jclass* classes = nullptr;
+  jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetImplementedInterfaces: %s\n", err);
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) {
+    return classes[i];
+  };
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
+  if (classes != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
+  }
+  return ret;
 }
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus(
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index de23b7b..9f5254c 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -30,3 +30,12 @@
 class java.lang.Object 111
 class Main$TestForNonInit 11
 class Main$TestForInitFail 1001
+int []
+class [Ljava.lang.String; []
+class java.lang.Object []
+interface Main$InfA []
+interface Main$InfB [interface Main$InfA]
+interface Main$InfC [interface Main$InfB]
+class Main$ClassA [interface Main$InfA]
+class Main$ClassB [interface Main$InfB]
+class Main$ClassC [interface Main$InfA, interface Main$InfC]
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index cbcfe71..7a7f4c0 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -61,6 +61,16 @@
     } catch (ExceptionInInitializerError e) {
     }
     testClassStatus(TestForInitFail.class);
+
+    testInterfaces(int.class);
+    testInterfaces(String[].class);
+    testInterfaces(Object.class);
+    testInterfaces(InfA.class);
+    testInterfaces(InfB.class);
+    testInterfaces(InfC.class);
+    testInterfaces(ClassA.class);
+    testInterfaces(ClassB.class);
+    testInterfaces(ClassC.class);
   }
 
   private static Class<?> proxyClass = null;
@@ -107,6 +117,10 @@
     System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
   }
 
+  private static void testInterfaces(Class<?> c) {
+    System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
+  }
+
   private static native String[] getClassSignature(Class<?> c);
 
   private static native boolean isInterface(Class<?> c);
@@ -116,6 +130,7 @@
 
   private static native Object[] getClassFields(Class<?> c);
   private static native Object[] getClassMethods(Class<?> c);
+  private static native Class[] getImplementedInterfaces(Class<?> c);
 
   private static native int getClassStatus(Class<?> c);
 
@@ -126,4 +141,18 @@
   private static class TestForInitFail {
     public static int dummy = ((int)Math.random())/0;  // So it throws when initializing.
   }
+
+  public static interface InfA {
+  }
+  public static interface InfB extends InfA {
+  }
+  public static interface InfC extends InfB {
+  }
+
+  public abstract static class ClassA implements InfA {
+  }
+  public abstract static class ClassB extends ClassA implements InfB {
+  }
+  public abstract static class ClassC implements InfA, InfC {
+  }
 }