Move super class verification to class linker

Verify super classes in the class linker and chain exceptions when super
class verification fails. Add support for dumping chained exceptions to
Throwable::Dump. Improve verifier error detail messages.

Fixes problem where super class verification would leave a pending
exception that would be tripped over elsewhere.

Change-Id: I3ca850fc66674c8601132d7ec40bebbf9c108ae3
diff --git a/src/class_linker.cc b/src/class_linker.cc
index cccf0f6..51e5f3c 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1889,9 +1889,41 @@
   CHECK_EQ(klass->GetStatus(), Class::kStatusResolved) << PrettyClass(klass);
   klass->SetStatus(Class::kStatusVerifying);
 
+  // Verify super class
+  Class* super = klass->GetSuperClass();
+  std::string error_msg;
+  if (super != NULL) {
+    // Acquire lock to prevent races on verifying the super class
+    ObjectLock lock(super);
+
+    if (!super->IsVerified() && !super->IsErroneous()) {
+      Runtime::Current()->GetClassLinker()->VerifyClass(super);
+    }
+    if (!super->IsVerified()) {
+      error_msg = "Rejecting class ";
+      error_msg += PrettyDescriptor(klass);
+      error_msg += " that attempts to sub-class erroneous class ";
+      error_msg += PrettyDescriptor(super);
+      LOG(ERROR) << error_msg  << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+      Thread* self = Thread::Current();
+      SirtRef<Throwable> cause(self->GetException());
+      if (cause.get() != NULL) {
+        self->ClearException();
+      }
+      self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+      if (cause.get() != NULL) {
+        self->GetException()->SetCause(cause.get());
+      }
+      CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
+      klass->SetStatus(Class::kStatusError);
+      return;
+    }
+  }
+
   // Try to use verification information from oat file, otherwise do runtime verification
   const DexFile& dex_file = FindDexFile(klass->GetDexCache());
-  if (VerifyClassUsingOatFile(dex_file, klass) || verifier::DexVerifier::VerifyClass(klass)) {
+  if (VerifyClassUsingOatFile(dex_file, klass) ||
+      verifier::DexVerifier::VerifyClass(klass, error_msg)) {
     // Make sure all classes referenced by catch blocks are resolved
     ResolveClassExceptionHandlerTypes(dex_file, klass);
     klass->SetStatus(Class::kStatusVerified);
@@ -1899,12 +1931,12 @@
     CheckMethodsHaveGcMaps(klass);
   } else {
     LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(klass)
-        << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+        << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
+        << " because: " << error_msg;
     Thread* self = Thread::Current();
     CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
-    self->ThrowNewExceptionF("Ljava/lang/VerifyError;", "Verification of %s failed",
-        PrettyDescriptor(klass).c_str());
-    CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyClass(klass);
+    self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+    CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
     klass->SetStatus(Class::kStatusError);
   }
 }
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 31c2697..8776458 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -922,45 +922,41 @@
   }
 }
 
-bool DexVerifier::VerifyClass(const Class* klass) {
+bool DexVerifier::VerifyClass(const Class* klass, std::string& error) {
   if (klass->IsVerified()) {
     return true;
   }
   Class* super = klass->GetSuperClass();
   if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") {
-    LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " that has no super class";
+    error = "Verifier rejected class ";
+    error += PrettyDescriptor(klass);
+    error += " that has no super class";
     return false;
   }
-  if (super != NULL) {
-    // Acquire lock to prevent races on verifying the super class
-    ObjectLock lock(super);
-
-    if (!super->IsVerified() && !super->IsErroneous()) {
-      Runtime::Current()->GetClassLinker()->VerifyClass(super);
-    }
-    if (!super->IsVerified()) {
-      LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass)
-                 << " that attempts to sub-class corrupt class " << PrettyClass(super);
-      return false;
-    } else if (super->IsFinal()) {
-      LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass)
-                 << " that attempts to sub-class final class " << PrettyClass(super);
-      return false;
-    }
+  if (super != NULL && super->IsFinal()) {
+    error = "Verifier rejected class ";
+    error += PrettyDescriptor(klass);
+    error += " that attempts to sub-class final class ";
+    error += PrettyDescriptor(super);
+    return false;
   }
   for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
     Method* method = klass->GetDirectMethod(i);
     if (!VerifyMethod(method)) {
-      LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " due to bad method "
-                 << PrettyMethod(method, true);
+      error = "Verifier rejected class ";
+      error += PrettyDescriptor(klass);
+      error += " due to bad method ";
+      error += PrettyMethod(method, true);
       return false;
     }
   }
   for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
     Method* method = klass->GetVirtualMethod(i);
     if (!VerifyMethod(method)) {
-      LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " due to bad method "
-                 << PrettyMethod(method, true);
+      error = "Verifier rejected class ";
+      error += PrettyDescriptor(klass);
+      error += " due to bad method ";
+      error += PrettyMethod(method, true);
       return false;
     }
   }
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index cbdc7fd..0488ba3 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -826,7 +826,7 @@
 class DexVerifier {
  public:
   /* Verify a class. Returns "true" on success. */
-  static bool VerifyClass(const Class* klass);
+  static bool VerifyClass(const Class* klass, std::string& error);
   /*
    * Perform verification on a single method.
    *
diff --git a/src/dex_verifier_test.cc b/src/dex_verifier_test.cc
index 379e8c4..51fb1bc 100644
--- a/src/dex_verifier_test.cc
+++ b/src/dex_verifier_test.cc
@@ -33,7 +33,8 @@
     Class* klass = class_linker_->FindSystemClass(descriptor.c_str());
 
     // Verify the class
-    ASSERT_TRUE(DexVerifier::VerifyClass(klass));
+    std::string error_msg;
+    ASSERT_TRUE(DexVerifier::VerifyClass(klass, error_msg)) << error_msg;
   }
 
   void VerifyDexFile(const DexFile* dex, ClassLoader* class_loader) {
@@ -55,7 +56,8 @@
 TEST_F(DexVerifierTest, IntMath) {
   SirtRef<ClassLoader> class_loader(LoadDex("IntMath"));
   Class* klass = class_linker_->FindClass("LIntMath;", class_loader.get());
-  ASSERT_TRUE(DexVerifier::VerifyClass(klass));
+  std::string error_msg;
+  ASSERT_TRUE(DexVerifier::VerifyClass(klass, error_msg)) << error_msg;
 }
 
 TEST_F(DexVerifierTest, RegTypes_Primitives) {
diff --git a/src/object.cc b/src/object.cc
index 7f6e451..f90031a 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1337,6 +1337,13 @@
   return result;
 }
 
+void Throwable::SetCause(Throwable* cause) {
+  CHECK(cause != NULL);
+  CHECK(cause != this);
+  CHECK(GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false) == NULL);
+  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+}
+
 bool Throwable::IsCheckedException() const {
   Class* error = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Error;");
   if (InstanceOf(error)) {
@@ -1367,6 +1374,11 @@
       result += "\n";
     }
   }
+  Throwable* cause = GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false);
+  if (cause != NULL) {
+    result += "Caused by: ";
+    result += cause->Dump();
+  }
   return result;
 }
 
diff --git a/src/object.h b/src/object.h
index 1841042..e9556ef 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2273,6 +2273,10 @@
   }
   std::string Dump() const;
 
+  // This is a runtime version of initCause, you shouldn't use it if initCause may have been
+  // overridden. Also it asserts rather than throwing exceptions. Currently this is only used
+  // in cases like the verifier where the checks cannot fail and initCause isn't overridden.
+  void SetCause(Throwable* cause);
   bool IsCheckedException() const;
  private:
   Object* GetStackState() const {