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());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8e27413..1bd9f0a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -657,6 +657,12 @@
   // Return the quick generic JNI stub for testing.
   const void* GetRuntimeQuickGenericJniStub() const;
 
+  // Throw the class initialization failure recorded when first trying to initialize the given
+  // class.
+  // Note: Currently we only store the descriptor, so we cannot throw the exact throwable, only
+  //       a recreation with a custom string.
+  void ThrowEarlierClassFailure(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   std::vector<const DexFile*> boot_class_path_;
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;