ART: Fix re-throwing failures of non-convention errors
While it is convention that Throwable subclasses should have a
constructor with a String argument, that is not rigorously enforced.
So if a static initializer throws an error that omits that
constructor, we must not provide a message when trying to throw
again.
Bug: 20495321
Bug: 20497840
Change-Id: Ia4334fa24223750f90a8f2732f1eb1e738575e8d
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c344eb4..dc8bf2a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -92,8 +92,28 @@
va_end(args);
}
-static void ThrowEarlierClassFailure(mirror::Class* c)
+static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const char* descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = self->GetCurrentMethod(nullptr);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(method != nullptr ?
+ method->GetDeclaringClass()->GetClassLoader()
+ : nullptr));
+ mirror::Class* exception_class = class_linker->FindClass(self, descriptor, class_loader);
+
+ if (exception_class == nullptr) {
+ // No exc class ~ no <init>-with-string.
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ return false;
+ }
+
+ mirror::ArtMethod* exception_init_method =
+ exception_class->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V");
+ return exception_init_method != nullptr;
+}
+
+void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
@@ -112,9 +132,15 @@
} else {
if (c->GetVerifyErrorClass() != nullptr) {
// TODO: change the verifier to store an _instance_, with a useful detail message?
+ // It's possible the exception doesn't have a <init>(String).
std::string temp;
- self->ThrowNewException(c->GetVerifyErrorClass()->GetDescriptor(&temp),
- PrettyDescriptor(c).c_str());
+ const char* descriptor = c->GetVerifyErrorClass()->GetDescriptor(&temp);
+
+ if (HasInitWithString(self, this, descriptor)) {
+ self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str());
+ } else {
+ self->ThrowNewException(descriptor, nullptr);
+ }
} else {
self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());