Use @Record annotation to implement the record type

Bug: 272698028
Test: atest CtsLibcoreTestCases:crossvmtest
Change-Id: Ia7a7ff66634b3fae40b029ee51d5e6bf8513a461
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index cdd07f6..682f358 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -9382,31 +9382,8 @@
     }
   }
 
-  // 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;
-      }
-    }
+  if (annotations::IsRecordClassAnnotationPresent(klass)) {
+    klass->SetRecordClass();
   }
 }
 
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index cefdc98..a29b9a4 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1257,17 +1257,6 @@
   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());
@@ -1784,6 +1773,43 @@
                                                 "value");
 }
 
+ObjPtr<mirror::Object> getRecordAnnotationElement(Handle<mirror::Class> klass,
+                                                  Handle<mirror::Class> array_class,
+                                                  const char* element_name) {
+  ClassData data(klass);
+  const DexFile& dex_file = klass->GetDexFile();
+  const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+  if (annotation_set == nullptr) {
+    return nullptr;
+  }
+  const AnnotationItem* annotation_item = SearchAnnotationSet(
+      dex_file, annotation_set, "Ldalvik/annotation/Record;", DexFile::kDexVisibilitySystem);
+  if (annotation_item == nullptr) {
+    return nullptr;
+  }
+  const uint8_t* annotation =
+      SearchEncodedAnnotation(dex_file, annotation_item->annotation_, element_name);
+  if (annotation == nullptr) {
+    return nullptr;
+  }
+  DexFile::AnnotationValue annotation_value;
+  bool result = Runtime::Current()->IsActiveTransaction()
+      ? ProcessAnnotationValue<true>(data,
+                                     &annotation,
+                                     &annotation_value,
+                                     array_class,
+                                     DexFile::kPrimitivesOrObjects)
+      : ProcessAnnotationValue<false>(data,
+                                      &annotation,
+                                      &annotation_value,
+                                      array_class,
+                                      DexFile::kPrimitivesOrObjects);
+  if (!result) {
+    return nullptr;
+  }
+  return annotation_value.value_.GetL();
+}
+
 bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) {
   ClassData data(klass);
   const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
@@ -1795,6 +1821,19 @@
   return annotation_item != nullptr;
 }
 
+bool IsRecordClassAnnotationPresent(Handle<mirror::Class> klass) {
+  ClassData data(klass);
+  const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+  if (annotation_set == nullptr) {
+    return false;
+  }
+  const AnnotationItem* annotation_item = SearchAnnotationSet(data.GetDexFile(),
+                                                              annotation_set,
+                                                              "Ldalvik/annotation/Record;",
+                                                              DexFile::kDexVisibilitySystem);
+  return annotation_item != nullptr;
+}
+
 int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t rel_pc) {
   // For native method, lineno should be -2 to indicate it is native. Note that
   // "line number == -2" is how libcore tells from StackTraceElement.
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 87b2052..8b5df81 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -79,11 +79,6 @@
                                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.
@@ -150,12 +145,19 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 ObjPtr<mirror::ObjectArray<mirror::Class>> GetNestMembers(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::Object> getRecordAnnotationElement(Handle<mirror::Class> klass,
+                                                  Handle<mirror::Class> array_class,
+                                                  const char* element_name)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 ObjPtr<mirror::ObjectArray<mirror::Class>> GetPermittedSubclasses(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool IsClassAnnotationPresent(Handle<mirror::Class> klass,
                               Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+bool IsRecordClassAnnotationPresent(Handle<mirror::Class> klass)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
 // Map back from a PC to the line number in a method.
 int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t rel_pc)
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d757d34..232d683 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -575,7 +575,7 @@
   // The size of java.lang.Class.class.
   static uint32_t ClassClassSize(PointerSize pointer_size) {
     // The number of vtable entries in java.lang.Class.
-    uint32_t vtable_entries = Object::kVTableLength + 80;
+    uint32_t vtable_entries = Object::kVTableLength + 81;
     return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
   }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 7cd1dc0..2506ca8 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -809,6 +809,27 @@
   return soa.AddLocalReference<jobjectArray>(classes);
 }
 
+static jobjectArray Class_getRecordAnnotationElement(JNIEnv* env,
+                                                     jobject javaThis,
+                                                     jstring element_name,
+                                                     jclass array_class) {
+  ScopedFastNativeObjectAccess soa(env);
+  ScopedUtfChars name(env, element_name);
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+  if (!(klass->IsRecordClass())) {
+    return nullptr;
+  }
+
+  Handle<mirror::Class> a_class(hs.NewHandle(DecodeClass(soa, array_class)));
+  ObjPtr<mirror::Object> element_array =
+      annotations::getRecordAnnotationElement(klass, a_class, name.c_str());
+  if (element_array == nullptr || !(element_array->IsObjectArray())) {
+    return nullptr;
+  }
+  return soa.AddLocalReference<jobjectArray>(element_array);
+}
+
 static jobjectArray Class_getPermittedSubclassesFromAnnotation(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
@@ -958,6 +979,7 @@
   FAST_NATIVE_METHOD(Class, getNestMembersFromAnnotation, "()[Ljava/lang/Class;"),
   FAST_NATIVE_METHOD(Class, getPermittedSubclassesFromAnnotation, "()[Ljava/lang/Class;"),
   FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"),
+  FAST_NATIVE_METHOD(Class, getRecordAnnotationElement, "(Ljava/lang/String;Ljava/lang/Class;)[Ljava/lang/Object;"),
   FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"),
   FAST_NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
   FAST_NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"),
diff --git a/test/1980-obsolete-object-cleared/expected-stdout.txt b/test/1980-obsolete-object-cleared/expected-stdout.txt
index 24e9b53..8562dcb 100644
--- a/test/1980-obsolete-object-cleared/expected-stdout.txt
+++ b/test/1980-obsolete-object-cleared/expected-stdout.txt
@@ -183,6 +183,25 @@
 public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
 Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
 public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on (obsolete)class Main$Transform with [] = null
+Calling public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY], [null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [NOT_USED_STRING, class java.lang.Class] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [foo, class java.lang.Class] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on (obsolete)class Main$Transform with [SECRET_ARRAY, class java.lang.Class] = null
 Calling public java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents() with params: []
 public java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents() on (obsolete)class Main$Transform with [] = null
 Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]
@@ -432,6 +451,25 @@
 public java.lang.Class[] java.lang.Class.getPermittedSubclasses() on class Main$Transform with [] = null
 Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
 public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on class Main$Transform with [] = null
+Calling public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY], [null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [NOT_USED_STRING, class java.lang.Class] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [foo, class java.lang.Class] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, null] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, class java.lang.Object] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, (obsolete)class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, class Main$Transform] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, long] = null
+public native java.lang.Object[] java.lang.Class.getRecordAnnotationElement(java.lang.String,java.lang.Class) on class Main$Transform with [SECRET_ARRAY, class java.lang.Class] = null
 Calling public java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents() with params: []
 public java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents() on class Main$Transform with [] = null
 Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]