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]]