diff options
author | 2023-03-21 17:00:46 +0000 | |
---|---|---|
committer | 2023-03-27 08:59:07 +0000 | |
commit | 4efc53d64dd5a04fcb9203bee756ae462c6da9f9 (patch) | |
tree | 2d07b8b73d81664213dec5d36acb58a99be8d171 | |
parent | 456ea95d710b05659a87b5f348f523493a7718c6 (diff) |
Set Record flag during class linking
It's needed for adding new record-specific behavior in ART.
For example, fields are still immutable and un-modifiable
via java.lang.reflect.Field#set*() API. See http://b/272698028
Bug: 272698028
Test: atest CtsLibcoreTestCases:RecordTest
Change-Id: I6805fc4a8b8966b913eaba9f7c4dfdd1ab5886c4
-rw-r--r-- | runtime/class_linker.cc | 59 | ||||
-rw-r--r-- | runtime/class_linker.h | 2 | ||||
-rw-r--r-- | runtime/dex/dex_file_annotations.cc | 11 | ||||
-rw-r--r-- | runtime/dex/dex_file_annotations.h | 5 | ||||
-rw-r--r-- | runtime/mirror/class.h | 9 | ||||
-rw-r--r-- | runtime/mirror/class_flags.h | 3 | ||||
-rw-r--r-- | runtime/native/java_lang_Class.cc | 8 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 3 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 1 |
9 files changed, 101 insertions, 0 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 314ba40c6b..da7b845d67 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -70,6 +70,7 @@ #include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" #include "dex/dex_file_exception_helpers.h" #include "dex/dex_file_loader.h" #include "dex/signature-inl.h" @@ -6121,6 +6122,7 @@ bool ClassLinker::LinkClass(Thread* self, if (!LinkStaticFields(self, klass, &class_size)) { return false; } + SetRecordClassFlagIfNeeded(klass); CreateReferenceInstanceOffsets(klass); CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus()); @@ -9350,6 +9352,63 @@ bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, si return LinkFieldsHelper::LinkFields(this, self, klass, true, class_size); } +// Set kClassFlagRecord if all conditions are fulfilled. +void ClassLinker::SetRecordClassFlagIfNeeded(Handle<mirror::Class> klass) { + CHECK(klass != nullptr); + // First, we check the conditions specified in java.lang.Class#isRecord(). + // If any of the following check fails, ART will treat it as a normal class, + // but still inherited from java.lang.Record. + if (!klass->IsFinal()) { + return; + } + + ObjPtr<mirror::Class> super = klass->GetSuperClass(); + if (super == nullptr) { + return; + } + + // Compare the string directly when this ClassLinker is initializing before + // WellKnownClasses initializes + if (WellKnownClasses::java_lang_Record == nullptr) { + if (!super->DescriptorEquals("Ljava/lang/Record;")) { + return; + } + } else { + ObjPtr<mirror::Class> java_lang_Record = + WellKnownClasses::ToClass(WellKnownClasses::java_lang_Record); + if (super.Ptr() != java_lang_Record.Ptr()) { + return; + } + } + + // Record class should have record attributes specifying various things, + // including component type, name and the order of record components. The dexer + // should emit @dalvik.annotation.Record annotation. But before the annotation is + // emitted correctly b/272698028, we workaround by reading @MethodParameters annotation + // as a temporary replacement. + uint32_t num_of_fields = klass->NumInstanceFields(); + for (ArtMethod& method : klass->GetDirectMethods(image_pointer_size_)) { + // JLS 8.10.2 Record body can't declare instance fields. + // JLS 8.10.4 Record class has only one canonical constructor. + // Dexer should emit @MethodParameters method annotation along with + // the canonical constructor. + if (method.IsConstructor() && method.GetNumberOfParameters() == num_of_fields) { + if (num_of_fields == 0 || + annotations::IsMethodAnnotationPresent( + &method, "Ldalvik/annotation/MethodParameters;", DexFile::kDexVisibilitySystem)) { + // JLS 8.10.4 / 8.10.4.1 We should check if the constructor + // doesn't invoke other constructor in this class or invoke the super + // class constructor to confirm that it's the canonical constructor. + + // But it's good enough until @dalvik.annotation.Record is emitted + // from the dexer. + klass->SetRecordClass(); + return; + } + } + } +} + // Set the bitmap of reference instance field offsets. void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { uint32_t reference_offsets = 0; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index bd7ae4cc9e..de6e80dcee 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1185,6 +1185,8 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_); bool LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + void SetRecordClassFlagIfNeeded(Handle<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_); void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 7c461fd26e..cefdc981bf 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -1257,6 +1257,17 @@ bool IsMethodAnnotationPresent(ArtMethod* method, return annotation_item != nullptr; } +bool IsMethodAnnotationPresent(ArtMethod* method, const char* descriptor, uint32_t visibility) { + const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); + if (annotation_set == nullptr) { + return false; + } + const DexFile* dex_file = method->GetDexFile(); + const AnnotationItem* annotation_item = + SearchAnnotationSet(*dex_file, annotation_set, descriptor, visibility); + return annotation_item != nullptr; +} + static void DCheckNativeAnnotation(const char* descriptor, jclass cls) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index ea11e10c96..87b2052793 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -79,6 +79,11 @@ bool IsMethodAnnotationPresent(ArtMethod* method, uint32_t visibility = DexFile::kDexVisibilityRuntime) REQUIRES_SHARED(Locks::mutator_lock_); +bool IsMethodAnnotationPresent(ArtMethod* method, + const char* descriptor, + uint32_t visibility = DexFile::kDexVisibilityRuntime) + REQUIRES_SHARED(Locks::mutator_lock_); + // Check whether a method from the `dex_file` with the given `method_index` // is annotated with @dalvik.annotation.optimization.FastNative or // @dalvik.annotation.optimization.CriticalNative with build visibility. diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 45676c8d88..d757d343a5 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -317,6 +317,15 @@ class MANAGED Class final : public Object { SetClassFlags(GetClassFlags() | kClassFlagDexCache); } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE bool IsRecordClass() REQUIRES_SHARED(Locks::mutator_lock_) { + return (GetClassFlags<kVerifyFlags>() & kClassFlagRecord) != 0; + } + + ALWAYS_INLINE void SetRecordClass() REQUIRES_SHARED(Locks::mutator_lock_) { + SetClassFlags(GetClassFlags() | kClassFlagRecord); + } + // Returns true if the class is abstract. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ALWAYS_INLINE bool IsAbstract() REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/mirror/class_flags.h b/runtime/mirror/class_flags.h index 139c4cb67a..c85b9e0eee 100644 --- a/runtime/mirror/class_flags.h +++ b/runtime/mirror/class_flags.h @@ -56,6 +56,9 @@ static constexpr uint32_t kClassFlagFinalizerReference = 0x00000200; // Class is the phantom reference class. static constexpr uint32_t kClassFlagPhantomReference = 0x00000400; +// Class is a record class. See doc at java.lang.Class#isRecord(). +static constexpr uint32_t kClassFlagRecord = 0x00000800; + // Combination of flags to figure out if the class is either the weak/soft/phantom/finalizer // reference class. static constexpr uint32_t kClassFlagReference = diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 593a454d37..7cd1dc081f 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -732,6 +732,13 @@ static jboolean Class_isAnonymousClass(JNIEnv* env, jobject javaThis) { return class_name == nullptr; } +static jboolean Class_isRecord0(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis))); + return klass->IsRecordClass(); +} + static jboolean Class_isDeclaredAnnotationPresent(JNIEnv* env, jobject javaThis, jclass annotationType) { ScopedFastNativeObjectAccess soa(env); @@ -954,6 +961,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"), FAST_NATIVE_METHOD(Class, isAnonymousClass, "()Z"), FAST_NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"), + FAST_NATIVE_METHOD(Class, isRecord0, "()Z"), FAST_NATIVE_METHOD(Class, newInstance, "()Ljava/lang/Object;"), }; diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 6a4a87f3b8..780fa830b8 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -51,6 +51,7 @@ jclass WellKnownClasses::dalvik_annotation_optimization_NeverCompile; jclass WellKnownClasses::dalvik_annotation_optimization_NeverInline; jclass WellKnownClasses::java_lang_annotation_Annotation__array; jclass WellKnownClasses::java_lang_ClassValue; +jclass WellKnownClasses::java_lang_Record; jclass WellKnownClasses::java_lang_reflect_Parameter__array; jclass WellKnownClasses::java_lang_StringFactory; jclass WellKnownClasses::java_lang_System; @@ -325,6 +326,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_annotation_Annotation__array = CacheClass(env, "[Ljava/lang/annotation/Annotation;"); java_lang_ClassValue = CacheClass(env, "java/lang/ClassValue"); + java_lang_Record = CacheClass(env, "java/lang/Record"); java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;"); java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory"); java_lang_System = CacheClass(env, "java/lang/System"); @@ -799,6 +801,7 @@ void WellKnownClasses::Clear() { dalvik_annotation_optimization_NeverInline = nullptr; java_lang_annotation_Annotation__array = nullptr; java_lang_ClassValue = nullptr; + java_lang_Record = nullptr; java_lang_reflect_Parameter__array = nullptr; java_lang_StringFactory = nullptr; java_lang_System = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 753d3866a2..cc6347cd5e 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -96,6 +96,7 @@ struct WellKnownClasses { static jclass dalvik_annotation_optimization_NeverInline; static jclass java_lang_annotation_Annotation__array; static jclass java_lang_ClassValue; + static jclass java_lang_Record; static jclass java_lang_reflect_Parameter__array; static jclass java_lang_StringFactory; static jclass java_lang_System; |