ART: Rewrite EnsureInitialized hit case
When an initialized class is found during EnsureInitialized, do
not check whether verification was attempted and fix up bits.
Instead, ensure that all class-loading paths have that done
when eventually getting here.
Special runtime-constructed classes (primitives, arrays, proxies)
need code to do this work. "Normal" classes have the work done
during VerifyClass.
Leave a DCHECK in as a state check. Protect state transfers with
additional checks.
This reduces the overhead of the interpreter which cannot elide
initialization checks for static accesses.
Bug: 115834172
Test: m test-art-host
Change-Id: Iacd6652583364509c37eafe81fed1198abb1b71a
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 18f7105..0039be0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1742,6 +1742,9 @@
if (&cls->GetDexFile() == &accessor.GetDexFile()) {
ObjectLock<mirror::Class> lock(self, cls);
mirror::Class::SetStatus(cls, status, self);
+ if (status >= ClassStatus::kVerified) {
+ cls->SetVerificationAttempted();
+ }
}
} else {
DCHECK(self->IsExceptionPending());
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f6a3e32..3b92e2c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -212,6 +212,22 @@
self->AssertPendingException();
}
+// Ensures that methods have the kAccSkipAccessChecks bit set. We use the
+// kAccVerificationAttempted bit on the class access flags to determine whether this has been done
+// before.
+template <bool kNeedsVerified = false>
+static void EnsureSkipAccessChecksMethods(Handle<mirror::Class> klass, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kNeedsVerified) {
+ // To not fail access-flags access checks, push a minimal state.
+ mirror::Class::SetStatus(klass, ClassStatus::kVerified, Thread::Current());
+ }
+ if (!klass->WasVerificationAttempted()) {
+ klass->SetSkipAccessChecksFlagOnAllMethods(pointer_size);
+ klass->SetVerificationAttempted();
+ }
+}
+
void ClassLinker::ThrowEarlierClassFailure(ObjPtr<mirror::Class> c, bool wrap_in_no_class_def) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
@@ -3950,6 +3966,7 @@
h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
h_class->SetPrimitiveType(type);
h_class->SetIfTable(GetClassRoot<mirror::Object>(this)->GetIfTable());
+ EnsureSkipAccessChecksMethods</* kNeedsVerified= */ true>(h_class, image_pointer_size_);
mirror::Class::SetStatus(h_class, ClassStatus::kInitialized, self);
const char* descriptor = Primitive::Descriptor(type);
ObjPtr<mirror::Class> existing = InsertClass(descriptor,
@@ -4097,6 +4114,7 @@
new_class->PopulateEmbeddedVTable(image_pointer_size_);
ImTable* object_imt = java_lang_Object->GetImt(image_pointer_size_);
new_class->SetImt(object_imt, image_pointer_size_);
+ EnsureSkipAccessChecksMethods</* kNeedsVerified= */ true>(new_class, image_pointer_size_);
mirror::Class::SetStatus(new_class, ClassStatus::kInitialized, self);
// don't need to set new_class->SetObjectSize(..)
// because Object::SizeOf delegates to Array::SizeOf
@@ -4127,6 +4145,8 @@
// and remove "interface".
access_flags |= kAccAbstract | kAccFinal;
access_flags &= ~kAccInterface;
+ // Arrays are access-checks-clean and preverified.
+ access_flags |= kAccVerificationAttempted;
new_class->SetAccessFlags(access_flags);
@@ -4361,17 +4381,6 @@
return false;
}
-// Ensures that methods have the kAccSkipAccessChecks bit set. We use the
-// kAccVerificationAttempted bit on the class access flags to determine whether this has been done
-// before.
-static void EnsureSkipAccessChecksMethods(Handle<mirror::Class> klass, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!klass->WasVerificationAttempted()) {
- klass->SetSkipAccessChecksFlagOnAllMethods(pointer_size);
- klass->SetVerificationAttempted();
- }
-}
-
verifier::FailureKind ClassLinker::VerifyClass(
Thread* self, Handle<mirror::Class> klass, verifier::HardFailLogMode log_level) {
{
@@ -4848,6 +4857,7 @@
{
// Lock on klass is released. Lock new class object.
ObjectLock<mirror::Class> initialization_lock(self, klass);
+ EnsureSkipAccessChecksMethods(klass, image_pointer_size_);
mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self);
}
@@ -5598,8 +5608,7 @@
DCHECK(c != nullptr);
if (c->IsInitialized()) {
- EnsureSkipAccessChecksMethods(c, image_pointer_size_);
- self->AssertNoPendingException();
+ DCHECK(c->WasVerificationAttempted()) << c->PrettyClassAndClassLoader();
return true;
}
// SubtypeCheckInfo::Initialized must happen-before any new-instance for that type.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index fe45b9e..061c788 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -114,7 +114,8 @@
EXPECT_EQ(0, primitive->GetIfTableCount());
EXPECT_TRUE(primitive->GetIfTable() != nullptr);
EXPECT_EQ(primitive->GetIfTable()->Count(), 0u);
- EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags());
+ EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract | kAccVerificationAttempted,
+ primitive->GetAccessFlags());
}
void AssertObjectClass(ObjPtr<mirror::Class> JavaLangObject)
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 8eff21c..c5ed1bf 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -206,6 +206,10 @@
}
}
+ if (kIsDebugBuild && new_status >= ClassStatus::kInitialized) {
+ CHECK(h_this->WasVerificationAttempted()) << h_this->PrettyClassAndClassLoader();
+ }
+
if (!class_linker_initialized) {
// When the class linker is being initialized its single threaded and by definition there can be
// no waiters. During initialization classes may appear temporary but won't be retired as their
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index 52367c7..82c82c6 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -41,6 +41,7 @@
if (status == ClassStatus::kResolved) {
ObjectLock<mirror::Class> lock(soa.Self(), klass);
klass->SetStatus(klass, ClassStatus::kVerified, soa.Self());
+ klass->SetVerificationAttempted();
} else {
LOG(ERROR) << klass->PrettyClass() << " has unexpected status: " << status;
}