Finish cleanup of class/field/method caching.

Change-Id: I289ae724cbd98487429275837d23b7b2d7096156
diff --git a/src/class_linker.cc b/src/class_linker.cc
index c1382e2..43f1b42 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -135,25 +135,20 @@
 }
 
 static void WrapExceptionInInitializer() {
-  JNIEnv* env = Thread::Current()->GetJniEnv();
+  Thread* self = Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
 
   ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
   CHECK(cause.get() != NULL);
 
   env->ExceptionClear();
+  bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
+  env->Throw(cause.get());
 
-  if (env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error)) {
-    // We only wrap non-Error exceptions; an Error can just be used as-is.
-    env->Throw(cause.get());
-    return;
+  // We only wrap non-Error exceptions; an Error can just be used as-is.
+  if (!is_error) {
+    self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", NULL);
   }
-
-  jmethodID mid = env->GetMethodID(WellKnownClasses::java_lang_ExceptionInInitializerError, "<init>" , "(Ljava/lang/Throwable;)V");
-  CHECK(mid != NULL);
-
-  ScopedLocalRef<jthrowable> eiie(env,
-      reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ExceptionInInitializerError, mid, cause.get())));
-  env->Throw(eiie.get());
 }
 
 static size_t Hash(const char* s) {
@@ -477,9 +472,6 @@
   Class* java_lang_ref_ReferenceQueue = FindSystemClass("Ljava/lang/ref/ReferenceQueue;");
   Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
 
-  Heap* heap = Runtime::Current()->GetHeap();
-  heap->SetWellKnownClasses(java_lang_ref_FinalizerReference, java_lang_ref_ReferenceQueue);
-
   const DexFile& java_lang_dex = FindDexFile(java_lang_ref_Reference->GetDexCache());
 
   Field* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
@@ -512,6 +504,7 @@
   CHECK_EQ(java_lang_dex.GetFieldId(zombie->GetDexFieldIndex()).type_idx_,
            GetClassRoot(kJavaLangObject)->GetDexTypeIndex());
 
+  Heap* heap = Runtime::Current()->GetHeap();
   heap->SetReferenceOffsets(referent->GetOffset(),
                             queue->GetOffset(),
                             queueNext->GetOffset(),
@@ -1139,18 +1132,14 @@
     std::string class_name_string(DescriptorToDot(descriptor));
     ScopedThreadStateChange tsc(self, kNative);
     JNIEnv* env = self->GetJniEnv();
-    ScopedLocalRef<jclass> c(env, AddLocalReference<jclass>(env, GetClassRoot(kJavaLangClassLoader)));
-    CHECK(c.get() != NULL);
-    // TODO: cache method?
-    jmethodID mid = env->GetMethodID(c.get(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
-    CHECK(mid != NULL);
     ScopedLocalRef<jobject> class_name_object(env, env->NewStringUTF(class_name_string.c_str()));
     if (class_name_object.get() == NULL) {
       return NULL;
     }
     ScopedLocalRef<jobject> class_loader_object(env, AddLocalReference<jobject>(env, class_loader));
     CHECK(class_loader_object.get() != NULL);
-    ScopedLocalRef<jobject> result(env, env->CallObjectMethod(class_loader_object.get(), mid,
+    ScopedLocalRef<jobject> result(env, env->CallObjectMethod(class_loader_object.get(),
+                                                              WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                               class_name_object.get()));
     if (env->ExceptionCheck()) {
       // If the ClassLoader threw, pass that exception up.
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index a8101ba..1440023 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -760,10 +760,7 @@
     // In the case of checked exceptions that aren't declared, the exception must be wrapped by
     // a UndeclaredThrowableException.
     Throwable* exception = thread->GetException();
-    thread->ClearException();
-    if (!exception->IsCheckedException()) {
-      thread->SetException(exception);
-    } else {
+    if (exception->IsCheckedException()) {
       SynthesizedProxyClass* proxy_class =
           down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
       int throws_index = -1;
@@ -782,10 +779,8 @@
         Class* declared_exception = declared_exceptions->Get(i);
         declares_exception = declared_exception->IsAssignableFrom(exception_class);
       }
-      if (declares_exception) {
-        thread->SetException(exception);
-      } else {
-        ThrowNewUndeclaredThrowableException(thread, env, exception);
+      if (!declares_exception) {
+        ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL);
       }
     }
   }
diff --git a/src/heap.cc b/src/heap.cc
index bc59cfc..e50a31c 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -142,8 +142,6 @@
       is_gc_running_(false),
       num_bytes_allocated_(0),
       num_objects_allocated_(0),
-      java_lang_ref_FinalizerReference_(NULL),
-      java_lang_ref_ReferenceQueue_(NULL),
       reference_referent_offset_(0),
       reference_queue_offset_(0),
       reference_queueNext_offset_(0),
@@ -728,14 +726,6 @@
   lock_->Unlock();
 }
 
-void Heap::SetWellKnownClasses(Class* java_lang_ref_FinalizerReference,
-    Class* java_lang_ref_ReferenceQueue) {
-  java_lang_ref_FinalizerReference_ = java_lang_ref_FinalizerReference;
-  java_lang_ref_ReferenceQueue_ = java_lang_ref_ReferenceQueue;
-  CHECK(java_lang_ref_FinalizerReference_ != NULL);
-  CHECK(java_lang_ref_ReferenceQueue_ != NULL);
-}
-
 void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset,
     MemberOffset reference_queue_offset,
     MemberOffset reference_queueNext_offset,
@@ -813,26 +803,19 @@
 
 void Heap::AddFinalizerReference(Thread* self, Object* object) {
   ScopedThreadStateChange tsc(self, kRunnable);
-  static Method* FinalizerReference_add =
-      java_lang_ref_FinalizerReference_->FindDirectMethod("add", "(Ljava/lang/Object;)V");
-  DCHECK(FinalizerReference_add != NULL);
   JValue args[1];
   args[0].SetL(object);
-  FinalizerReference_add->Invoke(self, NULL, args, NULL);
+  DecodeMethod(WellKnownClasses::java_lang_ref_FinalizerReference_add)->Invoke(self, NULL, args, NULL);
 }
 
 void Heap::EnqueueClearedReferences(Object** cleared) {
   DCHECK(cleared != NULL);
   if (*cleared != NULL) {
-    static Method* ReferenceQueue_add =
-        java_lang_ref_ReferenceQueue_->FindDirectMethod("add", "(Ljava/lang/ref/Reference;)V");
-    DCHECK(ReferenceQueue_add != NULL);
-
     Thread* self = Thread::Current();
     ScopedThreadStateChange tsc(self, kRunnable);
     JValue args[1];
     args[0].SetL(*cleared);
-    ReferenceQueue_add->Invoke(self, NULL, args, NULL);
+    DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(self, NULL, args, NULL);
     *cleared = NULL;
   }
 }
diff --git a/src/heap.h b/src/heap.h
index 3f0ffc0..5b6b991 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -139,9 +139,6 @@
     return mark_bitmap_;
   }
 
-  void SetWellKnownClasses(Class* java_lang_ref_FinalizerReference,
-      Class* java_lang_ref_ReferenceQueue);
-
   void SetReferenceOffsets(MemberOffset reference_referent_offset,
                            MemberOffset reference_queue_offset,
                            MemberOffset reference_queueNext_offset,
@@ -280,9 +277,6 @@
   // Number of objects allocated.  Adjusted after each allocation and free.
   size_t num_objects_allocated_;
 
-  Class* java_lang_ref_FinalizerReference_;
-  Class* java_lang_ref_ReferenceQueue_;
-
   // offset of java.lang.ref.Reference.referent
   MemberOffset reference_referent_offset_;
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 03d668c..87d5449 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -522,6 +522,47 @@
   }
 }
 
+int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause) {
+    ScopedJniThreadState ts(env);
+
+    // Turn the const char* into a java.lang.String.
+    ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg));
+    if (msg != NULL && s.get() == NULL) {
+      return JNI_ERR;
+    }
+
+    // Choose an appropriate constructor and set up the arguments.
+    jvalue args[2];
+    const char* signature;
+    if (msg == NULL && cause == NULL) {
+      signature = "()V";
+    } else if (msg != NULL && cause == NULL) {
+      signature = "(Ljava/lang/String;)V";
+      args[0].l = s.get();
+    } else if (msg == NULL && cause != NULL) {
+      signature = "(Ljava/lang/Throwable;)V";
+      args[0].l = cause;
+    } else {
+      signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V";
+      args[0].l = s.get();
+      args[1].l = cause;
+    }
+    jmethodID mid = env->GetMethodID(exception_class, "<init>", signature);
+    if (mid == NULL) {
+      LOG(ERROR) << "No <init>" << signature << " in " << PrettyClass(Decode<Class*>(env, exception_class));
+      return JNI_ERR;
+    }
+
+    ScopedLocalRef<jthrowable> exception(env, reinterpret_cast<jthrowable>(env->NewObjectA(exception_class, mid, args)));
+    if (exception.get() == NULL) {
+      return JNI_ERR;
+    }
+
+    ts.Self()->SetException(Decode<Throwable*>(ts, exception.get()));
+
+    return JNI_OK;
+}
+
 static jint JII_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
   if (vm == NULL || p_env == NULL) {
     return JNI_ERR;
@@ -821,29 +862,7 @@
   }
 
   static jint ThrowNew(JNIEnv* env, jclass c, const char* msg) {
-    ScopedJniThreadState ts(env);
-    // TODO: check for a pending exception to decide what constructor to call.
-    jmethodID mid = ((msg != NULL)
-                     ? env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V")
-                     : env->GetMethodID(c, "<init>", "()V"));
-    if (mid == NULL) {
-      return JNI_ERR;
-    }
-    ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg));
-    if (msg != NULL && s.get() == NULL) {
-      return JNI_ERR;
-    }
-
-    jvalue args[1];
-    args[0].l = s.get();
-    ScopedLocalRef<jthrowable> exception(env, reinterpret_cast<jthrowable>(env->NewObjectA(c, mid, args)));
-    if (exception.get() == NULL) {
-      return JNI_ERR;
-    }
-
-    ts.Self()->SetException(Decode<Throwable*>(ts, exception.get()));
-
-    return JNI_OK;
+    return ThrowNewException(env, c, msg, NULL);
   }
 
   static jboolean ExceptionCheck(JNIEnv* env) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 2964bba..389c96b 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -87,6 +87,8 @@
 JValue InvokeWithJValues(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args);
 JValue InvokeWithJValues(Thread* self, Object* receiver, Method* m, JValue* args);
 
+int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause);
+
 struct JavaVMExt : public JavaVM {
   JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
   ~JavaVMExt();
diff --git a/src/oat/runtime/support_proxy.cc b/src/oat/runtime/support_proxy.cc
index 26f61cf..37cacb4 100644
--- a/src/oat/runtime/support_proxy.cc
+++ b/src/oat/runtime/support_proxy.cc
@@ -188,10 +188,7 @@
     // In the case of checked exceptions that aren't declared, the exception must be wrapped by
     // a UndeclaredThrowableException.
     Throwable* exception = self->GetException();
-    self->ClearException();
-    if (!exception->IsCheckedException()) {
-      self->SetException(exception);
-    } else {
+    if (exception->IsCheckedException()) {
       SynthesizedProxyClass* proxy_class =
           down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
       int throws_index = -1;
@@ -210,10 +207,8 @@
         Class* declared_exception = declared_exceptions->Get(i);
         declares_exception = declared_exception->IsAssignableFrom(exception_class);
       }
-      if (declares_exception) {
-        self->SetException(exception);
-      } else {
-        ThrowNewUndeclaredThrowableException(self, env, exception);
+      if (!declares_exception) {
+        self->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL);
       }
     }
   }
diff --git a/src/object.cc b/src/object.cc
index d91e70c..7e73e8d 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -37,6 +37,7 @@
 #include "runtime_support.h"
 #include "stack.h"
 #include "utils.h"
+#include "well_known_classes.h"
 
 #if defined(ART_USE_LLVM_COMPILER)
 #include "compiler_llvm/inferred_reg_category_map.h"
@@ -932,8 +933,7 @@
 }
 
 bool Class::IsThrowableClass() const {
-  Class* throwable = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Throwable;");
-  return throwable->IsAssignableFrom(this);
+  return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this);
 }
 
 ClassLoader* Class::GetClassLoader() const {
@@ -1485,12 +1485,10 @@
 }
 
 bool Throwable::IsCheckedException() const {
-  Class* error = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Error;");
-  if (InstanceOf(error)) {
+  if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
     return false;
   }
-  Class* jlre = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/RuntimeException;");
-  return !InstanceOf(jlre);
+  return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException));
 }
 
 std::string Throwable::Dump() const {
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 8d649a6..903bbf9 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -609,18 +609,4 @@
   return klass;
 }
 
-void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
-  jmethodID jlre_UTE_constructor = env->GetMethodID(WellKnownClasses::java_lang_reflect_UndeclaredThrowableException, "<init>",
-                                                    "(Ljava/lang/Throwable;)V");
-  jthrowable jexception = AddLocalReference<jthrowable>(env, exception);
-  ScopedLocalRef<jthrowable> jlr_UTE(env,
-      reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_reflect_UndeclaredThrowableException,
-      jlre_UTE_constructor, jexception)));
-  int rc = env->Throw(jlr_UTE.get());
-  if (rc != JNI_OK) {
-    LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
-  }
-  CHECK(self->IsExceptionPending());
-}
-
 }  // namespace art
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 50b6735..d96cab9 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -214,8 +214,6 @@
   return class_linker->ResolveString(string_idx, referrer);
 }
 
-extern void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception);
-
 }  // namespace art
 
 #endif  // ART_SRC_RUNTIME_SUPPORT_H_
diff --git a/src/thread.cc b/src/thread.cc
index 5c221e8..d1458b2 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1440,6 +1440,11 @@
 }
 
 void Thread::ThrowNewException(const char* exception_class_descriptor, const char* msg) {
+  CHECK(!IsExceptionPending()); // Callers should either clear or call ThrowNewWrappedException.
+  ThrowNewWrappedException(exception_class_descriptor, msg);
+}
+
+void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg) {
   // Convert "Ljava/lang/Exception;" into JNI-style "java/lang/Exception".
   CHECK_EQ('L', exception_class_descriptor[0]);
   std::string descriptor(exception_class_descriptor + 1);
@@ -1447,6 +1452,9 @@
   descriptor.erase(descriptor.length() - 1);
 
   JNIEnv* env = GetJniEnv();
+  jobject cause = env->ExceptionOccurred();
+  env->ExceptionClear();
+
   ScopedLocalRef<jclass> exception_class(env, env->FindClass(descriptor.c_str()));
   if (exception_class.get() == NULL) {
     LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI FindClass failed: "
@@ -1473,7 +1481,7 @@
     }
     return;
   }
-  int rc = env->ThrowNew(exception_class.get(), msg);
+  int rc = ::art::ThrowNewException(env, exception_class.get(), msg, cause);
   if (rc != JNI_OK) {
     LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI ThrowNew failed: "
                << PrettyTypeOf(GetException());
diff --git a/src/thread.h b/src/thread.h
index 91ea568..951aa2a 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -247,9 +247,13 @@
     top_of_managed_stack_pc_ = pc;
   }
 
-  // 'msg' may be NULL.
+  // If 'msg' is NULL, no detail message is set.
   void ThrowNewException(const char* exception_class_descriptor, const char* msg);
 
+  // If 'msg' is NULL, no detail message is set. An exception must be pending, and will be
+  // used as the new exception's cause.
+  void ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg);
+
   void ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...)
       __attribute__((format(printf, 3, 4)));
 
diff --git a/src/well_known_classes.cc b/src/well_known_classes.cc
index c89771a..20e71d5 100644
--- a/src/well_known_classes.cc
+++ b/src/well_known_classes.cc
@@ -29,23 +29,26 @@
 jclass WellKnownClasses::java_lang_ClassNotFoundException;
 jclass WellKnownClasses::java_lang_Daemons;
 jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
 jclass WellKnownClasses::java_lang_reflect_InvocationHandler;
 jclass WellKnownClasses::java_lang_reflect_Method;
 jclass WellKnownClasses::java_lang_reflect_Proxy;
-jclass WellKnownClasses::java_lang_reflect_UndeclaredThrowableException;
+jclass WellKnownClasses::java_lang_RuntimeException;
 jclass WellKnownClasses::java_lang_Thread;
 jclass WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler;
 jclass WellKnownClasses::java_lang_ThreadGroup;
 jclass WellKnownClasses::java_lang_ThreadLock;
+jclass WellKnownClasses::java_lang_Throwable;
 jclass WellKnownClasses::java_nio_ReadWriteDirectByteBuffer;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
 
 jmethodID WellKnownClasses::com_android_dex_Dex_create;
 jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
+jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;
 jmethodID WellKnownClasses::java_lang_Daemons_requestHeapTrim;
 jmethodID WellKnownClasses::java_lang_Daemons_start;
+jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
+jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
 jmethodID WellKnownClasses::java_lang_reflect_InvocationHandler_invoke;
 jmethodID WellKnownClasses::java_lang_Thread_init;
 jmethodID WellKnownClasses::java_lang_Thread_run;
@@ -104,23 +107,30 @@
   java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
   java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
   java_lang_Error = CacheClass(env, "java/lang/Error");
-  java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
   java_lang_reflect_InvocationHandler = CacheClass(env, "java/lang/reflect/InvocationHandler");
   java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method");
   java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
-  java_lang_reflect_UndeclaredThrowableException = CacheClass(env, "java/lang/reflect/UndeclaredThrowableException");
+  java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
   java_lang_Thread = CacheClass(env, "java/lang/Thread");
   java_lang_Thread$UncaughtExceptionHandler = CacheClass(env, "java/lang/Thread$UncaughtExceptionHandler");
   java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
   java_lang_ThreadLock = CacheClass(env, "java/lang/ThreadLock");
+  java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
   java_nio_ReadWriteDirectByteBuffer = CacheClass(env, "java/nio/ReadWriteDirectByteBuffer");
   org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
   org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
 
   com_android_dex_Dex_create = CacheMethod(env, com_android_dex_Dex, true, "create", "(Ljava/nio/ByteBuffer;)Lcom/android/dex/Dex;");
   java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+  java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
   java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V");
   java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
+
+  ScopedLocalRef<jclass> java_lang_ref_FinalizerReference(env, env->FindClass("java/lang/ref/FinalizerReference"));
+  java_lang_ref_FinalizerReference_add = CacheMethod(env, java_lang_ref_FinalizerReference.get(), true, "add", "(Ljava/lang/Object;)V");
+  ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue"));
+  java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
+
   java_lang_reflect_InvocationHandler_invoke = CacheMethod(env, java_lang_reflect_InvocationHandler, false, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
   java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
diff --git a/src/well_known_classes.h b/src/well_known_classes.h
index 613c47e..6a6dd60 100644
--- a/src/well_known_classes.h
+++ b/src/well_known_classes.h
@@ -37,23 +37,26 @@
   static jclass java_lang_ClassNotFoundException;
   static jclass java_lang_Daemons;
   static jclass java_lang_Error;
-  static jclass java_lang_ExceptionInInitializerError;
   static jclass java_lang_reflect_InvocationHandler;
   static jclass java_lang_reflect_Method;
   static jclass java_lang_reflect_Proxy;
-  static jclass java_lang_reflect_UndeclaredThrowableException;
+  static jclass java_lang_RuntimeException;
   static jclass java_lang_Thread;
   static jclass java_lang_ThreadGroup;
   static jclass java_lang_ThreadLock;
   static jclass java_lang_Thread$UncaughtExceptionHandler;
+  static jclass java_lang_Throwable;
   static jclass java_nio_ReadWriteDirectByteBuffer;
   static jclass org_apache_harmony_dalvik_ddmc_Chunk;
   static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
 
   static jmethodID com_android_dex_Dex_create;
+  static jmethodID java_lang_ClassLoader_loadClass;
   static jmethodID java_lang_ClassNotFoundException_init;
   static jmethodID java_lang_Daemons_requestHeapTrim;
   static jmethodID java_lang_Daemons_start;
+  static jmethodID java_lang_ref_FinalizerReference_add;
+  static jmethodID java_lang_ref_ReferenceQueue_add;
   static jmethodID java_lang_reflect_InvocationHandler_invoke;
   static jmethodID java_lang_Thread_init;
   static jmethodID java_lang_Thread_run;