test: Add JNI test to check for <clinit> method lookup

Bug: 65059445
Change-Id: I03cd36466cf1edd5c64607542185e2cfd8b47ba5
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
index c8440fc..e563d73 100755
--- a/test/004-JniTest/build
+++ b/test/004-JniTest/build
@@ -38,4 +38,30 @@
 export -f javac_wrapper
 export JAVAC=javac_wrapper
 
+######################################################################
+
+# Use the original dx with no extra magic or pessimizing flags.
+# This ensures that any default optimizations that dx do would not break JNI.
+
+export ORIGINAL_DX="$DX"
+
+# Filter out --debug flag from dx.
+function dx_wrapper {
+  local args=("$@")
+  local args_filtered=()
+  for i in "${args[@]}"; do
+    case "$i" in
+      --debug)
+        ;;
+      *)
+        args_filtered+=("$i")
+        ;;
+    esac
+  done
+  "$ORIGINAL_DX" "${args_filtered[@]}"
+}
+
+export -f dx_wrapper
+export DX=dx_wrapper
+
 ./default-build "$@"
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index f7e404d..7e85ab1 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -58,3 +58,5 @@
 hi-lambda: λ
 hi-default δλ
 hi-default δλ
+Clinit Lookup: ClassWithoutClinit: <NSME Exception>
+Clinit Lookup: ClassWithClinit: Main$ClassWithClinit()(Class: class java.lang.reflect.Constructor)
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index a34d633..bc5a0a6 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -776,6 +776,18 @@
   return a + b + c;
 }
 
+extern "C" JNIEXPORT jobject JNICALL Java_Main_lookupClinit(JNIEnv* env, jclass, jclass kls) {
+  jmethodID clinit_id = env->GetStaticMethodID(kls, "<clinit>", "()V");
+
+  if (clinit_id != nullptr) {
+    jobject obj = env->ToReflectedMethod(kls, clinit_id, /*isStatic*/ true);
+    CHECK(obj != nullptr);
+    return obj;
+  } else {
+    return nullptr;
+  }
+}
+
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isSlowDebug(JNIEnv*, jclass) {
   // Return whether slow-debug is on. Only relevant for debug builds.
   if (kIsDebugBuild) {
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 0c4ed89..fe5f4e3 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -56,6 +56,8 @@
         registerNativesJniTest();
         testFastNativeMethods();
         testCriticalNativeMethods();
+
+        testClinitMethodLookup();
     }
 
     private static native boolean registerNativesJniTest();
@@ -314,6 +316,36 @@
     }
 
     private static native boolean isSlowDebug();
+
+    private static void testClinitMethodLookup() {
+      // Expect this to print <NSME Exception>
+      try {
+        System.out.println("Clinit Lookup: ClassWithoutClinit: " + methodString(lookupClinit(ClassWithoutClinit.class)));
+      } catch (NoSuchMethodError e) {
+        System.out.println("Clinit Lookup: ClassWithoutClinit: <NSME Exception>");
+      }
+      // Expect this to print <clinit>
+      try {
+        System.out.println("Clinit Lookup: ClassWithClinit: " + methodString(lookupClinit(ClassWithClinit.class)));
+      } catch (NoSuchMethodError e) {
+        System.out.println("Clinit Lookup: ClassWithClinit: <NSME Exception>");
+      }
+   }
+
+    private static String methodString(java.lang.reflect.Executable method) {
+      if (method == null) {
+        return "<<null>>";
+      } else {
+        return method.toString() + "(Class: " + method.getClass().toString() + ")";
+      }
+    }
+    private static native java.lang.reflect.Executable lookupClinit(Class kls);
+
+    private static class ClassWithoutClinit {
+    }
+    private static class ClassWithClinit {
+      static {}
+    }
 }
 
 @FunctionalInterface