Two exception-related tweaks: (1) Make the dynamic exception construction
mechanism be able to handle exception constructors that take Object instead
of String. (2) Add a convenience in JNIHelp to throw RuntimeExceptions.

Change-Id: Ie5ce680c30043a4b186e59d7c8883666648b2c87
diff --git a/libnativehelper/JNIHelp.c b/libnativehelper/JNIHelp.c
index aacecb6..748d8ff 100644
--- a/libnativehelper/JNIHelp.c
+++ b/libnativehelper/JNIHelp.c
@@ -55,7 +55,15 @@
 }
 
 /*
- * Throw a java.IO.IOException, generating the message from errno.
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(JNIEnv* env, const char* msg)
+{
+    return jniThrowException(env, "java/lang/RuntimeException", msg);
+}
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
  */
 int jniThrowIOException(JNIEnv* env, int errnum)
 {
@@ -85,4 +93,3 @@
 
     return jniThrowException(env, "java/io/IOException", message);
 }
-
diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h
index 3982797..698cba7 100644
--- a/libnativehelper/include/nativehelper/JNIHelp.h
+++ b/libnativehelper/include/nativehelper/JNIHelp.h
@@ -53,7 +53,12 @@
 int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
 
 /*
- * Throw a java.IO.IOException, generating the message from errno.
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
  */
 int jniThrowIOException(C_JNIEnv* env, int errnum);
 
diff --git a/vm/Exception.c b/vm/Exception.c
index 3a56cc3..808b0b2 100644
--- a/vm/Exception.c
+++ b/vm/Exception.c
@@ -338,6 +338,45 @@
 }
 
 /*
+ * Find and return an exception constructor method that can take the
+ * indicated parameters, or return NULL if no such constructor exists.
+ */
+static Method* findExceptionInitMethod(ClassObject* excepClass,
+    bool hasMessage, bool hasCause)
+{
+    if (hasMessage) {
+        Method* result;
+
+        if (hasCause) {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+        } else {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/String;)V");
+        }
+
+        if (result != NULL) {
+            return result;
+        }
+
+        if (hasCause) {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
+        } else {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/Object;)V");
+        }
+    } else if (hasCause) {
+        return dvmFindDirectMethodByDescriptor(
+                excepClass, "<init>", "(Ljava/lang/Throwable;)V");
+    } else {
+        return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+    }
+}
+
+/*
  * Initialize an exception with an appropriate constructor.
  *
  * "exception" is the exception object to initialize.
@@ -418,43 +457,45 @@
      * not #2.  (Some might argue that the constructor is actually not #3,
      * because it doesn't take the message string as an argument, but it
      * has the same effect and we can work with it here.)
+     *
+     * java.lang.AssertionError is also a strange case -- it has a
+     * constructor that takes an Object, but not one that takes a String.
+     * There may be other cases like this, as well, so we generally look
+     * for an Object-taking constructor if we can't find one that takes
+     * a String.
      */
     if (cause == NULL) {
         if (msgStr == NULL) {
-            initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+            initMethod = findExceptionInitMethod(excepClass, false, false);
             initKind = kInitNoarg;
         } else {
-            initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>",
-                            "(Ljava/lang/String;)V");
+            initMethod = findExceptionInitMethod(excepClass, true, false);
             if (initMethod != NULL) {
                 initKind = kInitMsg;
             } else {
                 /* no #2, try #3 */
-                initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>",
-                                "(Ljava/lang/String;Ljava/lang/Throwable;)V");
-                if (initMethod != NULL)
+                initMethod = findExceptionInitMethod(excepClass, true, true);
+                if (initMethod != NULL) {
                     initKind = kInitMsgThrow;
+                }
             }
         }
     } else {
         if (msgStr == NULL) {
-            initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>",
-                            "(Ljava/lang/Throwable;)V");
+            initMethod = findExceptionInitMethod(excepClass, false, true);
             if (initMethod != NULL) {
                 initKind = kInitThrow;
             } else {
-                initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+                initMethod = findExceptionInitMethod(excepClass, false, false);
                 initKind = kInitNoarg;
                 needInitCause = true;
             }
         } else {
-            initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>",
-                            "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+            initMethod = findExceptionInitMethod(excepClass, true, true);
             if (initMethod != NULL) {
                 initKind = kInitMsgThrow;
             } else {
-                initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>",
-                                "(Ljava/lang/String;)V");
+                initMethod = findExceptionInitMethod(excepClass, true, false);
                 initKind = kInitMsg;
                 needInitCause = true;
             }