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
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 314ba40..da7b845 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 @@
if (!LinkStaticFields(self, klass, &class_size)) {
return false;
}
+ SetRecordClassFlagIfNeeded(klass);
CreateReferenceInstanceOffsets(klass);
CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus());
@@ -9350,6 +9352,63 @@
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 bd7ae4c..de6e80d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1185,6 +1185,8 @@
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 7c461fd..cefdc98 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1257,6 +1257,17 @@
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 ea11e10..87b2052 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -79,6 +79,11 @@
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 45676c8..d757d34 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -317,6 +317,15 @@
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 139c4cb..c85b9e0 100644
--- a/runtime/mirror/class_flags.h
+++ b/runtime/mirror/class_flags.h
@@ -56,6 +56,9 @@
// 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 593a454..7cd1dc0 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -732,6 +732,13 @@
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 @@
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 6a4a87f..780fa83 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -51,6 +51,7 @@
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 @@
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 @@
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 753d386..cc6347c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -96,6 +96,7 @@
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;