Fix recursive initialization on app image.
Assume class B extends class A and class A's clinit tries to instantiate
class B as a field. In this case, class A is under initializing but
ClassLinker allows initialize B as class A is initialized. If class B is
initialized successfully and transaction is commited then class A abort
the transaction, status of class B will not be reverted.
Fixed by a simple approach, when compiling images, AotClassLinker does
not allow initialize a subclass when it's superclass is not fully
initialized.
A testcase produce this error is added in /c/433381
Test: make test-art-host -j64
Change-Id: Ic6bcbf1a5162d0e6ec26979b336c0f644a1c39bc
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 871604b..b1bc3f8 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -31,18 +31,29 @@
bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
Runtime* const runtime = Runtime::Current();
+ bool strict_mode_ = runtime->IsActiveStrictTransactionMode();
DCHECK(klass != nullptr);
if (klass->IsInitialized() || klass->IsInitializing()) {
return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
}
- if (runtime->IsActiveStrictTransactionMode()) {
+ // Don't initialize klass if it's superclass is not initialized, because superclass might abort
+ // the transaction and rolled back after klass's change is commited.
+ if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) {
+ if (klass->GetSuperClass()->GetStatus() == mirror::Class::kStatusInitializing) {
+ runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+ + klass->PrettyTypeOf() + " because it's superclass is not initialized.");
+ return false;
+ }
+ }
+
+ if (strict_mode_) {
runtime->EnterTransactionMode(true, klass.Get()->AsClass());
}
bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
- if (runtime->IsActiveStrictTransactionMode()) {
+ if (strict_mode_) {
if (success) {
// Exit Transaction if success.
runtime->ExitTransactionMode();