Re-run verification when it fails at compile time
Verification may fail at compile time as we have incomplete information.
Always re-run at runtime in this case and don't report a VerifyError
yet.
Clean up GetCaughtExceptionType to better handle unresolved exceptions.
Change-Id: Ic526d0a1b43eea9783d540b7432cf22bf244bade
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 51e5f3c..f343ed4 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1924,6 +1924,7 @@
const DexFile& dex_file = FindDexFile(klass->GetDexCache());
if (VerifyClassUsingOatFile(dex_file, klass) ||
verifier::DexVerifier::VerifyClass(klass, error_msg)) {
+ DCHECK(!Thread::Current()->IsExceptionPending());
// Make sure all classes referenced by catch blocks are resolved
ResolveClassExceptionHandlerTypes(dex_file, klass);
klass->SetStatus(Class::kStatusVerified);
@@ -1934,7 +1935,7 @@
<< " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
<< " because: " << error_msg;
Thread* self = Thread::Current();
- CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
+ CHECK(!self->IsExceptionPending());
self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
klass->SetStatus(Class::kStatusError);
@@ -1961,17 +1962,34 @@
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
CHECK(oat_class.get() != NULL) << descriptor;
Class::Status status = oat_class->GetStatus();
- if (status == Class::kStatusError) {
- Thread* self = Thread::Current();
- self->ThrowNewExceptionF("Ljava/lang/VerifyError;", "Compile-time verification of %s failed",
- PrettyDescriptor(klass).c_str());
- klass->SetStatus(Class::kStatusError);
- return true;
- }
if (status == Class::kStatusVerified || status == Class::kStatusInitialized) {
return true;
}
+ if (status == Class::kStatusError) {
+ // Compile time verification failed. Compile time verification can fail because we have
+ // incomplete type information. Consider the following:
+ // class ... {
+ // Foo x;
+ // .... () {
+ // if (...) {
+ // v1 gets assigned a type of resolved class Foo
+ // } else {
+ // v1 gets assigned a type of unresolved class Bar
+ // }
+ // iput x = v1
+ // } }
+ // when we merge v1 following the if-the-else it results in Conflict
+ // (see verifier::RegType::Merge) as we can't know the type of Bar and we could possibly be
+ // allowing an unsafe assignment to the field x in the iput (javac may have compiled this as
+ // it knew Bar was a sub-class of Foo, but for us this may have been moved into a separate apk
+ // at compile time).
+ return false;
+ }
if (status == Class::kStatusNotReady) {
+ // Status is uninitialized if we couldn't determine the status at compile time, for example,
+ // not loading the class.
+ // TODO: when the verifier doesn't rely on Class-es failing to resolve/load the type hierarchy
+ // isn't a problem and this case shouldn't occur
return false;
}
LOG(FATAL) << "Unexpected class status: " << status;