in runtime/class_linker.cc [4481:4749]
bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
// see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
// Are we already initialized and therefore done?
// Note: we differ from the JLS here as we don't do this under the lock, this is benign as
// an initialized class will never change its state.
if (klass->IsInitialized()) {
return true;
}
std::string tempStorage;
const char* descriptorCString = klass.Get()->GetDescriptorAssumingDex(&tempStorage);
// We should never be asked to initialize a class whose descriptor isn't from the dex file. So their
// should be no need to ever allocate extra storage space for the descriptor.
CHECK(tempStorage.empty()) << "Check failed for " << descriptorCString;
NANO_TRACE_SCOPE_FROM_STRING_AND_META(self, "bool ClassLinker.InitializeClass()", descriptorCString);
// Fast fail if initialization requires a full runtime. Not part of the JLS.
if (!CanWeInitializeClass(klass.Get(), can_init_statics, can_init_parents)) {
return false;
}
self->AllowThreadSuspension();
uint64_t t0;
{
ObjectLock<mirror::Class> lock(self, klass);
// Re-check under the lock in case another thread initialized ahead of us.
if (klass->IsInitialized()) {
return true;
}
// Was the class already found to be erroneous? Done under the lock to match the JLS.
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass.Get(), true);
VlogClassInitializationFailure(klass);
return false;
}
CHECK(klass->IsResolved()) << PrettyClass(klass.Get()) << ": state=" << klass->GetStatus();
if (!klass->IsVerified()) {
VerifyClass(self, klass);
if (!klass->IsVerified()) {
// We failed to verify, expect either the klass to be erroneous or verification failed at
// compile time.
if (klass->IsErroneous()) {
// The class is erroneous. This may be a verifier error, or another thread attempted
// verification and/or initialization and failed. We can distinguish those cases by
// whether an exception is already pending.
if (self->IsExceptionPending()) {
// Check that it's a VerifyError.
DCHECK_EQ("java.lang.Class<java.lang.VerifyError>",
PrettyClass(self->GetException()->GetClass()));
} else {
// Check that another thread attempted initialization.
DCHECK_NE(0, klass->GetClinitThreadId());
DCHECK_NE(self->GetTid(), klass->GetClinitThreadId());
// Need to rethrow the previous failure now.
ThrowEarlierClassFailure(klass.Get(), true);
}
VlogClassInitializationFailure(klass);
} else {
CHECK(Runtime::Current()->IsAotCompiler());
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
}
return false;
} else {
self->AssertNoPendingException();
}
// 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());
if (klass->IsInitialized()) {
return true;
}
}
// If the class is kStatusInitializing, either this thread is
// initializing higher up the stack or another thread has beat us
// to initializing and we need to wait. Either way, this
// invocation of InitializeClass will not be responsible for
// running <clinit> and will return.
if (klass->GetStatus() == mirror::Class::kStatusInitializing) {
// Could have got an exception during verification.
if (self->IsExceptionPending()) {
VlogClassInitializationFailure(klass);
return false;
}
// We caught somebody else in the act; was it us?
if (klass->GetClinitThreadId() == self->GetTid()) {
// Yes. That's fine. Return so we can continue initializing.
return true;
}
// No. That's fine. Wait for another thread to finish initializing.
return WaitForInitializeClass(klass, self, lock);
}
if (!ValidateSuperClassDescriptors(klass)) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
return false;
}
self->AllowThreadSuspension();
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.Get())
<< " self.tid=" << self->GetTid() << " clinit.tid=" << klass->GetClinitThreadId();
// From here out other threads may observe that we're initializing and so changes of state
// require the a notification.
klass->SetClinitThreadId(self->GetTid());
mirror::Class::SetStatus(klass, mirror::Class::kStatusInitializing, self);
t0 = NanoTime();
}
// Initialize super classes, must be done while initializing for the JLS.
if (!klass->IsInterface() && klass->HasSuperClass()) {
mirror::Class* super_class = klass->GetSuperClass();
if (!super_class->IsInitialized()) {
CHECK(!super_class->IsInterface());
CHECK(can_init_parents);
StackHandleScope<1> hs(self);
Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class));
bool super_initialized = InitializeClass(self, handle_scope_super, can_init_statics, true);
if (!super_initialized) {
// The super class was verified ahead of entering initializing, we should only be here if
// the super class became erroneous due to initialization.
CHECK(handle_scope_super->IsErroneous() && self->IsExceptionPending())
<< "Super class initialization failed for "
<< PrettyDescriptor(handle_scope_super.Get())
<< " that has unexpected status " << handle_scope_super->GetStatus()
<< "\nPending exception:\n"
<< (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);
return false;
}
}
}
if (!klass->IsInterface()) {
// Initialize interfaces with default methods for the JLS.
size_t num_direct_interfaces = klass->NumDirectInterfaces();
// Only setup the (expensive) handle scope if we actually need to.
if (UNLIKELY(num_direct_interfaces > 0)) {
StackHandleScope<1> hs_iface(self);
MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
for (size_t i = 0; i < num_direct_interfaces; i++) {
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass, i));
CHECK(handle_scope_iface.Get() != nullptr);
CHECK(handle_scope_iface->IsInterface());
if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
// We have already done this for this interface. Skip it.
continue;
}
// We cannot just call initialize class directly because we need to ensure that ALL
// interfaces with default methods are initialized. Non-default interface initialization
// will not affect other non-default super-interfaces.
bool iface_initialized = InitializeDefaultInterfaceRecursive(self,
handle_scope_iface,
can_init_statics,
can_init_parents);
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);
return false;
}
}
}
}
const size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields > 0) {
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
CHECK(dex_class_def != nullptr);
const DexFile& dex_file = klass->GetDexFile();
StackHandleScope<3> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
// Eagerly fill in static fields so that the we don't have to do as many expensive
// Class::FindStaticField in ResolveField.
for (size_t i = 0; i < num_static_fields; ++i) {
ArtField* field = klass->GetStaticField(i);
const uint32_t field_idx = field->GetDexFieldIndex();
ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
if (resolved_field == nullptr) {
dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
} else {
DCHECK_EQ(field, resolved_field);
}
}
EncodedStaticFieldValueIterator value_it(dex_file, &dex_cache, &class_loader,
this, *dex_class_def);
const uint8_t* class_data = dex_file.GetClassData(*dex_class_def);
ClassDataItemIterator field_it(dex_file, class_data);
if (value_it.HasNext()) {
DCHECK(field_it.HasNextStaticField());
CHECK(can_init_statics);
for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) {
ArtField* field = ResolveField(
dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true);
if (Runtime::Current()->IsActiveTransaction()) {
value_it.ReadValueToField<true>(field);
} else {
value_it.ReadValueToField<false>(field);
}
if (self->IsExceptionPending()) {
break;
}
DCHECK(!value_it.HasNext() || field_it.HasNextStaticField());
}
}
}
if (!self->IsExceptionPending()) {
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
if (clinit != nullptr) {
CHECK(can_init_statics);
JValue result;
clinit->Invoke(self, nullptr, 0, &result, "V");
}
}
self->AllowThreadSuspension();
uint64_t t1 = NanoTime();
bool success = true;
{
ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
success = false;
} else if (Runtime::Current()->IsTransactionAborted()) {
// The exception thrown when the transaction aborted has been caught and cleared
// so we need to throw it again now.
VLOG(compiler) << "Return from class initializer of " << 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);
success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
RuntimeStats* thread_stats = self->GetStats();
++global_stats->class_init_count;
++thread_stats->class_init_count;
global_stats->class_init_time_ns += (t1 - t0);
thread_stats->class_init_time_ns += (t1 - t0);
// Set the class as initialized except if failed to initialize static fields.
mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self);
if (VLOG_IS_ON(class_linker)) {
std::string temp;
LOG(INFO) << "Initialized class " << klass->GetDescriptor(&temp) << " from " <<
klass->GetLocation();
}
// Opportunistically set static method trampolines to their destination.
FixupStaticTrampolines(klass.Get());
}
}
return success;
}