Wrap certain exception types when loading an erroneous class.

Bug: 28787733
Change-Id: I22e1e4339a95706a0fb9c1474b360e9bde232539
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5d4bca1..86fef28 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -641,6 +641,12 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Throw the class initialization failure recorded when first trying to initialize the given
+  // class.
+  void ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_class_def = false)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_);
+
   struct DexCacheData {
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
@@ -1057,12 +1063,6 @@
   // 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.
-  void ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_class_def = false)
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!dex_lock_);
-
   bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 1515630..6f735aa 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -41,6 +41,23 @@
   if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
+  // If class is erroneous, throw the earlier failure, wrapped in certain cases. See b/28787733.
+  if (c != nullptr && c->IsErroneous()) {
+    cl->ThrowEarlierClassFailure(c);
+    Thread* self = soa.Self();
+    mirror::Class* eiie_class =
+        self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
+    mirror::Class* iae_class =
+        self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
+    mirror::Class* ncdfe_class =
+        self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
+    mirror::Class* exception = self->GetException()->GetClass();
+    if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
+      self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
+                                     PrettyDescriptor(c).c_str());
+    }
+    return nullptr;
+  }
   if (loader != nullptr) {
     // Try the common case.
     StackHandleScope<1> hs(soa.Self());
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index d288943..355d552 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -41,6 +41,9 @@
 jclass WellKnownClasses::java_lang_ClassNotFoundException;
 jclass WellKnownClasses::java_lang_Daemons;
 jclass WellKnownClasses::java_lang_Error;
+jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
+jclass WellKnownClasses::java_lang_IllegalAccessError;
+jclass WellKnownClasses::java_lang_NoClassDefFoundError;
 jclass WellKnownClasses::java_lang_Object;
 jclass WellKnownClasses::java_lang_OutOfMemoryError;
 jclass WellKnownClasses::java_lang_reflect_AbstractMethod;
@@ -228,6 +231,9 @@
   java_lang_Object = CacheClass(env, "java/lang/Object");
   java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
   java_lang_Error = CacheClass(env, "java/lang/Error");
+  java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
+  java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
+  java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
   java_lang_reflect_AbstractMethod = CacheClass(env, "java/lang/reflect/AbstractMethod");
   java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor");
   java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 482ff0a..cc60b4d 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -52,6 +52,9 @@
   static jclass java_lang_ClassNotFoundException;
   static jclass java_lang_Daemons;
   static jclass java_lang_Error;
+  static jclass java_lang_ExceptionInInitializerError;
+  static jclass java_lang_IllegalAccessError;
+  static jclass java_lang_NoClassDefFoundError;
   static jclass java_lang_Object;
   static jclass java_lang_OutOfMemoryError;
   static jclass java_lang_reflect_AbstractMethod;
diff --git a/test/142-classloader2/smali/B.smali b/test/142-classloader2/smali/B.smali
new file mode 100644
index 0000000..01bd593
--- /dev/null
+++ b/test/142-classloader2/smali/B.smali
@@ -0,0 +1,10 @@
+.class public LB;
+
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+  .registers 1
+  invoke-direct {p1}, Ljava/lang/Object;-><init>()V
+  return-void
+.end method
+
diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java
index 86c61eb..89dadce 100644
--- a/test/142-classloader2/src/Main.java
+++ b/test/142-classloader2/src/Main.java
@@ -71,6 +71,21 @@
             throw new IllegalStateException("Expected Ex-A, found " + exValue);
         }
 
+        // Try to load a dex file with bad dex code. Use new instance to force verification.
+        try {
+          Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+          badClass.newInstance();
+          System.out.println("Should not be able to load class from bad dex file.");
+        } catch (VerifyError e) {
+        }
+
+        // Make sure the same error is rethrown when reloading the bad class.
+        try {
+          Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+          System.out.println("Should not be able to load class from bad dex file.");
+        } catch (VerifyError e) {
+        }
+
         System.out.println("Everything OK.");
     }
 }