ART: Balanced locking

Change the verifier to check for balanced locking. When balanced
locking can't be guaranteed, use a new failure kind to punt to
the interpreter.

Add smali tests, with JNI code to check the balanced-locking result.

Bug: 23502994
Change-Id: Icd7db0be20ef2f69f0ac784de43dcba990035cd8
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index b60c71e..af1eaea 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 
 /*
  * Entry point and tests that are expected to succeed.
@@ -38,11 +41,6 @@
         System.out.println("constantLock ok");
 
         m.notExcessiveNesting();
-        try {
-            TooDeep.excessiveNesting();
-            System.err.println("excessiveNesting did not throw");
-        } catch (VerifyError ve) {}
-        System.out.println("excessiveNesting ok");
 
         m.notNested();
         System.out.println("notNested ok");
@@ -55,6 +53,9 @@
 
         m.triplet(obj1, obj2, 0);
         System.out.println("triplet ok");
+
+        System.loadLibrary("arttest");
+        runSmaliTests();
     }
 
     /**
@@ -216,4 +217,62 @@
 
         doNothing(localObj);
     }
+
+    // Smali testing code.
+    private static void runSmaliTests() {
+        runTest("OK", new Object[] { new Object(), new Object() }, null);
+        runTest("TooDeep", new Object[] { new Object() }, null);
+        runTest("NotStructuredOverUnlock", new Object[] { new Object() },
+                IllegalMonitorStateException.class);
+        runTest("NotStructuredUnderUnlock", new Object[] { new Object() }, null);
+                // TODO: new IllegalMonitorStateException());
+        runTest("UnbalancedJoin", new Object[] { new Object(), new Object() }, null);
+        runTest("UnbalancedStraight", new Object[] { new Object(), new Object() }, null);
+    }
+
+    private static void runTest(String className, Object[] parameters, Class<?> excType) {
+        System.out.println(className);
+        try {
+            Class<?> c = Class.forName(className);
+
+            Method[] methods = c.getDeclaredMethods();
+
+            // For simplicity we assume that test methods are not overloaded. So searching by name
+            // will give us the method we need to run.
+            Method method = null;
+            for (Method m : methods) {
+                if (m.getName().equals("run")) {
+                    method = m;
+                    break;
+                }
+            }
+
+            if (method == null) {
+                System.out.println("Could not find test method for " + className);
+            } else if (!Modifier.isStatic(method.getModifiers())) {
+                System.out.println("Test method for " + className + " is not static.");
+            } else {
+                method.invoke(null, parameters);
+                if (excType != null) {
+                    System.out.println("Expected an exception in " + className);
+                }
+            }
+        } catch (Throwable exc) {
+            if (excType == null) {
+                System.out.println("Did not expect exception " + exc + " for " + className);
+                exc.printStackTrace(System.out);
+            } else if (exc instanceof InvocationTargetException && exc.getCause() != null &&
+                       exc.getCause().getClass().equals(excType)) {
+                // Expected exception is wrapped in InvocationTargetException.
+            } else if (!excType.equals(exc.getClass())) {
+                System.out.println("Expected " + excType.getName() + ", but got " + exc.getClass());
+            } else {
+              // Expected exception, do nothing.
+            }
+        }
+    }
+
+    // Helpers for the smali code.
+    public static native void assertCallerIsInterpreted();
+    public static native void assertCallerIsManaged();
 }