Class::SetStatus(kStatusError) now checks that an exception is pending and uses it to SetVerifyErrorClass
Change-Id: I02f4adc51ac6da88d4969655fa828f93941c4c0a
diff --git a/src/class_linker.cc b/src/class_linker.cc
index ac2f4f1..5687453 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -121,6 +121,7 @@
*/
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
+ CHECK(c->IsErroneous()) << PrettyClass(c);
if (c->GetVerifyErrorClass() != NULL) {
// TODO: change the verifier to store an _instance_, with a useful detail message?
ClassHelper ve_ch(c->GetVerifyErrorClass());
@@ -1079,6 +1080,7 @@
if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
self->ThrowNewException("Ljava/lang/ClassCircularityError;",
PrettyDescriptor(klass).c_str());
+ klass->SetStatus(Class::kStatusError);
return NULL;
}
// Wait for the pending initialization to complete.
@@ -1159,7 +1161,7 @@
ScopedLocalRef<jobject> class_loader_object(env, AddLocalReference<jobject>(env, class_loader));
ScopedLocalRef<jobject> result(env, env->CallObjectMethod(class_loader_object.get(), mid,
class_name_object.get()));
- if (env->ExceptionOccurred()) {
+ if (env->ExceptionCheck()) {
// If the ClassLoader threw, pass that exception up.
return NULL;
} else if (result.get() == NULL) {
@@ -1208,6 +1210,7 @@
// Check for a pending exception during load
Thread* self = Thread::Current();
if (self->IsExceptionPending()) {
+ klass->SetStatus(Class::kStatusError);
return NULL;
}
ObjectLock lock(klass.get());
@@ -1224,7 +1227,6 @@
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
- CHECK(self->IsExceptionPending());
klass->SetStatus(Class::kStatusError);
lock.NotifyAll();
return NULL;
@@ -1234,7 +1236,6 @@
CHECK(!klass->IsResolved());
if (!LinkClass(klass, NULL)) {
// Linking failed.
- CHECK(self->IsExceptionPending());
klass->SetStatus(Class::kStatusError);
lock.NotifyAll();
return NULL;
@@ -1902,8 +1903,9 @@
CHECK(oat_class.get() != NULL) << descriptor;
Class::Status status = oat_class->GetStatus();
if (status == Class::kStatusError) {
- ThrowEarlierClassFailure(klass);
- klass->SetVerifyErrorClass(Thread::Current()->GetException()->GetClass());
+ // TODO: include appropriate verify_error_class_ information in oat file for use here.
+ ThrowNoClassDefFoundError("Class failed compile-time verification: %s",
+ PrettyDescriptor(klass).c_str());
klass->SetStatus(Class::kStatusError);
return true;
}
@@ -2002,7 +2004,7 @@
// Link the fields and virtual methods, creating vtable and iftables
if (!LinkClass(klass, interfaces)) {
- DCHECK(Thread::Current()->IsExceptionPending());
+ klass->SetStatus(Class::kStatusError);
return NULL;
}
sfield->SetObject(NULL, throws); // initialize throws field
@@ -2156,7 +2158,6 @@
}
if (!ValidateSuperClassDescriptors(klass)) {
- CHECK(self->IsExceptionPending());
klass->SetStatus(Class::kStatusError);
return false;
}
@@ -2170,7 +2171,7 @@
uint64_t t0 = NanoTime();
if (!InitializeSuperClass(klass, can_run_clinit)) {
- CHECK(self->IsExceptionPending());
+ CHECK(klass->IsErroneous());
return false;
}
@@ -2346,10 +2347,10 @@
// TODO: check for a pending exception
if (!super_initialized) {
if (!can_run_clinit) {
- // Don't set status to error when we can't run <clinit>.
- CHECK_EQ(klass->GetStatus(), Class::kStatusInitializing) << PrettyClass(klass);
- klass->SetStatus(Class::kStatusVerified);
- return false;
+ // Don't set status to error when we can't run <clinit>.
+ CHECK_EQ(klass->GetStatus(), Class::kStatusInitializing) << PrettyClass(klass);
+ klass->SetStatus(Class::kStatusVerified);
+ return false;
}
klass->SetStatus(Class::kStatusError);
klass->NotifyAll();
@@ -2370,7 +2371,7 @@
ScopedThreadStateChange tsc(self, Thread::kRunnable);
bool success = InitializeClass(c, can_run_clinit);
if (!success) {
- CHECK(self->IsExceptionPending());
+ CHECK(self->IsExceptionPending()) << PrettyClass(c);
}
return success;
}
@@ -2496,13 +2497,12 @@
}
// Verify
if (super->IsFinal() || super->IsInterface()) {
- Thread* thread = Thread::Current();
- thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+ Thread* self = Thread::Current();
+ self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
"Superclass %s of %s is %s",
PrettyDescriptor(super).c_str(),
PrettyDescriptor(klass.get()).c_str(),
super->IsFinal() ? "declared final" : "an interface");
- klass->SetVerifyErrorClass(thread->GetException()->GetClass());
return false;
}
if (!klass->CanAccess(super)) {
@@ -2663,12 +2663,11 @@
DCHECK(interface != NULL);
if (!interface->IsInterface()) {
ClassHelper ih(interface);
- Thread* thread = Thread::Current();
- thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+ Thread* self = Thread::Current();
+ self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
"Class %s implements non-interface class %s",
PrettyDescriptor(klass.get()).c_str(),
PrettyDescriptor(ih.GetDescriptor()).c_str());
- klass->SetVerifyErrorClass(thread->GetException()->GetClass());
return false;
}
// Check if interface is already in iftable
diff --git a/src/object.cc b/src/object.cc
index 7ead4ab..c16cdb0 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -597,6 +597,30 @@
CHECK(new_status > GetStatus() || new_status == kStatusError || !Runtime::Current()->IsStarted())
<< PrettyClass(this) << " " << GetStatus() << " -> " << new_status;
CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
+ if (new_status == kStatusError) {
+ CHECK_NE(GetStatus(), kStatusError) << PrettyClass(this);
+
+ // stash current exception
+ Thread* self = Thread::Current();
+ SirtRef<Throwable> exception(self->GetException());
+ CHECK(exception.get() != NULL);
+
+ // clear exception to call FindSystemClass
+ self->ClearException();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
+ CHECK(!self->IsExceptionPending());
+
+ // only verification errors, not initialization problems, should set a verify error.
+ // this is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case.
+ Class* exception_class = exception->GetClass();
+ if (!eiie_class->IsAssignableFrom(exception_class)) {
+ SetVerifyErrorClass(exception_class);
+ }
+
+ // restore exception
+ self->SetException(exception.get());
+ }
return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
}
diff --git a/src/object.h b/src/object.h
index 3cbe9d6..1841042 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1708,10 +1708,6 @@
return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false);
}
- void SetVerifyErrorClass(Class* klass) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
- }
-
uint16_t GetDexTypeIndex() const {
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), false);
}
@@ -1721,6 +1717,11 @@
}
private:
+ void SetVerifyErrorClass(Class* klass) {
+ CHECK(klass != NULL) << PrettyClass(this);
+ SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+ }
+
bool Implements(const Class* klass) const;
bool IsArrayAssignableFromArray(const Class* klass) const;
bool IsAssignableFromArray(const Class* klass) const;
diff --git a/test/068-classloader/src/FancyLoader.java b/test/068-classloader/src/FancyLoader.java
index 28c17a8..2c6fcde 100644
--- a/test/068-classloader/src/FancyLoader.java
+++ b/test/068-classloader/src/FancyLoader.java
@@ -35,10 +35,10 @@
*/
public class FancyLoader extends ClassLoader {
/* this is where the "alternate" .class files live */
- static final String CLASS_PATH = "/data/art-test";
+ static final String CLASS_PATH = "classes-ex/";
/* this is the "alternate" DEX/Jar file */
- static final String DEX_FILE = "068-classloader-ex.jar";
+ static final String DEX_FILE = "/data/art-test/068-classloader-ex.jar";
/* on Dalvik, this is a DexFile; otherwise, it's null */
private Class mDexClass;
@@ -94,7 +94,7 @@
}
try {
- mDexFile = ctor.newInstance(CLASS_PATH + File.separator + DEX_FILE);
+ mDexFile = ctor.newInstance(DEX_FILE);
} catch (InstantiationException ie) {
throw new ClassNotFoundException("newInstance failed", ie);
} catch (IllegalAccessException iae) {
diff --git a/test/068-classloader/src/Main.java b/test/068-classloader/src/Main.java
index 1bc7b04..7dfb6f5 100644
--- a/test/068-classloader/src/Main.java
+++ b/test/068-classloader/src/Main.java
@@ -99,7 +99,7 @@
try {
altClass = loader.loadClass("Inaccessible2");
- System.err.println("ERROR: Inaccessible2 was accessible");
+ System.err.println("ERROR: Inaccessible2 was accessible: " + altClass);
} catch (ClassNotFoundException cnfe) {
Throwable cause = cnfe.getCause();
if (cause instanceof IllegalAccessError) {
@@ -119,7 +119,7 @@
try {
altClass = loader.loadClass("Inaccessible3");
- System.err.println("ERROR: Inaccessible3 was accessible");
+ System.err.println("ERROR: Inaccessible3 was accessible: " + altClass);
} catch (ClassNotFoundException cnfe) {
Throwable cause = cnfe.getCause();
if (cause instanceof IllegalAccessError) {