You can't call initCause on a ClassNotFoundException.

Unlike NoClassDefFoundError, ClassNotFoundException has to be constructed
with a cause, or it will stupidly set a null cause which can't then be
changed.

This patch also fixes incorrect caching of jclass local references in statics,
which I noticed while fixing the test I'd broken.

Change-Id: I017f4a4e4158554427ccb37b650c985add28980c
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 3b5ac5d..8a2cc5c 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1156,8 +1156,7 @@
     // Initialize the cause of the NCDFE.
     ScopedLocalRef<jthrowable> ncdfe(env, env->ExceptionOccurred());
     env->ExceptionClear();
-    static jclass Throwable_class = env->FindClass("java/lang/Throwable");
-    static jmethodID initCause_mid = env->GetMethodID(Throwable_class, "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
+    static jmethodID initCause_mid = env->GetMethodID(env->FindClass("java/lang/Throwable"), "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
     env->CallObjectMethod(ncdfe.get(), initCause_mid, cause.get());
     env->Throw(ncdfe.get());
   }
diff --git a/src/debugger.cc b/src/debugger.cc
index f1e6066..72a4fdd 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -1739,10 +1739,9 @@
   Thread* self = Thread::Current();
   JNIEnv* env = self->GetJniEnv();
 
-  static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
-  static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
-  static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
-      "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+  static jclass Chunk_class = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
+  static jclass DdmServer_class = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
+  static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
   static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
   static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
   static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
@@ -1836,7 +1835,7 @@
   }
 
   JNIEnv* env = self->GetJniEnv();
-  static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
+  static jclass DdmServer_class = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
   static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
   jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
   env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 96eec2f..996d6a4 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -52,18 +52,12 @@
   Class* c = class_linker->FindClass(descriptor.c_str(), class_loader);
   if (c == NULL) {
     // Convert NoClassDefFoundError to ClassNotFoundException.
-    ScopedLocalRef<jthrowable> ncdfe(env, env->ExceptionOccurred());
+    ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
     env->ExceptionClear();
-
-    Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;", name.c_str());
-
-    ScopedLocalRef<jthrowable> cnfe(env, env->ExceptionOccurred());
-    env->ExceptionClear();
-
-    static jclass Throwable_class = env->FindClass("java/lang/Throwable");
-    static jmethodID initCause_mid = env->GetMethodID(Throwable_class, "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
-    env->CallObjectMethod(cnfe.get(), initCause_mid, ncdfe.get());
-    env->Throw(cnfe.get());
+    static jclass ClassNotFoundException_class = CacheClass(env, "java/lang/ClassNotFoundException");
+    static jmethodID ctor = env->GetMethodID(ClassNotFoundException_class, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+    jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(ClassNotFoundException_class, ctor, javaName, cause.get()));
+    env->Throw(cnfe);
     return NULL;
   }
   if (initialize) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 8362895..8e7c0a2 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2964,6 +2964,14 @@
   // The weak_globals table is visited by the GC itself (because it mutates the table).
 }
 
+jclass CacheClass(JNIEnv* env, const char* jni_class_name) {
+  ScopedLocalRef<jclass> c(env, env->FindClass(jni_class_name));
+  if (c.get() == NULL) {
+    return NULL;
+  }
+  return reinterpret_cast<jclass>(env->NewGlobalRef(c.get()));
+}
+
 }  // namespace art
 
 std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index e02d01d..dc268c4 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -26,6 +26,7 @@
 void SetJniGlobalsMax(size_t max);
 void JniAbort(const char* jni_function_name);
 void* FindNativeMethod(Thread* thread);
+jclass CacheClass(JNIEnv* env, const char* jni_class_name);
 
 template<typename T> T Decode(JNIEnv*, jobject);
 template<typename T> T AddLocalReference(JNIEnv*, const Object*);