Add a bit of structure to the Exception startup code.

This is in preparation for having a *lot* more exception classes get
looked up at startup time.

Change-Id: Id464c5b19a6a15f6779d8959f5d6397a0c6c5842
diff --git a/vm/Exception.c b/vm/Exception.c
index 2e93242..f00b6fc 100644
--- a/vm/Exception.c
+++ b/vm/Exception.c
@@ -101,6 +101,30 @@
 
 
 /*
+ * Helper for dvmExceptionStartup(), which looks up classes and stores
+ * them to the indicated pointer, returning a failure code (false ==
+ * failure).
+ */
+static bool initRef(ClassObject** pClass, const char* name)
+{
+    ClassObject* result;
+
+    if (name[0] == '[') {
+        result = dvmFindArrayClass(name, NULL);
+    } else {
+        result = dvmFindSystemClassNoInit(name);
+    }
+
+    if (result == NULL) {
+        LOGE("Could not find exception class %s\n", name);
+        return false;
+    }
+
+    *pClass = result;
+    return true;
+}
+
+/*
  * Cache pointers to some of the exception classes we use locally.
  *
  * Note this is NOT called during dexopt optimization.  Some of the fields
@@ -108,23 +132,21 @@
  */
 bool dvmExceptionStartup(void)
 {
-    gDvm.classJavaLangThrowable =
-        dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
-    gDvm.classJavaLangRuntimeException =
-        dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;");
-    gDvm.classJavaLangStackOverflowError =
-        dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;");
-    gDvm.classJavaLangError =
-        dvmFindSystemClassNoInit("Ljava/lang/Error;");
-    gDvm.classJavaLangStackTraceElement =
-        dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;");
-    gDvm.classJavaLangStackTraceElementArray =
-        dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL);
-    if (gDvm.classJavaLangThrowable == NULL ||
-        gDvm.classJavaLangStackTraceElement == NULL ||
-        gDvm.classJavaLangStackTraceElementArray == NULL)
-    {
-        LOGE("Could not find one or more essential exception classes\n");
+    bool ok = true;
+
+    ok &= initRef(&gDvm.exError, "Ljava/lang/Error;");
+    ok &= initRef(&gDvm.exExceptionInInitializerError,
+            "Ljava/lang/ExceptionInInitializerError;");
+    ok &= initRef(&gDvm.exRuntimeException, "Ljava/lang/RuntimeException;");
+    ok &= initRef(&gDvm.exStackOverflowError,
+            "Ljava/lang/StackOverflowError;");
+    ok &= initRef(&gDvm.exThrowable, "Ljava/lang/Throwable;");
+    ok &= initRef(&gDvm.classJavaLangStackTraceElement,
+            "Ljava/lang/StackTraceElement;");
+    ok &= initRef(&gDvm.classJavaLangStackTraceElementArray,
+            "[Ljava/lang/StackTraceElement;");
+
+    if (!ok) {
         return false;
     }
 
@@ -145,7 +167,7 @@
 
     /* grab an offset for the stackData field */
     gDvm.offJavaLangThrowable_stackState =
-        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+        dvmFindFieldOffset(gDvm.exThrowable,
             "stackState", "Ljava/lang/Object;");
     if (gDvm.offJavaLangThrowable_stackState < 0) {
         LOGE("Unable to find Throwable.stackState\n");
@@ -154,13 +176,26 @@
 
     /* and one for the cause field, just 'cause */
     gDvm.offJavaLangThrowable_cause =
-        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+        dvmFindFieldOffset(gDvm.exThrowable,
             "cause", "Ljava/lang/Throwable;");
     if (gDvm.offJavaLangThrowable_cause < 0) {
         LOGE("Unable to find Throwable.cause\n");
         return false;
     }
 
+    /*
+     * ExceptionInInitializerError is used in the guts of Class.c; it
+     * wants to call the constructor more directly, so look that up
+     * explicitly, here.
+     */
+    gDvm.methJavaLangExceptionInInitializerError_init =
+        dvmFindDirectMethodByDescriptor(gDvm.exExceptionInInitializerError,
+            "<init>", "(Ljava/lang/Throwable;)V");
+    if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) {
+        LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+        return false;
+    }
+
     return true;
 }
 
@@ -425,7 +460,7 @@
     }
 
     if (cause != NULL) {
-        if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
+        if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) {
             LOGE("Tried to init exception with cause '%s'\n",
                 cause->clazz->descriptor);
             dvmAbort();
@@ -519,7 +554,7 @@
             excepClass->descriptor, msg, initKind);
         assert(strcmp(excepClass->descriptor,
                       "Ljava/lang/RuntimeException;") != 0);
-        dvmThrowChainedException("Ljava/lang/RuntimeException;",
+        dvmThrowChainedExceptionByClass(gDvm.exRuntimeException,
             "re-throw on exception class missing constructor", NULL);
         goto bail;
     }
@@ -620,8 +655,8 @@
  */
 bool dvmIsCheckedException(const Object* exception)
 {
-    if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) ||
-        dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException))
+    if (dvmInstanceof(exception->clazz, gDvm.exError) ||
+        dvmInstanceof(exception->clazz, gDvm.exRuntimeException))
     {
         return false;
     } else {
@@ -690,7 +725,7 @@
  */
 Object* dvmGetExceptionCause(const Object* exception)
 {
-    if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
+    if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) {
         LOGE("Tried to get cause from object of type '%s'\n",
             exception->clazz->descriptor);
         dvmAbort();
@@ -1003,7 +1038,7 @@
 
         if (dvmIsBreakFrame((u4*)fp))
             break;
-        if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
+        if (!dvmInstanceof(method->clazz, gDvm.exThrowable))
             break;
         //LOGD("EXCEP: ignoring %s.%s\n",
         //         method->clazz->descriptor, method->name);
@@ -1490,7 +1525,7 @@
 }
 
 void dvmThrowRuntimeException(const char* msg) {
-    dvmThrowException("Ljava/lang/RuntimeException;", msg);
+    dvmThrowExceptionByClass(gDvm.exRuntimeException, msg);
 }
 
 void dvmThrowStaleDexCacheError(const char* msg) {
diff --git a/vm/Globals.h b/vm/Globals.h
index d0be494..5cca83c 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -236,16 +236,12 @@
      */
     ClassObject* classJavaLangClass;
     ClassObject* classJavaLangClassArray;
-    ClassObject* classJavaLangError;
     ClassObject* classJavaLangObject;
     ClassObject* classJavaLangObjectArray;
-    ClassObject* classJavaLangRuntimeException;
     ClassObject* classJavaLangString;
     ClassObject* classJavaLangThread;
     ClassObject* classJavaLangVMThread;
     ClassObject* classJavaLangThreadGroup;
-    ClassObject* classJavaLangThrowable;
-    ClassObject* classJavaLangStackOverflowError;
     ClassObject* classJavaLangStackTraceElement;
     ClassObject* classJavaLangStackTraceElementArray;
     ClassObject* classJavaLangAnnotationAnnotationArray;
@@ -258,7 +254,6 @@
     ClassObject* classJavaLangReflectMethod;
     ClassObject* classJavaLangReflectMethodArray;
     ClassObject* classJavaLangReflectProxy;
-    ClassObject* classJavaLangExceptionInInitializerError;
     ClassObject* classJavaLangRefPhantomReference;
     ClassObject* classJavaLangRefReference;
     ClassObject* classJavaNioReadWriteDirectByteBuffer;
@@ -267,6 +262,17 @@
     ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember;
     ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray;
 
+    /*
+     * classes representing exception types. The names here don't include
+     * packages, just to keep the use sites a bit less verbose. All are
+     * in java.lang, except where noted.
+     */
+    ClassObject* exError;
+    ClassObject* exExceptionInInitializerError;
+    ClassObject* exRuntimeException;
+    ClassObject* exStackOverflowError;
+    ClassObject* exThrowable;
+
     /* synthetic classes for arrays of primitives */
     ClassObject* classArrayBoolean;
     ClassObject* classArrayChar;
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index d86c45f..760ef3b 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -2887,7 +2887,7 @@
                 foundPossibleHandler = true;
 
                 if (handler->typeIdx == kDexNoIndex)
-                    clazz = gDvm.classJavaLangThrowable;
+                    clazz = gDvm.exThrowable;
                 else
                     clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx,
                                 &localFailure);
@@ -3449,11 +3449,11 @@
     if (gDvm.classJavaLangString == NULL)
         gDvm.classJavaLangString =
             dvmFindSystemClassNoInit("Ljava/lang/String;");
-    if (gDvm.classJavaLangThrowable == NULL) {
-        gDvm.classJavaLangThrowable =
+    if (gDvm.exThrowable == NULL) {
+        gDvm.exThrowable =
             dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
         gDvm.offJavaLangThrowable_cause =
-            dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+            dvmFindFieldOffset(gDvm.exThrowable,
                 "cause", "Ljava/lang/Throwable;");
     }
     if (gDvm.classJavaLangObject == NULL)
@@ -4285,7 +4285,7 @@
     case OP_THROW:
         resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
         if (VERIFY_OK(failure) && resClass != NULL) {
-            if (!dvmInstanceof(resClass, gDvm.classJavaLangThrowable)) {
+            if (!dvmInstanceof(resClass, gDvm.exThrowable)) {
                 LOG_VFY("VFY: thrown class %s not instanceof Throwable\n",
                         resClass->descriptor);
                 failure = VERIFY_ERROR_GENERIC;
diff --git a/vm/interp/Stack.c b/vm/interp/Stack.c
index 14696a7..fd1d791 100644
--- a/vm/interp/Stack.c
+++ b/vm/interp/Stack.c
@@ -1064,8 +1064,7 @@
         LOGW("Stack overflow while throwing exception\n");
         dvmClearException(self);
     }
-    dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError,
-        NULL, excep);
+    dvmThrowChainedExceptionByClass(gDvm.exStackOverflowError, NULL, excep);
 }
 
 /*
@@ -1078,7 +1077,7 @@
 
     assert(self->stackOverflowed);
 
-    if (exception->clazz != gDvm.classJavaLangStackOverflowError) {
+    if (exception->clazz != gDvm.exStackOverflowError) {
         /* exception caused during SOE, not the SOE itself */
         return;
     }
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index 66c9fca..7b37f74 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -3711,37 +3711,28 @@
  */
 static void throwClinitError(void)
 {
-    Thread* self = dvmThreadSelf();
-    Object* exception;
-    Object* eiie;
+    if (gDvm.exExceptionInInitializerError == NULL) {
+        /*
+         * ExceptionInInitializerError isn't itself initialized. This
+         * can happen very early during VM startup if there is a
+         * problem with one of the corest-of-the-core classes, and it
+         * can possibly happen during a dexopt run. Rather than do
+         * anything fancier, we just abort here with a blatant
+         * message.
+         */
+        LOGE("Fatal error during early class initialization:\n");
+        dvmLogExceptionStackTrace();
+        dvmAbort();
+    }
 
-    exception = dvmGetException(self);
+    Thread* self = dvmThreadSelf();
+    Object* exception = dvmGetException(self);
+
     dvmAddTrackedAlloc(exception, self);
     dvmClearException(self);
 
-    if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
-        /*
-         * Always resolves to same thing -- no race condition.
-         */
-        gDvm.classJavaLangExceptionInInitializerError =
-            dvmFindSystemClass(
-                    "Ljava/lang/ExceptionInInitializerError;");
-        if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
-            LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
-            goto fail;
-        }
-
-        gDvm.methJavaLangExceptionInInitializerError_init =
-            dvmFindDirectMethodByDescriptor(gDvm.classJavaLangExceptionInInitializerError,
-            "<init>", "(Ljava/lang/Throwable;)V");
-        if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) {
-            LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
-            goto fail;
-        }
-    }
-
-    eiie = dvmAllocObject(gDvm.classJavaLangExceptionInInitializerError,
-                ALLOC_DEFAULT);
+    Object* eiie =
+        dvmAllocObject(gDvm.exExceptionInInitializerError, ALLOC_DEFAULT);
     if (eiie == NULL)
         goto fail;