diff options
| author | 2017-01-20 19:32:50 +0000 | |
|---|---|---|
| committer | 2017-01-24 12:05:02 +0000 | |
| commit | 72ab684871f870aead76b23cb67deb046107b380 (patch) | |
| tree | d4bb40cab65df449246b35ae582234238676b7bb | |
| parent | e36c51aee58e61e9fc89851b767379c587f050e3 (diff) | |
Add class status for resolved erroneous classes.
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, 385 insertions, 79 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c87075f51c..0aedf71904 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -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 diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c03ffcaa31..b00d083c28 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -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: diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 459aca3b38..15e4cd8e9c 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -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(); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index de5af97b9a..bd2c5e3bfc 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -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; } diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index b9f688de57..917db3e486 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -52,7 +52,7 @@ inline uint32_t ArtField::GetAccessFlags() { } inline MemberOffset ArtField::GetOffset() { - DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous()); + DCHECK(GetDeclaringClass()->IsResolved()); return MemberOffset(offset_); } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 15938c52be..a35c7ab701 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -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_; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b8ed530981..e65672a71b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -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()); } diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 7b6c0dc510..026afdc792 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -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); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index fe6a6e9140..3d3ad593b3 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -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; } diff --git a/runtime/image.cc b/runtime/image.cc index 6d8889532f..54b099eb14 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -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, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 4ea1130947..bbd6d352d3 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -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); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 9964b73c98..f08d4daf95 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -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()); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index fb2792a316..7f6aa12fdb 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -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_)); } diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 284d2d10c5..a8fa7db688 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -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()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 62d99fb51c..111755e7a1 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -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: diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 507ea165e5..2610252aa7 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -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"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index b3ce3d1597..db8a53c44c 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -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; diff --git a/test/008-exceptions/expected.txt b/test/008-exceptions/expected.txt index 083ecf7ced..fcf2ef49f3 100644 --- a/test/008-exceptions/expected.txt +++ b/test/008-exceptions/expected.txt @@ -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 index 0000000000..a3746f5149 --- /dev/null +++ b/test/008-exceptions/multidex.jpp @@ -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 index 0000000000..f3953bd4c7 --- /dev/null +++ b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java @@ -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; +} diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java index b8231f12bd..74af00ccf7 100644 --- a/test/008-exceptions/src/Main.java +++ b/test/008-exceptions/src/Main.java @@ -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 index 0000000000..e3ebb9ca45 --- /dev/null +++ b/test/008-exceptions/src/MultiDexBadInit.java @@ -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 index 0000000000..059e6a3d7d --- /dev/null +++ b/test/008-exceptions/src/MultiDexBadInitWrapper1.java @@ -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; +} diff --git a/test/142-classloader2/expected.txt b/test/142-classloader2/expected.txt index 86f5e220e2..056d9785de 100644 --- a/test/142-classloader2/expected.txt +++ b/test/142-classloader2/expected.txt @@ -1 +1,5 @@ +Loaded class B. +Caught VerifyError. +Loaded class B. +Caught wrapped VerifyError. Everything OK. diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java index 80b00e7dd2..a0c77645a3 100644 --- a/test/142-classloader2/src/Main.java +++ b/test/142-classloader2/src/Main.java @@ -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."); diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index d0b77a4bae..328216b324 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -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 index 0000000000..67b7b204dc --- /dev/null +++ b/test/ErroneousInit/ErroneousInit.java @@ -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(); + } + } +} |