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;