Throw a pre-fabricated NoClassDefFoundError.

When a class loader tries to load a class, it first asks its parent to
look for it.  This continues up the chain until the bootstrap class loader
is reached.  If the class can't be found, the bootstrap loader throws a
NoClassDefFoundError.  As a result, we're throwing at least one exception
for every class we load that doesn't come from the bootstrap path.

This change creates a "stock" NoClassDefFoundError exception with a trivial
stack trace and throws that instead, saving the overhead of creating and
initializing the exception object.

I think the only way that anyone will see this is if they try to
load a class directly from the bootstrap loader, in which case the
ClassNotFoundException will show the canned NCDFE as the cause.  I
don't think any useful diagnostic information is being lost.
diff --git a/vm/Globals.h b/vm/Globals.h
index e4ad4c6..5e75b5d 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -418,12 +418,11 @@
     GcHeap*     gcHeap;
 
     /*
-     * Pre-allocated object for out-of-memory errors.
+     * Pre-allocated throwables.
      */
     Object*     outOfMemoryObj;
-
-    /* pre-allocated general failure exception */
     Object*     internalErrorObj;
+    Object*     noClassDefFoundErrorObj;
 
     /* Monitor list, so we can free them */
     /*volatile*/ Monitor* monitorList;
diff --git a/vm/Init.c b/vm/Init.c
index 3663745..f546dab 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1211,7 +1211,7 @@
      * Do some "late" initialization for the memory allocator.  This may
      * allocate storage and initialize classes.
      */
-    if (!dvmGcLateInit())
+    if (!dvmCreateStockExceptions())
         goto fail;
 
     /*
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c
index e247413..0961520 100644
--- a/vm/alloc/Alloc.c
+++ b/vm/alloc/Alloc.c
@@ -68,25 +68,27 @@
 }
 
 /*
- * Create a "stock instance" of an exception class.  These won't have
- * useful stack traces in them, but they can be thrown when everything
- * else is not working in a container class.
+ * Create a "stock instance" of an exception class.
  */
-static Object* createStockException(const char* descriptor)
+static Object* createStockException(const char* descriptor, const char* msg)
 {
+    Thread* self = dvmThreadSelf();
+    StringObject* msgStr = NULL;
     ClassObject* clazz;
     Method* init;
     Object* obj;
 
+    /* find class, initialize if necessary */
     clazz = dvmFindSystemClass(descriptor);
     if (clazz == NULL) {
         LOGE("Unable to find %s\n", descriptor);
         return NULL;
     }
 
-    init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V");
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
+            "(Ljava/lang/String;)V");
     if (init == NULL) {
-        LOGE("Unable to find nullary constructor for %s\n", descriptor);
+        LOGE("Unable to find String-arg constructor for %s\n", descriptor);
         return NULL;
     }
 
@@ -94,30 +96,56 @@
     if (obj == NULL)
         return NULL;
 
-    Thread* self = dvmThreadSelf();
-    JValue unused;
-    dvmCallMethod(self, init, obj, &unused);
-    if (dvmCheckException(self))
-        return NULL;
+    if (msg == NULL) {
+        msgStr = NULL;
+    } else {
+        msgStr = dvmCreateStringFromCstr(msg, ALLOC_DEFAULT);
+        if (msgStr == NULL) {
+            LOGW("Could not allocate message string \"%s\"\n", msg);
+            dvmReleaseTrackedAlloc(obj, self);
+            return NULL;
+        }
+    }
 
+    JValue unused;
+    dvmCallMethod(self, init, obj, &unused, msgStr);
+    if (dvmCheckException(self)) {
+        dvmReleaseTrackedAlloc((Object*) msgStr, self);
+        dvmReleaseTrackedAlloc(obj, self);
+        return NULL;
+    }
+
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // okay if msgStr NULL
     return obj;
 }
 
 /*
- * "Late" initialization.  We had to defer this until we were able to
- * interpret code.
+ * Create some "stock" exceptions.  These can be thrown when the system is
+ * too screwed up to allocate and initialize anything, or when we don't
+ * need a meaningful stack trace.
+ *
+ * We can't do this during the initial startup because we need to execute
+ * the constructors.
  */
-bool dvmGcLateInit(void)
+bool dvmCreateStockExceptions(void)
 {
     /*
      * Pre-allocate some throwables.  These need to be explicitly added
-     * to the root set by the GC.
+     * to the GC's root set (see dvmHeapMarkRootSet()).
      */
-    gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;");
+    gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
+        "[memory exhausted]");
     dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
-    gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;");
+    gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
+        "[pre-allocated]");
     dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
-    if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL) {
+    gDvm.noClassDefFoundErrorObj =
+        createStockException("Ljava/lang/NoClassDefFoundError;", NULL);
+    dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
+
+    if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
+        gDvm.noClassDefFoundErrorObj == NULL)
+    {
         LOGW("Unable to create stock exceptions\n");
         return false;
     }
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index 8bf4520..f1156d3 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -25,9 +25,9 @@
  * Initialization.
  */
 bool dvmGcStartup(void);
+bool dvmCreateStockExceptions(void);
 bool dvmGcStartupAfterZygote(void);
 void dvmGcShutdown(void);
-bool dvmGcLateInit(void);
 
 /*
  * Do any last-minute preparation before we call fork() for the first time.
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 84349a9..b8da3a3 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -378,6 +378,7 @@
     LOG_SCAN("special objects\n");
     dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
+    dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
 
     HPROF_CLEAR_GC_SCAN_STATE();
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index 999a1a1..330c381 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -1414,8 +1414,14 @@
         }
 
         if (pDvmDex == NULL || pClassDef == NULL) {
-            dvmThrowExceptionWithClassMessage(
-                "Ljava/lang/NoClassDefFoundError;", descriptor);
+            if (gDvm.noClassDefFoundErrorObj != NULL) {
+                /* usual case -- use prefabricated object */
+                dvmSetException(self, gDvm.noClassDefFoundErrorObj);
+            } else {
+                /* dexopt case -- can't guarantee prefab (core.jar) */
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/NoClassDefFoundError;", descriptor);
+            }
             goto bail;
         }