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) {
diff --git a/src/dalvik_system_DexFile.cc b/src/dalvik_system_DexFile.cc
index a14f124..85948f0 100644
--- a/src/dalvik_system_DexFile.cc
+++ b/src/dalvik_system_DexFile.cc
@@ -190,7 +190,7 @@
// method says we simply return null if the class is not found.
static const char* ignored_exception_classes[] = {
"java/lang/ClassNotFoundException",
- "java/lang/NoClassDefFoundError"
+// "java/lang/NoClassDefFoundError"
};
bool clear_exception = false;
for (size_t i = 0; i < arraysize(ignored_exception_classes); i++) {
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 0a5a9ed..96eec2f 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -51,11 +51,19 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* c = class_linker->FindClass(descriptor.c_str(), class_loader);
if (c == NULL) {
- // Convert NoClassDefFoundError to ClassNotFoundException
- // TODO: chain exceptions?
- DCHECK(env->ExceptionCheck());
+ // Convert NoClassDefFoundError to ClassNotFoundException.
+ ScopedLocalRef<jthrowable> ncdfe(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());
return NULL;
}
if (initialize) {
diff --git a/test/003-omnibus-opcodes/expected.txt b/test/003-omnibus-opcodes/expected.txt
index 28b1813..746e702 100644
--- a/test/003-omnibus-opcodes/expected.txt
+++ b/test/003-omnibus-opcodes/expected.txt
@@ -68,6 +68,32 @@
UnresTest1...
UnresTest1...
UnresTest2...
+java.lang.NoClassDefFoundError: Class "LUnresClass;" not found
+ at UnresTest2.run(UnresTest2.java:33)
+ at Main.run(Main.java:64)
+ at Main.main(Main.java:26)
+Caused by: java.lang.ClassNotFoundException: Didn't find class "UnresClass" on path: /data/art-test/003-omnibus-opcodes.jar
+ at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:509)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
+ ... 3 more
+java.lang.NoClassDefFoundError: Class "LUnresClassSubclass;" not found
+ at UnresTest2.run(UnresTest2.java:41)
+ at Main.run(Main.java:64)
+ at Main.main(Main.java:26)
+Caused by: java.lang.NoClassDefFoundError: Class "LUnresClass;" not found
+ at dalvik.system.DexFile.defineClass(Native Method)
+ at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:213)
+ at dalvik.system.DexPathList.findClass(DexPathList.java:315)
+ at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:58)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:509)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
+ ... 3 more
+Caused by: java.lang.ClassNotFoundException: Didn't find class "UnresClass" on path: /data/art-test/003-omnibus-opcodes.jar
+ at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:509)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
+ ... 9 more
UnresTest2 done
InternedString.run
Done!
diff --git a/test/003-omnibus-opcodes/src/UnresClassSubclass.java b/test/003-omnibus-opcodes/src/UnresClassSubclass.java
new file mode 100644
index 0000000..601f2f5
--- /dev/null
+++ b/test/003-omnibus-opcodes/src/UnresClassSubclass.java
@@ -0,0 +1,4 @@
+public class UnresClassSubclass extends UnresClass {
+ public UnresClassSubclass() {
+ }
+}
diff --git a/test/003-omnibus-opcodes/src/UnresTest2.java b/test/003-omnibus-opcodes/src/UnresTest2.java
index f1e8dd1..c94f226 100644
--- a/test/003-omnibus-opcodes/src/UnresTest2.java
+++ b/test/003-omnibus-opcodes/src/UnresTest2.java
@@ -33,10 +33,19 @@
un = new UnresClass();
Main.assertTrue(false);
} catch (NoClassDefFoundError ncdfe) {
+ ncdfe.printStackTrace();
// good
}
try {
+ new UnresClassSubclass();
+ Main.assertTrue(false);
+ } catch (NoClassDefFoundError ncdfe) {
+ ncdfe.printStackTrace();
+ // good
+ }
+
+ try {
UnresClass[] uar = new UnresClass[3];
Main.assertTrue(false);
} catch (NoClassDefFoundError ncdfe) {