summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Victor Chang <vichang@google.com> 2023-03-21 17:00:46 +0000
committer Victor Chang <vichang@google.com> 2023-03-27 08:59:07 +0000
commit4efc53d64dd5a04fcb9203bee756ae462c6da9f9 (patch)
tree2d07b8b73d81664213dec5d36acb58a99be8d171
parent456ea95d710b05659a87b5f348f523493a7718c6 (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.cc59
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/dex/dex_file_annotations.cc11
-rw-r--r--runtime/dex/dex_file_annotations.h5
-rw-r--r--runtime/mirror/class.h9
-rw-r--r--runtime/mirror/class_flags.h3
-rw-r--r--runtime/native/java_lang_Class.cc8
-rw-r--r--runtime/well_known_classes.cc3
-rw-r--r--runtime/well_known_classes.h1
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;