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
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 9964b73..f08d4da 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -115,7 +115,9 @@
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 @@
<< 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 @@
// 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 @@
}
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 @@
}
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());