Improve exceptions thrown during class loading.
We've always gone to a lot of trouble to have good detail messages when
something goes wrong during class loading, but none of those exceptions
would ever make it to the calling code. This adds missing exception
chaining, and also adds a test for the case where a subclass is missing
its superclass; before we'd have reported that the subclass was missing,
but now we make it clear that the real error was an inability to find
the superclass.
Change-Id: I07ebc011ccdaed16be82bf08b323393e1d790989
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 3487af1..3b5ac5d 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1093,6 +1093,8 @@
return EnsureResolved(klass);
}
// Class is not yet loaded.
+ JNIEnv* env = self->GetJniEnv();
+ ScopedLocalRef<jthrowable> cause(env, NULL);
if (descriptor[0] == '[') {
return CreateArrayClass(descriptor, class_loader);
@@ -1122,7 +1124,6 @@
} else {
std::string class_name_string(DescriptorToDot(descriptor));
ScopedThreadStateChange(self, Thread::kNative);
- JNIEnv* env = self->GetJniEnv();
ScopedLocalRef<jclass> c(env, AddLocalReference<jclass>(env, GetClassRoot(kJavaLangClassLoader)));
CHECK(c.get() != NULL);
// TODO: cache method?
@@ -1135,9 +1136,10 @@
ScopedLocalRef<jobject> class_loader_object(env, AddLocalReference<jobject>(env, class_loader));
ScopedLocalRef<jobject> result(env, env->CallObjectMethod(class_loader_object.get(), mid,
class_name_object.get()));
- if (env->ExceptionOccurred()) {
- env->ExceptionClear(); // Failed to find class fall-through to NCDFE
- // TODO: initialize the cause of the NCDFE to this exception
+ cause.reset(env->ExceptionOccurred());
+ if (cause.get() != NULL) {
+ env->ExceptionClear();
+ // Failed to find class, so fall-through to throw NCDFE.
} else if (result.get() == NULL) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException("ClassLoader.loadClass returned null for %s",
@@ -1150,6 +1152,15 @@
}
ThrowNoClassDefFoundError("Class %s not found", PrintableString(StringPiece(descriptor)).c_str());
+ if (cause.get() != NULL) {
+ // 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;");
+ env->CallObjectMethod(ncdfe.get(), initCause_mid, cause.get());
+ env->Throw(ncdfe.get());
+ }
return NULL;
}
@@ -1449,8 +1460,6 @@
dst->SetDexCacheResolvedFields(klass->GetDexCache()->GetResolvedFields());
dst->SetDexCacheCodeAndDirectMethods(klass->GetDexCache()->GetCodeAndDirectMethods());
dst->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage());
-
- // TODO: check for finalize method
}
void ClassLinker::AppendToBootClassPath(const DexFile& dex_file) {