Class::SetStatus(kStatusError) now checks that an exception is pending and uses it to SetVerifyErrorClass

Change-Id: I02f4adc51ac6da88d4969655fa828f93941c4c0a
diff --git a/src/class_linker.cc b/src/class_linker.cc
index ac2f4f1..5687453 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -121,6 +121,7 @@
    */
   LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
 
+  CHECK(c->IsErroneous()) << PrettyClass(c);
   if (c->GetVerifyErrorClass() != NULL) {
     // TODO: change the verifier to store an _instance_, with a useful detail message?
     ClassHelper ve_ch(c->GetVerifyErrorClass());
@@ -1079,6 +1080,7 @@
     if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
       self->ThrowNewException("Ljava/lang/ClassCircularityError;",
           PrettyDescriptor(klass).c_str());
+      klass->SetStatus(Class::kStatusError);
       return NULL;
     }
     // Wait for the pending initialization to complete.
@@ -1159,7 +1161,7 @@
     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()) {
+    if (env->ExceptionCheck()) {
       // If the ClassLoader threw, pass that exception up.
       return NULL;
     } else if (result.get() == NULL) {
@@ -1208,6 +1210,7 @@
   // Check for a pending exception during load
   Thread* self = Thread::Current();
   if (self->IsExceptionPending()) {
+    klass->SetStatus(Class::kStatusError);
     return NULL;
   }
   ObjectLock lock(klass.get());
@@ -1224,7 +1227,6 @@
   CHECK(!klass->IsLoaded());
   if (!LoadSuperAndInterfaces(klass, dex_file)) {
     // Loading failed.
-    CHECK(self->IsExceptionPending());
     klass->SetStatus(Class::kStatusError);
     lock.NotifyAll();
     return NULL;
@@ -1234,7 +1236,6 @@
   CHECK(!klass->IsResolved());
   if (!LinkClass(klass, NULL)) {
     // Linking failed.
-    CHECK(self->IsExceptionPending());
     klass->SetStatus(Class::kStatusError);
     lock.NotifyAll();
     return NULL;
@@ -1902,8 +1903,9 @@
   CHECK(oat_class.get() != NULL) << descriptor;
   Class::Status status = oat_class->GetStatus();
   if (status == Class::kStatusError) {
-    ThrowEarlierClassFailure(klass);
-    klass->SetVerifyErrorClass(Thread::Current()->GetException()->GetClass());
+    // TODO: include appropriate verify_error_class_ information in oat file for use here.
+    ThrowNoClassDefFoundError("Class failed compile-time verification: %s",
+                              PrettyDescriptor(klass).c_str());
     klass->SetStatus(Class::kStatusError);
     return true;
   }
@@ -2002,7 +2004,7 @@
 
   // Link the fields and virtual methods, creating vtable and iftables
   if (!LinkClass(klass, interfaces)) {
-    DCHECK(Thread::Current()->IsExceptionPending());
+    klass->SetStatus(Class::kStatusError);
     return NULL;
   }
   sfield->SetObject(NULL, throws);    // initialize throws field
@@ -2156,7 +2158,6 @@
     }
 
     if (!ValidateSuperClassDescriptors(klass)) {
-      CHECK(self->IsExceptionPending());
       klass->SetStatus(Class::kStatusError);
       return false;
     }
@@ -2170,7 +2171,7 @@
   uint64_t t0 = NanoTime();
 
   if (!InitializeSuperClass(klass, can_run_clinit)) {
-    CHECK(self->IsExceptionPending());
+    CHECK(klass->IsErroneous());
     return false;
   }
 
@@ -2346,10 +2347,10 @@
       // TODO: check for a pending exception
       if (!super_initialized) {
         if (!can_run_clinit) {
-         // Don't set status to error when we can't run <clinit>.
-         CHECK_EQ(klass->GetStatus(), Class::kStatusInitializing) << PrettyClass(klass);
-         klass->SetStatus(Class::kStatusVerified);
-         return false;
+          // Don't set status to error when we can't run <clinit>.
+          CHECK_EQ(klass->GetStatus(), Class::kStatusInitializing) << PrettyClass(klass);
+          klass->SetStatus(Class::kStatusVerified);
+          return false;
         }
         klass->SetStatus(Class::kStatusError);
         klass->NotifyAll();
@@ -2370,7 +2371,7 @@
   ScopedThreadStateChange tsc(self, Thread::kRunnable);
   bool success = InitializeClass(c, can_run_clinit);
   if (!success) {
-    CHECK(self->IsExceptionPending());
+    CHECK(self->IsExceptionPending()) << PrettyClass(c);
   }
   return success;
 }
@@ -2496,13 +2497,12 @@
   }
   // Verify
   if (super->IsFinal() || super->IsInterface()) {
-    Thread* thread = Thread::Current();
-    thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+    Thread* self = Thread::Current();
+    self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
         "Superclass %s of %s is %s",
         PrettyDescriptor(super).c_str(),
         PrettyDescriptor(klass.get()).c_str(),
         super->IsFinal() ? "declared final" : "an interface");
-    klass->SetVerifyErrorClass(thread->GetException()->GetClass());
     return false;
   }
   if (!klass->CanAccess(super)) {
@@ -2663,12 +2663,11 @@
     DCHECK(interface != NULL);
     if (!interface->IsInterface()) {
       ClassHelper ih(interface);
-      Thread* thread = Thread::Current();
-      thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+      Thread* self = Thread::Current();
+      self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
           "Class %s implements non-interface class %s",
           PrettyDescriptor(klass.get()).c_str(),
           PrettyDescriptor(ih.GetDescriptor()).c_str());
-      klass->SetVerifyErrorClass(thread->GetException()->GetClass());
       return false;
     }
     // Check if interface is already in iftable
diff --git a/src/object.cc b/src/object.cc
index 7ead4ab..c16cdb0 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -597,6 +597,30 @@
   CHECK(new_status > GetStatus() || new_status == kStatusError || !Runtime::Current()->IsStarted())
       << PrettyClass(this) << " " << GetStatus() << " -> " << new_status;
   CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
+  if (new_status == kStatusError) {
+    CHECK_NE(GetStatus(), kStatusError) << PrettyClass(this);
+
+    // stash current exception
+    Thread* self = Thread::Current();
+    SirtRef<Throwable> exception(self->GetException());
+    CHECK(exception.get() != NULL);
+
+    // clear exception to call FindSystemClass
+    self->ClearException();
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
+    CHECK(!self->IsExceptionPending());
+
+    // only verification errors, not initialization problems, should set a verify error.
+    // this is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case.
+    Class* exception_class = exception->GetClass();
+    if (!eiie_class->IsAssignableFrom(exception_class)) {
+      SetVerifyErrorClass(exception_class);
+    }
+
+    // restore exception
+    self->SetException(exception.get());
+  }
   return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
 }
 
diff --git a/src/object.h b/src/object.h
index 3cbe9d6..1841042 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1708,10 +1708,6 @@
     return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false);
   }
 
-  void SetVerifyErrorClass(Class* klass) {
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
-  }
-
   uint16_t GetDexTypeIndex() const {
     return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), false);
   }
@@ -1721,6 +1717,11 @@
   }
 
  private:
+  void SetVerifyErrorClass(Class* klass) {
+    CHECK(klass != NULL) << PrettyClass(this);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+  }
+
   bool Implements(const Class* klass) const;
   bool IsArrayAssignableFromArray(const Class* klass) const;
   bool IsAssignableFromArray(const Class* klass) const;
diff --git a/test/068-classloader/src/FancyLoader.java b/test/068-classloader/src/FancyLoader.java
index 28c17a8..2c6fcde 100644
--- a/test/068-classloader/src/FancyLoader.java
+++ b/test/068-classloader/src/FancyLoader.java
@@ -35,10 +35,10 @@
  */
 public class FancyLoader extends ClassLoader {
     /* this is where the "alternate" .class files live */
-    static final String CLASS_PATH = "/data/art-test";
+    static final String CLASS_PATH = "classes-ex/";
 
     /* this is the "alternate" DEX/Jar file */
-    static final String DEX_FILE = "068-classloader-ex.jar";
+    static final String DEX_FILE = "/data/art-test/068-classloader-ex.jar";
 
     /* on Dalvik, this is a DexFile; otherwise, it's null */
     private Class mDexClass;
@@ -94,7 +94,7 @@
                 }
 
                 try {
-                    mDexFile = ctor.newInstance(CLASS_PATH + File.separator + DEX_FILE);
+                    mDexFile = ctor.newInstance(DEX_FILE);
                 } catch (InstantiationException ie) {
                     throw new ClassNotFoundException("newInstance failed", ie);
                 } catch (IllegalAccessException iae) {
diff --git a/test/068-classloader/src/Main.java b/test/068-classloader/src/Main.java
index 1bc7b04..7dfb6f5 100644
--- a/test/068-classloader/src/Main.java
+++ b/test/068-classloader/src/Main.java
@@ -99,7 +99,7 @@
 
         try {
             altClass = loader.loadClass("Inaccessible2");
-            System.err.println("ERROR: Inaccessible2 was accessible");
+            System.err.println("ERROR: Inaccessible2 was accessible: " + altClass);
         } catch (ClassNotFoundException cnfe) {
             Throwable cause = cnfe.getCause();
             if (cause instanceof IllegalAccessError) {
@@ -119,7 +119,7 @@
 
         try {
             altClass = loader.loadClass("Inaccessible3");
-            System.err.println("ERROR: Inaccessible3 was accessible");
+            System.err.println("ERROR: Inaccessible3 was accessible: " + altClass);
         } catch (ClassNotFoundException cnfe) {
             Throwable cause = cnfe.getCause();
             if (cause instanceof IllegalAccessError) {