OSDN Git Service

Add class status for resolved erroneous classes.
authorVladimir Marko <vmarko@google.com>
Fri, 20 Jan 2017 19:32:50 +0000 (19:32 +0000)
committerVladimir Marko <vmarko@google.com>
Tue, 24 Jan 2017 12:05:02 +0000 (12:05 +0000)
Split the old ambiguous status mirror::Class::kStatusError
into kStatusErrorUnresolved and kStatusErrorResolved. Once
a class has been resolved, IsResolved() shall return true
even if the class later becomes erroneous. Allow returning
erroneous class from ClassLinker::EnsureResolved() if it has
been previously resolved. This allows consistent behavior
for retrieving classes, immune to multi-threaded races and
multi-dex weirdness. It also allows JVMTI to properly report
"prepared" (i.e. resolved) classes that are also erroneous.

The new behavior is consistent with the RI.

Add regression tests to 008-exceptions for inconsistent
behavior for multi-dex retrieval of erroneous resolved class
(wrapping or not wrapping the old exception based on which
dex file is used for lookup) and for a CHECK(IsResolved())
crash in ClassLinker::LoadSuperAndInterfaces() (without any
tests for similar checks that could have previously failed
only due to extremely unlikely race conditions; these should
now also be fixed).

Inconsistency still remains for class verification as shown
by the new exceptionsForSuperClassInitFailure() test in
008-exceptions, where interpreter and Optimizing still
cause different exceptions to be thrown.

Note: This is partially changing behavior implemented for
bug 28787733. Since we allow the class loader to retrieve an
erroneous resolved class, the ExceptionInInitializerError is
not thrown at all from VMClassLoader_findLoadedClass(), so
there is nothing to wrap in ClassNotFoundException.

Test: m test-art-host
Bug: 30627598
Bug: 28787733
Change-Id: I86cdca00f35a0d6221d2559e3026ac0428a3613c

27 files changed:
build/Android.gtest.mk
compiler/driver/compiler_driver.cc
compiler/image_writer.cc
compiler/oat_writer.cc
runtime/art_field-inl.h
runtime/art_method-inl.h
runtime/class_linker.cc
runtime/class_linker_test.cc
runtime/hprof/hprof.cc
runtime/image.cc
runtime/instrumentation.cc
runtime/mirror/class.cc
runtime/mirror/class.h
runtime/native/java_lang_VMClassLoader.cc
runtime/oat_file.h
runtime/well_known_classes.cc
runtime/well_known_classes.h
test/008-exceptions/expected.txt
test/008-exceptions/multidex.jpp [new file with mode: 0644]
test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java [new file with mode: 0644]
test/008-exceptions/src/Main.java
test/008-exceptions/src/MultiDexBadInit.java [new file with mode: 0644]
test/008-exceptions/src/MultiDexBadInitWrapper1.java [new file with mode: 0644]
test/142-classloader2/expected.txt
test/142-classloader2/src/Main.java
test/912-classes/expected.txt
test/ErroneousInit/ErroneousInit.java [new file with mode: 0644]

index c87075f..0aedf71 100644 (file)
@@ -28,6 +28,7 @@ GTEST_DEX_DIRECTORIES := \
   DexToDexDecompiler \
   ErroneousA \
   ErroneousB \
+  ErroneousInit \
   ExceptionHandle \
   GetMethodSignature \
   ImageLayoutA \
@@ -87,7 +88,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 
 ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_class_table_test_DEX_DEPS := XandY
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
index c03ffca..b00d083 100644 (file)
@@ -2199,7 +2199,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor {
     if (klass.Get() != nullptr) {
       // Only do this if the class is resolved. If even resolution fails, quickening will go very,
       // very wrong.
-      if (klass->IsResolved()) {
+      if (klass->IsResolved() && !klass->IsErroneousResolved()) {
         if (klass->GetStatus() < mirror::Class::kStatusVerified) {
           ObjectLock<mirror::Class> lock(soa.Self(), klass);
           // Set class status to verified.
@@ -2626,7 +2626,8 @@ CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const {
 void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
   switch (status) {
     case mirror::Class::kStatusNotReady:
-    case mirror::Class::kStatusError:
+    case mirror::Class::kStatusErrorResolved:
+    case mirror::Class::kStatusErrorUnresolved:
     case mirror::Class::kStatusRetryVerificationAtRuntime:
     case mirror::Class::kStatusVerified:
     case mirror::Class::kStatusInitialized:
index 459aca3..15e4cd8 100644 (file)
@@ -756,7 +756,7 @@ bool ImageWriter::PruneAppImageClassInternal(
   bool my_early_exit = false;  // Only for ourselves, ignore caller.
   // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
   // app image.
-  if (klass->GetStatus() == mirror::Class::kStatusError) {
+  if (klass->IsErroneous()) {
     result = true;
   } else {
     ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
@@ -777,8 +777,8 @@ bool ImageWriter::PruneAppImageClassInternal(
                                                   visited);
   }
   // Check static fields and their classes.
-  size_t num_static_fields = klass->NumReferenceStaticFields();
-  if (num_static_fields != 0 && klass->IsResolved()) {
+  if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) {
+    size_t num_static_fields = klass->NumReferenceStaticFields();
     // Presumably GC can happen when we are cross compiling, it should not cause performance
     // problems to do pointer size logic.
     MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset(
@@ -1154,7 +1154,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
       // Visit and assign offsets for fields and field arrays.
       mirror::Class* as_klass = obj->AsClass();
       mirror::DexCache* dex_cache = as_klass->GetDexCache();
-      DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError);
+      DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
       if (compile_app_image_) {
         // Extra sanity, no boot loader classes should be left!
         CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
index de5af97..bd2c5e3 100644 (file)
@@ -716,7 +716,10 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
     if (compiled_class != nullptr) {
       status = compiled_class->GetStatus();
     } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
-      status = mirror::Class::kStatusError;
+      // The oat class status is used only for verification of resolved classes,
+      // so use kStatusErrorResolved whether the class was resolved or unresolved
+      // during compile-time verification.
+      status = mirror::Class::kStatusErrorResolved;
     } else {
       status = mirror::Class::kStatusNotReady;
     }
index b9f688d..917db3e 100644 (file)
@@ -52,7 +52,7 @@ inline uint32_t ArtField::GetAccessFlags() {
 }
 
 inline MemberOffset ArtField::GetOffset() {
-  DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous());
+  DCHECK(GetDeclaringClass()->IsResolved());
   return MemberOffset(offset_);
 }
 
index 15938c5..a35c7ab 100644 (file)
@@ -109,8 +109,7 @@ inline uint32_t ArtMethod::GetAccessFlags() {
 }
 
 inline uint16_t ArtMethod::GetMethodIndex() {
-  DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved() ||
-         GetDeclaringClass()->IsErroneous());
+  DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved());
   return method_index_;
 }
 
index b8ed530..e65672a 100644 (file)
@@ -1340,7 +1340,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
           // The image space is not yet added to the heap, avoid read barriers.
           ObjPtr<mirror::Class> klass = types[j].Read();
           if (space->HasAddress(klass.Ptr())) {
-            DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+            DCHECK(!klass->IsErroneous()) << klass->GetStatus();
             auto it = new_class_set->Find(ClassTable::TableSlot(klass));
             DCHECK(it != new_class_set->end());
             DCHECK_EQ(it->Read(), klass);
@@ -1703,7 +1703,7 @@ bool ClassLinker::AddImageSpace(
       for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
         ObjPtr<mirror::Class> klass = types[j].Read();
         if (klass != nullptr) {
-          DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+          DCHECK(!klass->IsErroneous()) << klass->GetStatus();
         }
       }
     } else {
@@ -2232,7 +2232,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
   // For temporary classes we must wait for them to be retired.
   if (init_done_ && klass->IsTemp()) {
     CHECK(!klass->IsResolved());
-    if (klass->IsErroneous()) {
+    if (klass->IsErroneousUnresolved()) {
       ThrowEarlierClassFailure(klass);
       return nullptr;
     }
@@ -2240,10 +2240,10 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
     Handle<mirror::Class> h_class(hs.NewHandle(klass));
     ObjectLock<mirror::Class> lock(self, h_class);
     // Loop and wait for the resolving thread to retire this class.
-    while (!h_class->IsRetired() && !h_class->IsErroneous()) {
+    while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) {
       lock.WaitIgnoringInterrupts();
     }
-    if (h_class->IsErroneous()) {
+    if (h_class->IsErroneousUnresolved()) {
       ThrowEarlierClassFailure(h_class.Get());
       return nullptr;
     }
@@ -2258,7 +2258,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
   static const size_t kNumYieldIterations = 1000;
   // How long each sleep is in us.
   static const size_t kSleepDurationUS = 1000;  // 1 ms.
-  while (!klass->IsResolved() && !klass->IsErroneous()) {
+  while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) {
     StackHandleScope<1> hs(self);
     HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
     {
@@ -2269,7 +2269,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
         // Check for circular dependencies between classes, the lock is required for SetStatus.
         if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
           ThrowClassCircularityError(h_class.Get());
-          mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
+          mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self);
           return nullptr;
         }
       }
@@ -2286,7 +2286,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
     ++index;
   }
 
-  if (klass->IsErroneous()) {
+  if (klass->IsErroneousUnresolved()) {
     ThrowEarlierClassFailure(klass);
     return nullptr;
   }
@@ -2687,7 +2687,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
     // An exception occured during load, set status to erroneous while holding klass' lock in case
     // notification is necessary.
     if (!klass->IsErroneous()) {
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
     }
     return nullptr;
   }
@@ -2697,7 +2697,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
   if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
     // Loading failed.
     if (!klass->IsErroneous()) {
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
     }
     return nullptr;
   }
@@ -2716,13 +2716,13 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
   if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
     // Linking failed.
     if (!klass->IsErroneous()) {
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
     }
     return nullptr;
   }
   self->AssertNoPendingException();
   CHECK(h_new_class.Get() != nullptr) << descriptor;
-  CHECK(h_new_class->IsResolved()) << descriptor;
+  CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
 
   // Instrumentation may have updated entrypoints for all methods of all
   // classes. However it could not update methods of this class while we
@@ -3817,7 +3817,7 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
   }
   // Need to grab the lock to change status.
   ObjectLock<mirror::Class> super_lock(self, klass);
-  mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+  mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
   return false;
 }
 
@@ -3939,8 +3939,8 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
   bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
   // If the oat file says the class had an error, re-run the verifier. That way we will get a
   // precise error message. To ensure a rerun, test:
-  //     oat_file_class_status == mirror::Class::kStatusError => !preverified
-  DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
+  //     mirror::Class::IsErroneous(oat_file_class_status) => !preverified
+  DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified);
 
   std::string error_msg;
   verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -3998,7 +3998,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
                   << " because: " << error_msg;
     self->AssertNoPendingException();
     ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
-    mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+    mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
   }
   if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
     // Class is verified so we don't need to do any access check on its methods.
@@ -4089,7 +4089,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
     // at compile time).
     return false;
   }
-  if (oat_file_class_status == mirror::Class::kStatusError) {
+  if (mirror::Class::IsErroneous(oat_file_class_status)) {
     // Compile time verification failed with a hard error. This is caused by invalid instructions
     // in the class. These errors are unrecoverable.
     return false;
@@ -4248,7 +4248,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
     Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
         hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
     if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
       return nullptr;
     }
   }
@@ -4463,7 +4463,8 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
       return false;
     }
 
-    CHECK(klass->IsResolved()) << klass->PrettyClass() << ": state=" << klass->GetStatus();
+    CHECK(klass->IsResolved() && !klass->IsErroneousResolved())
+        << klass->PrettyClass() << ": state=" << klass->GetStatus();
 
     if (!klass->IsVerified()) {
       VerifyClass(self, klass);
@@ -4498,7 +4499,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
       // A separate thread could have moved us all the way to initialized. A "simple" example
       // involves a subclass of the current class being initialized at the same time (which
       // will implicitly initialize the superclass, if scheduled that way). b/28254258
-      DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+      DCHECK(!klass->IsErroneous()) << klass->GetStatus();
       if (klass->IsInitialized()) {
         return true;
       }
@@ -4525,7 +4526,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
     }
 
     if (!ValidateSuperClassDescriptors(klass)) {
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       return false;
     }
     self->AllowThreadSuspension();
@@ -4561,7 +4562,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
             << (self->GetException() != nullptr ? self->GetException()->Dump() : "");
         ObjectLock<mirror::Class> lock(self, klass);
         // Initialization failed because the super-class is erroneous.
-        mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+        mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
         return false;
       }
     }
@@ -4592,7 +4593,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
         if (!iface_initialized) {
           ObjectLock<mirror::Class> lock(self, klass);
           // Initialization failed because one of our interfaces with default methods is erroneous.
-          mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+          mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
           return false;
         }
       }
@@ -4665,7 +4666,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
 
     if (self->IsExceptionPending()) {
       WrapExceptionInInitializer(klass);
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       success = false;
     } else if (Runtime::Current()->IsTransactionAborted()) {
       // The exception thrown when the transaction aborted has been caught and cleared
@@ -4674,7 +4675,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
                      << mirror::Class::PrettyDescriptor(klass.Get())
                      << " without exception while transaction was aborted: re-throw it now.";
       Runtime::Current()->ThrowTransactionAbortError(self);
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       success = false;
     } else {
       RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -4758,7 +4759,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
     // we were not using WaitIgnoringInterrupts), bail out.
     if (self->IsExceptionPending()) {
       WrapExceptionInInitializer(klass);
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       return false;
     }
     // Spurious wakeup? Go back to waiting.
@@ -5169,7 +5170,7 @@ bool ClassLinker::LinkClass(Thread* self,
     klass->SetIFieldsPtrUnchecked(nullptr);
     if (UNLIKELY(h_new_class.Get() == nullptr)) {
       self->AssertPendingOOMException();
-      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
       return false;
     }
 
@@ -7781,7 +7782,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
       }
     }
   }
-  DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous())
+  DCHECK((resolved == nullptr) || resolved->IsResolved())
       << resolved->PrettyDescriptor() << " " << resolved->GetStatus();
   return resolved.Ptr();
 }
@@ -8516,13 +8517,12 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo
       }
       ++num_resolved;
       DCHECK(!klass->IsProxyClass());
-      if (!klass->IsResolved()) {
-        DCHECK(klass->IsErroneous());
+      DCHECK(klass->IsResolved());
+      if (klass->IsErroneousResolved()) {
         continue;
       }
       ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache();
       if (klass_dex_cache == dex_cache) {
-        DCHECK(klass->IsResolved());
         CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs);
         class_set.insert(klass->GetDexTypeIndex());
       }
index 7b6c0dc..026afdc 100644 (file)
@@ -87,6 +87,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
     EXPECT_FALSE(primitive->IsErroneous());
     EXPECT_TRUE(primitive->IsLoaded());
     EXPECT_TRUE(primitive->IsResolved());
+    EXPECT_FALSE(primitive->IsErroneousResolved());
     EXPECT_TRUE(primitive->IsVerified());
     EXPECT_TRUE(primitive->IsInitialized());
     EXPECT_FALSE(primitive->IsArrayInstance());
@@ -125,6 +126,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
     EXPECT_FALSE(JavaLangObject->IsErroneous());
     EXPECT_TRUE(JavaLangObject->IsLoaded());
     EXPECT_TRUE(JavaLangObject->IsResolved());
+    EXPECT_FALSE(JavaLangObject->IsErroneousResolved());
     EXPECT_TRUE(JavaLangObject->IsVerified());
     EXPECT_TRUE(JavaLangObject->IsInitialized());
     EXPECT_FALSE(JavaLangObject->IsArrayInstance());
@@ -199,6 +201,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
     EXPECT_FALSE(array->IsErroneous());
     EXPECT_TRUE(array->IsLoaded());
     EXPECT_TRUE(array->IsResolved());
+    EXPECT_FALSE(array->IsErroneousResolved());
     EXPECT_TRUE(array->IsVerified());
     EXPECT_TRUE(array->IsInitialized());
     EXPECT_FALSE(array->IsArrayInstance());
@@ -270,6 +273,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
     EXPECT_TRUE(klass->GetDexCache() != nullptr);
     EXPECT_TRUE(klass->IsLoaded());
     EXPECT_TRUE(klass->IsResolved());
+    EXPECT_FALSE(klass->IsErroneousResolved());
     EXPECT_FALSE(klass->IsErroneous());
     EXPECT_FALSE(klass->IsArrayClass());
     EXPECT_TRUE(klass->GetComponentType() == nullptr);
@@ -857,6 +861,7 @@ TEST_F(ClassLinkerTest, FindClass) {
   EXPECT_FALSE(MyClass->IsErroneous());
   EXPECT_TRUE(MyClass->IsLoaded());
   EXPECT_TRUE(MyClass->IsResolved());
+  EXPECT_FALSE(MyClass->IsErroneousResolved());
   EXPECT_FALSE(MyClass->IsVerified());
   EXPECT_FALSE(MyClass->IsInitialized());
   EXPECT_FALSE(MyClass->IsArrayInstance());
@@ -941,6 +946,47 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) {
       array_klass);
 }
 
+TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("ErroneousInit"))));
+  AssertNonExistentClass("LErroneousInit;");
+  Handle<mirror::Class> klass =
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LErroneousInit;", class_loader));
+  ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr));
+  dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
+  Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache());
+  const DexFile& dex_file = klass->GetDexFile();
+  EXPECT_OBJ_PTR_EQ(
+      class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+      klass.Get());
+  // Zero out the resolved type and make sure LookupResolvedType still finds it.
+  dex_cache->SetResolvedType(type_idx, nullptr);
+  EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+  EXPECT_OBJ_PTR_EQ(
+      class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+      klass.Get());
+  // Force initialization to turn the class erroneous.
+  bool initialized = class_linker_->EnsureInitialized(soa.Self(),
+                                                      klass,
+                                                      /* can_init_fields */ true,
+                                                      /* can_init_parents */ true);
+  EXPECT_FALSE(initialized);
+  EXPECT_TRUE(soa.Self()->IsExceptionPending());
+  soa.Self()->ClearException();
+  // Check that the LookupResolvedType() can still find the resolved type.
+  EXPECT_OBJ_PTR_EQ(
+      class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+      klass.Get());
+  // Zero out the resolved type and make sure LookupResolvedType() still finds it.
+  dex_cache->SetResolvedType(type_idx, nullptr);
+  EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+  EXPECT_OBJ_PTR_EQ(
+      class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+      klass.Get());
+}
+
 TEST_F(ClassLinkerTest, LibCore) {
   ScopedObjectAccess soa(Thread::Current());
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
index fe6a6e9..3d3ad59 100644 (file)
@@ -1169,7 +1169,7 @@ void Hprof::DumpHeapObject(mirror::Object* obj) {
 }
 
 void Hprof::DumpHeapClass(mirror::Class* klass) {
-  if (!klass->IsResolved() && !klass->IsErroneous()) {
+  if (!klass->IsResolved()) {
     // Class is allocated but not yet resolved: we cannot access its fields or super class.
     return;
   }
index 6d88895..54b099e 100644 (file)
@@ -25,7 +25,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '5', '\0' };  // ArtMethod update
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' };  // Erroneous resolved class.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
index 4ea1130..bbd6d35 100644 (file)
@@ -88,11 +88,11 @@ Instrumentation::Instrumentation()
 }
 
 void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
-  if (klass->IsErroneous()) {
-    // We can't execute code in a erroneous class: do nothing.
-  } else if (!klass->IsResolved()) {
+  if (!klass->IsResolved()) {
     // We need the class to be resolved to install/uninstall stubs. Otherwise its methods
     // could not be initialized or linked with regards to class inheritance.
+  } else if (klass->IsErroneousResolved()) {
+    // We can't execute code in a erroneous class: do nothing.
   } else {
     for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
       InstallStubsForMethod(&method);
index 9964b73..f08d4da 100644 (file)
@@ -115,7 +115,9 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized();
   if (LIKELY(class_linker_initialized)) {
-    if (UNLIKELY(new_status <= old_status && new_status != kStatusError &&
+    if (UNLIKELY(new_status <= old_status &&
+                 new_status != kStatusErrorUnresolved &&
+                 new_status != kStatusErrorResolved &&
                  new_status != kStatusRetired)) {
       LOG(FATAL) << "Unexpected change back of class status for " << h_this->PrettyClass()
                  << " " << old_status << " -> " << new_status;
@@ -127,10 +129,12 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
             << h_this->PrettyClass() << " " << old_status << " -> " << new_status;
     }
   }
-  if (UNLIKELY(new_status == kStatusError)) {
-    CHECK_NE(h_this->GetStatus(), kStatusError)
+  if (UNLIKELY(IsErroneous(new_status))) {
+    CHECK(!h_this->IsErroneous())
         << "Attempt to set as erroneous an already erroneous class "
-        << h_this->PrettyClass();
+        << h_this->PrettyClass()
+        << " old_status: " << old_status << " new_status: " << new_status;
+    CHECK_EQ(new_status == kStatusErrorResolved, old_status >= kStatusResolved);
     if (VLOG_IS_ON(class_linker)) {
       LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous.";
       if (self->IsExceptionPending()) {
@@ -177,7 +181,7 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
       // Class is a temporary one, ensure that waiters for resolution get notified of retirement
       // so that they can grab the new version of the class from the class linker's table.
       CHECK_LT(new_status, kStatusResolved) << h_this->PrettyDescriptor();
-      if (new_status == kStatusRetired || new_status == kStatusError) {
+      if (new_status == kStatusRetired || new_status == kStatusErrorUnresolved) {
         h_this->NotifyAll(self);
       }
     } else {
@@ -305,7 +309,7 @@ void Class::DumpClass(std::ostream& os, int flags) {
     }
     if (h_this->NumStaticFields() > 0) {
       os << "  static fields (" << h_this->NumStaticFields() << " entries):\n";
-      if (h_this->IsResolved() || h_this->IsErroneous()) {
+      if (h_this->IsResolved()) {
         for (size_t i = 0; i < h_this->NumStaticFields(); ++i) {
           os << StringPrintf("    %2zd: %s\n", i,
                              ArtField::PrettyField(h_this->GetStaticField(i)).c_str());
@@ -316,7 +320,7 @@ void Class::DumpClass(std::ostream& os, int flags) {
     }
     if (h_this->NumInstanceFields() > 0) {
       os << "  instance fields (" << h_this->NumInstanceFields() << " entries):\n";
-      if (h_this->IsResolved() || h_this->IsErroneous()) {
+      if (h_this->IsResolved()) {
         for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) {
           os << StringPrintf("    %2zd: %s\n", i,
                              ArtField::PrettyField(h_this->GetInstanceField(i)).c_str());
index fb2792a..7f6aa12 100644 (file)
@@ -84,6 +84,13 @@ class MANAGED Class FINAL : public Object {
   // will be gc'ed once all refs to the class point to the newly
   // cloned version.
   //
+  // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need
+  // to distinguish between classes that have been resolved and classes that
+  // have not. This is important because the const-class instruction needs to
+  // return a previously resolved class even if its subsequent initialization
+  // failed. We also need this to decide whether to wrap a previous
+  // initialization failure in ClassDefNotFound error or not.
+  //
   // kStatusNotReady: If a Class cannot be found in the class table by
   // FindClass, it allocates an new one with AllocClass in the
   // kStatusNotReady and calls LoadClass. Note if it does find a
@@ -119,8 +126,9 @@ class MANAGED Class FINAL : public Object {
   //
   // TODO: Explain the other states
   enum Status {
-    kStatusRetired = -2,  // Retired, should not be used. Use the newly cloned one instead.
-    kStatusError = -1,
+    kStatusRetired = -3,  // Retired, should not be used. Use the newly cloned one instead.
+    kStatusErrorResolved = -2,
+    kStatusErrorUnresolved = -1,
     kStatusNotReady = 0,
     kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
     kStatusLoaded = 2,  // DEX idx values resolved.
@@ -158,8 +166,25 @@ class MANAGED Class FINAL : public Object {
 
   // Returns true if the class has failed to link.
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsErroneousUnresolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetStatus<kVerifyFlags>() == kStatusErrorUnresolved;
+  }
+
+  // Returns true if the class has failed to initialize.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsErroneousResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetStatus<kVerifyFlags>() == kStatusErrorResolved;
+  }
+
+  // Returns true if the class status indicets that the class has failed to link or initialize.
+  static bool IsErroneous(Status status) {
+    return status == kStatusErrorUnresolved || status == kStatusErrorResolved;
+  }
+
+  // Returns true if the class has failed to link or initialize.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsErroneous() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetStatus<kVerifyFlags>() == kStatusError;
+    return IsErroneous(GetStatus<kVerifyFlags>());
   }
 
   // Returns true if the class has been loaded.
@@ -177,7 +202,8 @@ class MANAGED Class FINAL : public Object {
   // Returns true if the class has been linked.
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetStatus<kVerifyFlags>() >= kStatusResolved;
+    Status status = GetStatus<kVerifyFlags>();
+    return status >= kStatusResolved || status == kStatusErrorResolved;
   }
 
   // Returns true if the class was compile-time verified.
@@ -345,7 +371,7 @@ class MANAGED Class FINAL : public Object {
   // be replaced with a class with the right size for embedded imt/vtable.
   bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) {
     Status s = GetStatus();
-    return s < Status::kStatusResolving && ShouldHaveEmbeddedVTable();
+    return s < Status::kStatusResolving && s != kStatusErrorResolved && ShouldHaveEmbeddedVTable();
   }
 
   String* GetName() REQUIRES_SHARED(Locks::mutator_lock_);  // Returns the cached name.
@@ -1017,7 +1043,7 @@ class MANAGED Class FINAL : public Object {
   // Returns the number of instance fields containing reference types. Does not count fields in any
   // super classes.
   uint32_t NumReferenceInstanceFields() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(IsResolved() || IsErroneous());
+    DCHECK(IsResolved());
     return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_));
   }
 
@@ -1045,7 +1071,7 @@ class MANAGED Class FINAL : public Object {
 
   // Returns the number of static fields containing reference types.
   uint32_t NumReferenceStaticFields() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(IsResolved() || IsErroneous());
+    DCHECK(IsResolved());
     return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
   }
 
index 284d2d1..a8fa7db 100644 (file)
@@ -81,14 +81,12 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa
   if (c != nullptr && c->IsErroneous()) {
     cl->ThrowEarlierClassFailure(c.Ptr());
     Thread* self = soa.Self();
-    ObjPtr<mirror::Class> eiie_class =
-        self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
     ObjPtr<mirror::Class> iae_class =
         self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
     ObjPtr<mirror::Class> ncdfe_class =
         self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
     ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
-    if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
+    if (exception == iae_class || exception == ncdfe_class) {
       self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
                                      c->PrettyDescriptor().c_str());
     }
index 62d99fb..111755e 100644 (file)
@@ -201,8 +201,12 @@ class OatFile {
     // A representation of an invalid OatClass, used when an OatClass can't be found.
     // See FindOatClass().
     static OatClass Invalid() {
-      return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr,
-                      nullptr);
+      return OatClass(/* oat_file */ nullptr,
+                      mirror::Class::kStatusErrorUnresolved,
+                      kOatClassNoneCompiled,
+                      /* bitmap_size */ 0,
+                      /* bitmap_pointer */ nullptr,
+                      /* methods_pointer */ nullptr);
     }
 
    private:
index 507ea16..2610252 100644 (file)
@@ -51,7 +51,6 @@ jclass WellKnownClasses::java_lang_ClassLoader;
 jclass WellKnownClasses::java_lang_ClassNotFoundException;
 jclass WellKnownClasses::java_lang_Daemons;
 jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
 jclass WellKnownClasses::java_lang_invoke_MethodHandle;
 jclass WellKnownClasses::java_lang_IllegalAccessError;
 jclass WellKnownClasses::java_lang_NoClassDefFoundError;
@@ -290,7 +289,6 @@ void WellKnownClasses::Init(JNIEnv* env) {
   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_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle");
   java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
index b3ce3d1..db8a53c 100644 (file)
@@ -61,7 +61,6 @@ struct WellKnownClasses {
   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_invoke_MethodHandle;
   static jclass java_lang_NoClassDefFoundError;
index 083ecf7..fcf2ef4 100644 (file)
@@ -1,11 +1,11 @@
 Got an NPE: second throw
 java.lang.NullPointerException: second throw
-       at Main.catchAndRethrow(Main.java:77)
-       at Main.exceptions_007(Main.java:59)
-       at Main.main(Main.java:67)
+       at Main.catchAndRethrow(Main.java:94)
+       at Main.exceptions_007(Main.java:74)
+       at Main.main(Main.java:82)
 Caused by: java.lang.NullPointerException: first throw
-       at Main.throwNullPointerException(Main.java:84)
-       at Main.catchAndRethrow(Main.java:74)
+       at Main.throwNullPointerException(Main.java:101)
+       at Main.catchAndRethrow(Main.java:91)
        ... 2 more
 Static Init
 BadError: This is bad by convention: BadInit
@@ -15,3 +15,11 @@ Static BadInitNoStringInit
 BadErrorNoStringInit: This is bad by convention
 java.lang.NoClassDefFoundError: BadInitNoStringInit
 BadErrorNoStringInit: This is bad by convention
+BadSuperClass Static Init
+BadError: This is bad by convention: BadInit
+MultiDexBadInit Static Init
+java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+  cause: java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+  cause: java.lang.Error: MultiDexBadInit
diff --git a/test/008-exceptions/multidex.jpp b/test/008-exceptions/multidex.jpp
new file mode 100644 (file)
index 0000000..a3746f5
--- /dev/null
@@ -0,0 +1,27 @@
+BadError:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BadError
+BadInit:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BadInit
+BadErrorNoStringInit:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BadErrorNoStringInit
+BadInitNoStringInit:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BadInitNoStringInit
+BadSuperClass:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BadSuperClass
+DerivedFromBadSuperClass:
+  @@com.android.jack.annotations.ForceInMainDex
+  class DerivedFromBadSuperClass
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
+MultiDexBadInit:
+  @@com.android.jack.annotations.ForceInMainDex
+  class MultiDexBadInit
+MultiDexBadInitWrapper1:
+  @@com.android.jack.annotations.ForceInMainDex
+  class MultiDexBadInitWrapper1
diff --git a/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java
new file mode 100644 (file)
index 0000000..f3953bd
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MultiDexBadInitWrapper2 {
+    public static void setDummy(int value) {
+        if (doThrow) { throw new Error(); }
+        MultiDexBadInit.dummy = value;
+    }
+
+    public static boolean doThrow = false;
+}
index b8231f1..74af00c 100644 (file)
@@ -50,6 +50,21 @@ class BadInitNoStringInit {
     }
 }
 
+// A class that throws BadError during static initialization, serving as a super class.
+class BadSuperClass {
+ static int dummy;
+ static {
+     System.out.println("BadSuperClass Static Init");
+     if (true) {
+         throw new BadError("BadInit");
+     }
+ }
+}
+
+// A class that derives from BadSuperClass.
+class DerivedFromBadSuperClass extends BadSuperClass {
+}
+
 /**
  * Exceptions across method calls
  */
@@ -63,10 +78,12 @@ public class Main {
             npe.printStackTrace(System.out);
         }
     }
-    public static void main (String args[]) {
+    public static void main(String args[]) {
         exceptions_007();
         exceptionsRethrowClassInitFailure();
         exceptionsRethrowClassInitFailureNoStringInit();
+        exceptionsForSuperClassInitFailure();
+        exceptionsInMultiDex();
     }
 
     private static void catchAndRethrow() {
@@ -129,4 +146,70 @@ public class Main {
             error.printStackTrace(System.out);
         }
     }
+
+    private static void exceptionsForSuperClassInitFailure() {
+        try {
+            // Resolve DerivedFromBadSuperClass.
+            BadSuperClass.dummy = 1;
+            throw new IllegalStateException("Should not reach here.");
+        } catch (BadError e) {
+            System.out.println(e);
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        try {
+            // Before splitting mirror::Class::kStatusError into
+            // kStatusErrorUnresolved and kStatusErrorResolved,
+            // this would trigger a
+            //     CHECK(super_class->IsResolved())
+            // failure in
+            //     ClassLinker::LoadSuperAndInterfaces().
+            // After the change we're getting either VerifyError
+            // (for Optimizing) or NoClassDefFoundError wrapping
+            // BadError (for interpreter or JIT).
+            new DerivedFromBadSuperClass();
+            throw new IllegalStateException("Should not reach here.");
+        } catch (NoClassDefFoundError ncdfe) {
+            if (!(ncdfe.getCause() instanceof BadError)) {
+                ncdfe.getCause().printStackTrace();
+            }
+        } catch (VerifyError e) {
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    private static void exceptionsInMultiDex() {
+        try {
+            MultiDexBadInit.dummy = 1;
+            throw new IllegalStateException("Should not reach here.");
+        } catch (Error e) {
+            System.out.println(e);
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        // Before splitting mirror::Class::kStatusError into
+        // kStatusErrorUnresolved and kStatusErrorResolved,
+        // the exception from wrapper 1 would have been
+        // wrapped in NoClassDefFoundError but the exception
+        // from wrapper 2 would have been unwrapped.
+        try {
+            MultiDexBadInitWrapper1.setDummy(1);
+            throw new IllegalStateException("Should not reach here.");
+        } catch (NoClassDefFoundError ncdfe) {
+            System.out.println(ncdfe);
+            System.out.println("  cause: " + ncdfe.getCause());
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        try {
+            MultiDexBadInitWrapper2.setDummy(1);
+            throw new IllegalStateException("Should not reach here.");
+        } catch (NoClassDefFoundError ncdfe) {
+            System.out.println(ncdfe);
+            System.out.println("  cause: " + ncdfe.getCause());
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
 }
diff --git a/test/008-exceptions/src/MultiDexBadInit.java b/test/008-exceptions/src/MultiDexBadInit.java
new file mode 100644 (file)
index 0000000..e3ebb9c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MultiDexBadInit {
+    static int dummy;
+    static {
+        System.out.println("MultiDexBadInit Static Init");
+        if (true) {
+            throw new Error("MultiDexBadInit");
+        }
+    }
+}
diff --git a/test/008-exceptions/src/MultiDexBadInitWrapper1.java b/test/008-exceptions/src/MultiDexBadInitWrapper1.java
new file mode 100644 (file)
index 0000000..059e6a3
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MultiDexBadInitWrapper1 {
+    public static void setDummy(int value) {
+        if (doThrow) { throw new Error(); }
+        MultiDexBadInit.dummy = value;
+    }
+
+    public static boolean doThrow = false;
+}
index 86f5e22..056d978 100644 (file)
@@ -1 +1,5 @@
+Loaded class B.
+Caught VerifyError.
+Loaded class B.
+Caught wrapped VerifyError.
 Everything OK.
index 80b00e7..a0c7764 100644 (file)
@@ -74,16 +74,25 @@ public class Main {
         // Try to load a dex file with bad dex code. Use new instance to force verification.
         try {
           Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+          System.out.println("Loaded class B.");
           badClass.newInstance();
-          System.out.println("Should not be able to load class from bad dex file.");
+          System.out.println("Should not be able to instantiate B with bad dex bytecode.");
         } catch (VerifyError e) {
+          System.out.println("Caught VerifyError.");
         }
 
         // 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("Loaded class B.");
+          badClass.newInstance();
+          System.out.println("Should not be able to instantiate B with bad dex bytecode.");
+        } catch (NoClassDefFoundError e) {
+          if (e.getCause() instanceof VerifyError) {
+            System.out.println("Caught wrapped VerifyError.");
+          } else {
+            e.printStackTrace();
+          }
         }
 
         System.out.println("Everything OK.");
index d0b77a4..328216b 100644 (file)
@@ -29,7 +29,7 @@ int 100000
 class [Ljava.lang.String; 10000
 class java.lang.Object 111
 class Main$TestForNonInit 11
-class Main$TestForInitFail 1001
+class Main$TestForInitFail 1011
 int []
 class [Ljava.lang.String; []
 class java.lang.Object []
diff --git a/test/ErroneousInit/ErroneousInit.java b/test/ErroneousInit/ErroneousInit.java
new file mode 100644 (file)
index 0000000..67b7b20
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class ErroneousInit {
+    static {
+        if (true) {
+            throw new Error();
+        }
+    }
+}