ART: Add class events

Add initial support for ClassLoad and ClassPrepare events. Add tests.

The initial implementation does not deal with the difference between
ClassLoad and ClassPrepare classes (the former may be temporary
classes).

Bug: 31684920
Test: m test-art-host-run-test-912-classes
Change-Id: I83535b9c871971f60b6b61f26651958fb32d502f
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 8c78d71..859f80c 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -82,6 +82,10 @@
     System.out.println();
 
     testClassVersion();
+
+    System.out.println();
+
+    testClassEvents();
   }
 
   private static Class<?> proxyClass = null;
@@ -208,6 +212,60 @@
     System.out.println(Arrays.toString(getClassVersion(Main.class)));
   }
 
+  private static void testClassEvents() throws Exception {
+    ClassLoader cl = Main.class.getClassLoader();
+    while (cl.getParent() != null) {
+      cl = cl.getParent();
+    }
+    final ClassLoader boot = cl;
+
+    enableClassLoadEvents(true);
+
+    ClassLoader cl1 = create(boot, DEX1, DEX2);
+    System.out.println("B, false");
+    Class.forName("B", false, cl1);
+
+    ClassLoader cl2 = create(boot, DEX1, DEX2);
+    System.out.println("B, true");
+    Class.forName("B", true, cl2);
+
+    ClassLoader cl3 = create(boot, DEX1, DEX2);
+    System.out.println("C, false");
+    Class.forName("C", false, cl3);
+    System.out.println("A, false");
+    Class.forName("A", false, cl3);
+
+    ClassLoader cl4 = create(boot, DEX1, DEX2);
+    System.out.println("C, true");
+    Class.forName("C", true, cl4);
+    System.out.println("A, true");
+    Class.forName("A", true, cl4);
+
+    ClassLoader cl5 = create(boot, DEX1, DEX2);
+    System.out.println("A, true");
+    Class.forName("A", true, cl5);
+    System.out.println("C, true");
+    Class.forName("C", true, cl5);
+
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          ClassLoader cl6 = create(boot, DEX1, DEX2);
+          System.out.println("C, true");
+          Class.forName("C", true, cl6);
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+    Thread t = new Thread(r, "TestRunner");
+    t.start();
+    t.join();
+
+    enableClassLoadEvents(false);
+  }
+
   private static void printClassLoaderClasses(ClassLoader cl) {
     for (;;) {
       if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
@@ -270,6 +328,8 @@
 
   private static native int[] getClassVersion(Class<?> c);
 
+  private static native void enableClassLoadEvents(boolean b);
+
   private static class TestForNonInit {
     public static double dummy = Math.random();  // So it can't be compile-time initialized.
   }