Add comment & refactor check for Obsolete class in FindFieldFromCode
Add a comment explaining why we need a check for a class not being an
obsolete class in FindFieldFromCode. It is needed since if we
structurally redefine a class that is in the middle of running its
<clinit> or <init> function the classes final fields will be set from
a function with the obsolete class as its declaring-class.
Also refactor the code a bit to put this logic in ArtField.
Test: None
Change-Id: I9ebb0c89568b9d8e5a60d44c982584b174370eef
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 41808dd..5ab6d91 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -40,6 +40,17 @@
return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass<kVerifyNone>();
}
+// We are only ever allowed to set our own final fields. We do need to be careful since if a
+// structural redefinition occurs during <clinit> we can end up trying to set the non-obsolete
+// class's fields from the obsolete class. This is something we want to allow. This is tested by
+// run-test 2002-virtual-structural-initializing.
+inline bool ArtField::CanBeChangedBy(ArtMethod* method) {
+ ObjPtr<mirror::Class> declaring_class(GetDeclaringClass());
+ ObjPtr<mirror::Class> referring_class(method->GetDeclaringClass());
+ return !IsFinal() || (declaring_class == referring_class) ||
+ UNLIKELY(referring_class->IsObsoleteVersionOf(declaring_class));
+}
+
template<ReadBarrierOption kReadBarrierOption>
inline ObjPtr<mirror::Class> ArtField::GetDeclaringClass() {
GcRootSource gc_root_source(this);
diff --git a/runtime/art_field.h b/runtime/art_field.h
index c149003..86f67ad 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -233,6 +233,9 @@
std::string PrettyField(bool with_type = true)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if a set-* instruction in the given method is allowable.
+ ALWAYS_INLINE inline bool CanBeChangedBy(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
bool IsProxyField() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index c67b1b0..338928e 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -358,8 +358,7 @@
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
}
- if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class) &&
- !referring_class->IsObsoleteVersionOf(fields_class))) {
+ if (UNLIKELY(is_set && !resolved_field->CanBeChangedBy(referrer))) {
ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
return nullptr; // Failure.
} else {
@@ -630,7 +629,7 @@
ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
!referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) ||
- (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
+ (is_set && !resolved_field->CanBeChangedBy(referrer)))) {
// Illegal access.
return nullptr;
}