Yak-shaving in support of the exception cleanup.

This change restructures where early class initialization happens in
the VM, moving a few chunks of it together into Init.c. The exception
classes get moved here as well as the super-core classes needed
particularly for dex optimization. This is all done to support a follow-up
change that will put more of the verifier's required exceptions into
the list. (Before this change, it would have been unsafe to do that.)

This change also paves the way for further consolidation of class and
member initialization, which I hope will make it easier to transition
the whole mess into the new copying-gc world, when that arrives on the
scene.

Bug: 3500987
Change-Id: I0caae09432a05287af53aa36a6663997ab310985
diff --git a/vm/Exception.c b/vm/Exception.c
index 3fee47a..5398727 100644
--- a/vm/Exception.c
+++ b/vm/Exception.c
@@ -99,158 +99,6 @@
 static bool initException(Object* exception, const char* msg, Object* cause,
     Thread* self);
 
-
-/*
- * 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
- * are initialized by the verifier (dvmVerifyCodeFlow).
- */
-bool dvmExceptionStartup(void)
-{
-    bool ok = true;
-
-    ok &= initRef(&gDvm.exArithmeticException,
-            "Ljava/lang/ArithmeticException;");
-    ok &= initRef(&gDvm.exArrayIndexOutOfBoundsException,
-            "Ljava/lang/ArrayIndexOutOfBoundsException;");
-    ok &= initRef(&gDvm.exArrayStoreException,
-            "Ljava/lang/ArrayStoreException;");
-    ok &= initRef(&gDvm.exClassCastException,
-            "Ljava/lang/ClassCastException;");
-    ok &= initRef(&gDvm.exClassNotFoundException,
-            "Ljava/lang/ClassNotFoundException;");
-    ok &= initRef(&gDvm.exClassFormatError, "Ljava/lang/ClassFormatError;");
-    ok &= initRef(&gDvm.exError, "Ljava/lang/Error;");
-    ok &= initRef(&gDvm.exExceptionInInitializerError,
-            "Ljava/lang/ExceptionInInitializerError;");
-    ok &= initRef(&gDvm.exFileNotFoundException,
-            "Ljava/io/FileNotFoundException;");
-    ok &= initRef(&gDvm.exIOException, "Ljava/io/IOException;");
-    ok &= initRef(&gDvm.exIllegalAccessException,
-            "Ljava/lang/IllegalAccessException;");
-    ok &= initRef(&gDvm.exIllegalArgumentException,
-            "Ljava/lang/IllegalArgumentException;");
-    ok &= initRef(&gDvm.exIllegalMonitorStateException,
-            "Ljava/lang/IllegalMonitorStateException;");
-    ok &= initRef(&gDvm.exIllegalStateException,
-            "Ljava/lang/IllegalStateException;");
-    ok &= initRef(&gDvm.exIllegalThreadStateException,
-            "Ljava/lang/IllegalThreadStateException;");
-    ok &= initRef(&gDvm.exInstantiationException,
-            "Ljava/lang/InstantiationException;");
-    ok &= initRef(&gDvm.exInterruptedException,
-            "Ljava/lang/InterruptedException;");
-    ok &= initRef(&gDvm.exNegativeArraySizeException,
-            "Ljava/lang/NegativeArraySizeException;");
-    ok &= initRef(&gDvm.exNoSuchFieldException,
-            "Ljava/lang/NoSuchFieldException;");
-    ok &= initRef(&gDvm.exNullPointerException,
-            "Ljava/lang/NullPointerException;");
-    ok &= initRef(&gDvm.exRuntimeException, "Ljava/lang/RuntimeException;");
-    ok &= initRef(&gDvm.exStackOverflowError,
-            "Ljava/lang/StackOverflowError;");
-    ok &= initRef(&gDvm.exStaleDexCacheError,
-            "Ldalvik/system/StaleDexCacheError;");
-    ok &= initRef(&gDvm.exStringIndexOutOfBoundsException,
-            "Ljava/lang/StringIndexOutOfBoundsException;");
-    ok &= initRef(&gDvm.exThrowable, "Ljava/lang/Throwable;");
-    ok &= initRef(&gDvm.exUnsupportedOperationException,
-            "Ljava/lang/UnsupportedOperationException;");
-    ok &= initRef(&gDvm.exVirtualMachineError,
-            "Ljava/lang/VirtualMachineError;");
-
-    ok &= initRef(&gDvm.classJavaLangStackTraceElement,
-            "Ljava/lang/StackTraceElement;");
-    ok &= initRef(&gDvm.classJavaLangStackTraceElementArray,
-            "[Ljava/lang/StackTraceElement;");
-
-    if (!ok) {
-        return false;
-    }
-
-    /*
-     * Find the constructor.  Note that, unlike other saved method lookups,
-     * we're using a Method* instead of a vtable offset.  This is because
-     * constructors don't have vtable offsets.  (Also, since we're creating
-     * the object in question, it's impossible for anyone to sub-class it.)
-     */
-    Method* meth;
-    meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
-        "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
-    if (meth == NULL) {
-        LOGE("Unable to find constructor for StackTraceElement\n");
-        return false;
-    }
-    gDvm.methJavaLangStackTraceElement_init = meth;
-
-    /* grab an offset for the stackData field */
-    gDvm.offJavaLangThrowable_stackState =
-        dvmFindFieldOffset(gDvm.exThrowable,
-            "stackState", "Ljava/lang/Object;");
-    if (gDvm.offJavaLangThrowable_stackState < 0) {
-        LOGE("Unable to find Throwable.stackState\n");
-        return false;
-    }
-
-    /* and one for the cause field, just 'cause */
-    gDvm.offJavaLangThrowable_cause =
-        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;
-}
-
-/*
- * Clean up.
- */
-void dvmExceptionShutdown(void)
-{
-    // nothing to do
-}
-
-
 /*
  * Format the message into a small buffer and pass it along.
  */
diff --git a/vm/Exception.h b/vm/Exception.h
index 4bea088..855e7ca 100644
--- a/vm/Exception.h
+++ b/vm/Exception.h
@@ -20,10 +20,6 @@
 #ifndef _DALVIK_EXCEPTION
 #define _DALVIK_EXCEPTION
 
-/* initialization */
-bool dvmExceptionStartup(void);
-void dvmExceptionShutdown(void);
-
 /*
  * Throw an exception in the current thread, by class descriptor.
  */
diff --git a/vm/Globals.h b/vm/Globals.h
index e8bb651..c9a2f78 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -283,8 +283,10 @@
     ClassObject* exIllegalStateException;
     ClassObject* exIllegalThreadStateException;
     ClassObject* exInstantiationException;
+    ClassObject* exInternalError;
     ClassObject* exInterruptedException;
     ClassObject* exNegativeArraySizeException;
+    ClassObject* exNoClassDefFoundError;
     ClassObject* exNoSuchFieldException;
     ClassObject* exNullPointerException;
     ClassObject* exRuntimeException;
@@ -292,6 +294,7 @@
     ClassObject* exStaleDexCacheError;    /* in dalvik.system */
     ClassObject* exStringIndexOutOfBoundsException;
     ClassObject* exThrowable;
+    ClassObject* exUnsatisfiedLinkError;
     ClassObject* exUnsupportedOperationException;
     ClassObject* exVirtualMachineError;
 
@@ -369,7 +372,6 @@
 
     /* constructor method pointers; no vtable involved, so use Method* */
     Method*     methJavaLangStackTraceElement_init;
-    Method*     methJavaLangExceptionInInitializerError_init;
     Method*     methJavaLangRefPhantomReference_init;
     Method*     methJavaLangReflectConstructor_init;
     Method*     methJavaLangReflectField_init;
diff --git a/vm/Init.c b/vm/Init.c
index 1d6b4c8..7f72040 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1145,6 +1145,165 @@
 }
 
 /*
+ * Helper for dvmInitRequiredClassesAndMembers(), 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 (*pClass != NULL) {
+        /*
+         * There are a couple cases where it's legit to call this
+         * function with an already-initialized reference, so just
+         * silently tolerate this instead of complaining loudly.
+         */
+        return true;
+    }
+
+    if (name[0] == '[') {
+        result = dvmFindArrayClass(name, NULL);
+    } else {
+        result = dvmFindSystemClassNoInit(name);
+    }
+
+    if (result == NULL) {
+        LOGE("Could not find essential class %s\n", name);
+        return false;
+    }
+
+    *pClass = result;
+    return true;
+}
+
+/* (documented in header) */
+int dvmInitRequiredClassesAndMembers(void) {
+    /*
+     * Note: Under normal VM use, this is called by dvmStartup()
+     * below. For dex optimization, this is called as well, but in
+     * that case, the call is made from DexPrepare.c.
+     */
+
+    bool ok = true;
+
+    /* The corest of the core classes */
+
+    ok &= initRef(&gDvm.classJavaLangClass, "Ljava/lang/Class;");
+    ok &= initRef(&gDvm.classJavaLangObject, "Ljava/lang/Object;");
+
+    ok &= initRef(&gDvm.classJavaLangString, "Ljava/lang/String;");
+    ok &= initRef(&gDvm.classJavaLangThread, "Ljava/lang/Thread;");
+    ok &= initRef(&gDvm.classJavaLangThreadGroup, "Ljava/lang/ThreadGroup;");
+    ok &= initRef(&gDvm.exThrowable, "Ljava/lang/Throwable;");
+    ok &= initRef(&gDvm.classJavaLangVMThread, "Ljava/lang/VMThread;");
+
+    /* Exception classes and related support classes */
+
+    ok &= initRef(&gDvm.exArithmeticException,
+            "Ljava/lang/ArithmeticException;");
+    ok &= initRef(&gDvm.exArrayIndexOutOfBoundsException,
+            "Ljava/lang/ArrayIndexOutOfBoundsException;");
+    ok &= initRef(&gDvm.exArrayStoreException,
+            "Ljava/lang/ArrayStoreException;");
+    ok &= initRef(&gDvm.exClassCastException,
+            "Ljava/lang/ClassCastException;");
+    ok &= initRef(&gDvm.exClassNotFoundException,
+            "Ljava/lang/ClassNotFoundException;");
+    ok &= initRef(&gDvm.exClassFormatError, "Ljava/lang/ClassFormatError;");
+    ok &= initRef(&gDvm.exError, "Ljava/lang/Error;");
+    ok &= initRef(&gDvm.exExceptionInInitializerError,
+            "Ljava/lang/ExceptionInInitializerError;");
+    ok &= initRef(&gDvm.exFileNotFoundException,
+            "Ljava/io/FileNotFoundException;");
+    ok &= initRef(&gDvm.exIOException, "Ljava/io/IOException;");
+    ok &= initRef(&gDvm.exIllegalAccessException,
+            "Ljava/lang/IllegalAccessException;");
+    ok &= initRef(&gDvm.exIllegalArgumentException,
+            "Ljava/lang/IllegalArgumentException;");
+    ok &= initRef(&gDvm.exIllegalMonitorStateException,
+            "Ljava/lang/IllegalMonitorStateException;");
+    ok &= initRef(&gDvm.exIllegalStateException,
+            "Ljava/lang/IllegalStateException;");
+    ok &= initRef(&gDvm.exIllegalThreadStateException,
+            "Ljava/lang/IllegalThreadStateException;");
+    ok &= initRef(&gDvm.exInstantiationException,
+            "Ljava/lang/InstantiationException;");
+    ok &= initRef(&gDvm.exInternalError,
+            "Ljava/lang/InternalError;");
+    ok &= initRef(&gDvm.exInterruptedException,
+            "Ljava/lang/InterruptedException;");
+    ok &= initRef(&gDvm.exNegativeArraySizeException,
+            "Ljava/lang/NegativeArraySizeException;");
+    ok &= initRef(&gDvm.exNoClassDefFoundError,
+            "Ljava/lang/NoClassDefFoundError;");
+    ok &= initRef(&gDvm.exNoSuchFieldException,
+            "Ljava/lang/NoSuchFieldException;");
+    ok &= initRef(&gDvm.exNullPointerException,
+            "Ljava/lang/NullPointerException;");
+    ok &= initRef(&gDvm.exRuntimeException, "Ljava/lang/RuntimeException;");
+    ok &= initRef(&gDvm.exStackOverflowError,
+            "Ljava/lang/StackOverflowError;");
+    ok &= initRef(&gDvm.exStaleDexCacheError,
+            "Ldalvik/system/StaleDexCacheError;");
+    ok &= initRef(&gDvm.exStringIndexOutOfBoundsException,
+            "Ljava/lang/StringIndexOutOfBoundsException;");
+    ok &= initRef(&gDvm.exThrowable, "Ljava/lang/Throwable;");
+    ok &= initRef(&gDvm.exUnsatisfiedLinkError,
+            "Ljava/lang/UnsatisfiedLinkError;");
+    ok &= initRef(&gDvm.exUnsupportedOperationException,
+            "Ljava/lang/UnsupportedOperationException;");
+    ok &= initRef(&gDvm.exVirtualMachineError,
+            "Ljava/lang/VirtualMachineError;");
+
+    ok &= initRef(&gDvm.classJavaLangStackTraceElement,
+            "Ljava/lang/StackTraceElement;");
+    ok &= initRef(&gDvm.classJavaLangStackTraceElementArray,
+            "[Ljava/lang/StackTraceElement;");
+
+    if (!ok) {
+        return false;
+    }
+
+    /*
+     * Find the StackTraceElement constructor. Note that, unlike other
+     * saved method lookups, we're using a Method* instead of a vtable
+     * offset. This is because constructors don't have vtable offsets.
+     * (Also, since we're creating the object in question, it's
+     * impossible for anyone to sub-class it.)
+     */
+    Method* meth;
+    meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
+        "<init>",
+        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+    if (meth == NULL) {
+        LOGE("Unable to find constructor for StackTraceElement\n");
+        return false;
+    }
+    gDvm.methJavaLangStackTraceElement_init = meth;
+
+    /* grab an offset for the field Throwable.stackState */
+    gDvm.offJavaLangThrowable_stackState =
+        dvmFindFieldOffset(gDvm.exThrowable,
+            "stackState", "Ljava/lang/Object;");
+    if (gDvm.offJavaLangThrowable_stackState < 0) {
+        LOGE("Unable to find Throwable.stackState\n");
+        return false;
+    }
+
+    /* and one for the field Throwable.cause, just 'cause */
+    gDvm.offJavaLangThrowable_cause =
+        dvmFindFieldOffset(gDvm.exThrowable,
+            "cause", "Ljava/lang/Throwable;");
+    if (gDvm.offJavaLangThrowable_cause < 0) {
+        LOGE("Unable to find Throwable.cause\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
  * VM initialization.  Pass in any options provided on the command line.
  * Do not pass in the class name or the options for the class.
  *
@@ -1227,12 +1386,20 @@
         goto fail;
     if (!dvmClassStartup())
         goto fail;
+
+    /*
+     * At this point, the system is guaranteed to be sufficiently
+     * initialized that we can look up classes and class members. This
+     * call populates the gDvm instance with all the class and member
+     * references that the VM wants to use directly.
+     */
+    if (!dvmInitRequiredClassesAndMembers())
+        goto fail;
+
     if (!dvmBaseClassStartup())
         goto fail;
     if (!dvmThreadObjStartup())
         goto fail;
-    if (!dvmExceptionStartup())
-        goto fail;
     if (!dvmStringInternStartup())
         goto fail;
     if (!dvmNativeStartup())
@@ -1246,31 +1413,6 @@
     if (!dvmProfilingStartup())
         goto fail;
 
-    /* make sure we got these [can this go away?] */
-    assert(gDvm.classJavaLangClass != NULL);
-    assert(gDvm.classJavaLangObject != NULL);
-    //assert(gDvm.classJavaLangString != NULL);
-    assert(gDvm.classJavaLangThread != NULL);
-    assert(gDvm.classJavaLangVMThread != NULL);
-    assert(gDvm.classJavaLangThreadGroup != NULL);
-
-    /*
-     * Make sure these exist.  If they don't, we can return a failure out
-     * of main and nip the whole thing in the bud.
-     */
-    static const char* earlyClasses[] = {
-        "Ljava/lang/InternalError;",
-        "Ljava/lang/StackOverflowError;",
-        "Ljava/lang/UnsatisfiedLinkError;",
-        "Ljava/lang/NoClassDefFoundError;",
-        NULL
-    };
-    const char** pClassName;
-    for (pClassName = earlyClasses; *pClassName != NULL; pClassName++) {
-        if (dvmFindSystemClassNoInit(*pClassName) == NULL)
-            goto fail;
-    }
-
     /*
      * Create a table of methods for which we will substitute an "inline"
      * version for performance.
@@ -1679,7 +1821,6 @@
     dvmProfilingShutdown();
     dvmJniShutdown();
     dvmStringInternShutdown();
-    dvmExceptionShutdown();
     dvmThreadShutdown();
     dvmClassShutdown();
     dvmRegisterMapShutdown();
diff --git a/vm/Init.h b/vm/Init.h
index 63051a2..ac04bf4 100644
--- a/vm/Init.h
+++ b/vm/Init.h
@@ -41,6 +41,14 @@
     DexClassVerifyMode verifyMode, int dexoptFlags);
 
 /*
+ * Look up the set of classes and members used directly by the VM, storing
+ * them into the globals instance. See Globals.h. This function is exposed
+ * so that dex optimization may call it (while avoiding doing other
+ * unnecessary VM initialization).
+ */
+int dvmInitRequiredClassesAndMembers(void);
+
+/*
  * Replacement for fprintf() when we want to send a message to the console.
  * This defaults to fprintf(), but will use the JNI fprintf callback if
  * one was provided.
diff --git a/vm/Thread.c b/vm/Thread.c
index 9dfad4b..8861dff 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -327,22 +327,13 @@
 bool dvmThreadObjStartup(void)
 {
     /*
-     * Cache the locations of these classes.  It's likely that we're the
-     * first to reference them, so they're being loaded now.
+     * TODO: Pull this function's guts into dvmInitRequiredClassesAndMembers()
+     * in Init.c
      */
-    gDvm.classJavaLangThread =
-        dvmFindSystemClassNoInit("Ljava/lang/Thread;");
-    gDvm.classJavaLangVMThread =
-        dvmFindSystemClassNoInit("Ljava/lang/VMThread;");
-    gDvm.classJavaLangThreadGroup =
-        dvmFindSystemClassNoInit("Ljava/lang/ThreadGroup;");
-    if (gDvm.classJavaLangThread == NULL ||
-        gDvm.classJavaLangThreadGroup == NULL ||
-        gDvm.classJavaLangThreadGroup == NULL)
-    {
-        LOGE("Could not find one or more essential thread classes\n");
-        return false;
-    }
+
+    assert(gDvm.classJavaLangThread != NULL);
+    assert(gDvm.classJavaLangThreadGroup != NULL);
+    assert(gDvm.classJavaLangVMThread != NULL);
 
     /*
      * Cache field offsets.  This makes things a little faster, at the
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 760ef3b..f2b884d 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -3438,27 +3438,6 @@
     /* only need to do this if the table was updated */
     checkMergeTab();
 #endif
-
-    /*
-     * We rely on these for verification of const-class, const-string,
-     * and throw instructions.  Make sure we have them loaded.
-     */
-    if (gDvm.classJavaLangClass == NULL)
-        gDvm.classJavaLangClass =
-            dvmFindSystemClassNoInit("Ljava/lang/Class;");
-    if (gDvm.classJavaLangString == NULL)
-        gDvm.classJavaLangString =
-            dvmFindSystemClassNoInit("Ljava/lang/String;");
-    if (gDvm.exThrowable == NULL) {
-        gDvm.exThrowable =
-            dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
-        gDvm.offJavaLangThrowable_cause =
-            dvmFindFieldOffset(gDvm.exThrowable,
-                "cause", "Ljava/lang/Throwable;");
-    }
-    if (gDvm.classJavaLangObject == NULL)
-        gDvm.classJavaLangObject =
-            dvmFindSystemClassNoInit("Ljava/lang/Object;");
 }
 
 /*
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
index 99c8b1d..3efb13a 100644
--- a/vm/analysis/DexPrepare.c
+++ b/vm/analysis/DexPrepare.c
@@ -833,12 +833,23 @@
     dvmSetBootPathExtraDex(pDvmDex);
 
     /*
-     * We have some circularity issues with Class and Object that are most
-     * easily avoided by ensuring that Object is never the first thing we
-     * try to find.  Take care of that here.  (We only need to do this when
-     * loading classes from the DEX file that contains Object, and only
-     * when Object comes first in the list, but it costs very little to
-     * do it in all cases.)
+     * At this point, it is safe -- and necessary! -- to look up the
+     * VM's required classes and members, even when what we are in the
+     * process of processing is the core library that defines these
+     * classes itself.
+     */
+    if (!dvmInitRequiredClassesAndMembers()) {
+        return false;
+    }
+
+    /*
+     * We have some circularity issues with Class and Object that are
+     * most easily avoided by ensuring that Object is never the first
+     * thing we try to find-and-initialize. The call to
+     * dvmFindSystemClass() here takes care of that situation. (We
+     * only need to do this when loading classes from the DEX file
+     * that contains Object, and only when Object comes first in the
+     * list, but it costs very little to do it in all cases.)
      */
     if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
         LOGE("ERROR: java.lang.Class does not exist!\n");
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index e12f9d2..f9499cf 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -340,6 +340,10 @@
     gDvm.initiatingLoaderList = (InitiatingLoaderList*)
         calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
 
+    /*
+     * Initialize the class Class. This has to be done specially, particularly
+     * because it is an instance of itself.
+     */
     gDvm.classJavaLangClass = (ClassObject*) dvmMalloc(
         classObjectSize(CLASS_SFIELD_SLOTS), ALLOC_DEFAULT);
     DVM_OBJECT_INIT(&gDvm.classJavaLangClass->obj, gDvm.classJavaLangClass);