Skip to content

Commit aac4af0

Browse files
jerboaazakkak
authored andcommitted
Implement annotation support for RecordComponents
1 parent aab8c89 commit aac4af0

File tree

5 files changed

+133
-7
lines changed

5 files changed

+133
-7
lines changed

substratevm/src/com.oracle.svm.core.jdk16/src/com/oracle/svm/core/jdk16/RecordSupportJDK16OrLater.java

+26
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,19 @@
2626

2727
// Checkstyle: allow reflection
2828

29+
import java.lang.annotation.Annotation;
30+
import java.lang.reflect.AnnotatedType;
2931
import java.lang.reflect.Constructor;
3032
import java.lang.reflect.Method;
3133
import java.lang.reflect.RecordComponent;
3234
import java.util.Arrays;
35+
import java.util.HashMap;
36+
import java.util.Map;
3337

3438
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
3539
import org.graalvm.nativeimage.ImageSingletons;
3640
import org.graalvm.nativeimage.hosted.Feature;
41+
import org.graalvm.util.GuardedAnnotationAccess;
3742

3843
import com.oracle.svm.core.annotate.AutomaticFeature;
3944
import com.oracle.svm.core.jdk.RecordSupport;
@@ -70,6 +75,27 @@ public Constructor<?> getCanonicalRecordConstructor(Class<?> clazz) {
7075
throw VMError.shouldNotReachHere("Malformed record class that does not declare a canonical constructor: " + clazz.getTypeName());
7176
}
7277
}
78+
79+
@Override
80+
public Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz) {
81+
Map<String, Annotation[]> componentAnnotations = new HashMap<>();
82+
Arrays.stream(clazz.getRecordComponents())
83+
.forEach(t -> {
84+
componentAnnotations.put(t.getName(), GuardedAnnotationAccess.getAnnotations(t));
85+
});
86+
return componentAnnotations;
87+
}
88+
89+
@Override
90+
public Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz) {
91+
Map<String, AnnotatedType> componentAnnotTypes = new HashMap<>();
92+
Arrays.stream(clazz.getRecordComponents())
93+
.forEach(t -> {
94+
componentAnnotTypes.put(t.getName(), t.getAnnotatedType());
95+
});
96+
return componentAnnotTypes;
97+
}
98+
7399
}
74100

75101
@AutomaticFeature

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

+66-5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.security.ProtectionDomain;
5050
import java.security.cert.Certificate;
5151
import java.util.List;
52+
import java.util.Map;
5253
import java.util.Optional;
5354
import java.util.Set;
5455
import java.util.StringJoiner;
@@ -67,7 +68,9 @@
6768
import com.oracle.svm.core.annotate.Alias;
6869
import com.oracle.svm.core.annotate.Delete;
6970
import com.oracle.svm.core.annotate.Hybrid;
71+
import com.oracle.svm.core.annotate.Inject;
7072
import com.oracle.svm.core.annotate.KeepOriginal;
73+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
7174
import com.oracle.svm.core.annotate.Substitute;
7275
import com.oracle.svm.core.annotate.TargetClass;
7376
import com.oracle.svm.core.annotate.TargetElement;
@@ -1024,25 +1027,32 @@ public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
10241027
*/
10251028
public static final class ReflectionData {
10261029
static final ReflectionData EMPTY = new ReflectionData(new Field[0], new Field[0], new Field[0], new Method[0], new Method[0], new Constructor<?>[0], new Constructor<?>[0], null, new Field[0],
1027-
new Method[0], new Class<?>[0], new Class<?>[0], null, null);
1030+
new Method[0], new Class<?>[0], new Class<?>[0], null, null, null, null);
10281031

10291032
public static ReflectionData get(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods,
10301033
Constructor<?>[] declaredConstructors, Constructor<?>[] publicConstructors, Constructor<?> nullaryConstructor, Field[] declaredPublicFields,
1031-
Method[] declaredPublicMethods, Class<?>[] declaredClasses, Class<?>[] publicClasses, Executable enclosingMethodOrConstructor, Object[] recordComponents) {
1034+
Method[] declaredPublicMethods, Class<?>[] declaredClasses, Class<?>[] publicClasses, Executable enclosingMethodOrConstructor, Object[] recordComponents,
1035+
Map<String, Annotation[]> recordAnnotations, Map<String, AnnotatedType> recordAnnotatedType) {
10321036

10331037
if (z(declaredFields) && z(publicFields) && z(publicUnhiddenFields) && z(declaredMethods) && z(publicMethods) && z(declaredConstructors) &&
10341038
z(publicConstructors) && nullaryConstructor == null && z(declaredPublicFields) && z(declaredPublicMethods) && z(declaredClasses) &&
1035-
z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents))) {
1039+
z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents)) &&
1040+
(recordAnnotations == null || e(recordAnnotations)) && (recordAnnotatedType == null || e(recordAnnotatedType))) {
10361041
return EMPTY; // avoid redundant objects in image heap
10371042
}
10381043
return new ReflectionData(declaredFields, publicFields, publicUnhiddenFields, declaredMethods, publicMethods, declaredConstructors, publicConstructors, nullaryConstructor,
1039-
declaredPublicFields, declaredPublicMethods, declaredClasses, publicClasses, enclosingMethodOrConstructor, recordComponents);
1044+
declaredPublicFields, declaredPublicMethods, declaredClasses, publicClasses, enclosingMethodOrConstructor, recordComponents, recordAnnotations,
1045+
recordAnnotatedType);
10401046
}
10411047

10421048
private static boolean z(Object[] array) { // for better readability above
10431049
return array.length == 0;
10441050
}
10451051

1052+
private static boolean e(Map<String, ?> map) {
1053+
return map.isEmpty();
1054+
}
1055+
10461056
final Field[] declaredFields;
10471057
final Field[] publicFields;
10481058
final Field[] publicUnhiddenFields;
@@ -1056,6 +1066,8 @@ private static boolean z(Object[] array) { // for better readability above
10561066
final Class<?>[] declaredClasses;
10571067
final Class<?>[] publicClasses;
10581068
final Object[] recordComponents;
1069+
final Map<String, Annotation[]> recordAnnotations; // component name => annotations
1070+
final Map<String, AnnotatedType> recordAnnotatedType; // component name => annotated type
10591071

10601072
/**
10611073
* The result of {@link Class#getEnclosingMethod()} or
@@ -1066,7 +1078,7 @@ private static boolean z(Object[] array) { // for better readability above
10661078
ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor<?>[] declaredConstructors,
10671079
Constructor<?>[] publicConstructors, Constructor<?> nullaryConstructor, Field[] declaredPublicFields, Method[] declaredPublicMethods, Class<?>[] declaredClasses,
10681080
Class<?>[] publicClasses, Executable enclosingMethodOrConstructor,
1069-
Object[] recordComponents) {
1081+
Object[] recordComponents, Map<String, Annotation[]> recordAnnotations, Map<String, AnnotatedType> recordAnnotatedType) {
10701082
this.declaredFields = declaredFields;
10711083
this.publicFields = publicFields;
10721084
this.publicUnhiddenFields = publicUnhiddenFields;
@@ -1081,6 +1093,8 @@ private static boolean z(Object[] array) { // for better readability above
10811093
this.publicClasses = publicClasses;
10821094
this.enclosingMethodOrConstructor = enclosingMethodOrConstructor;
10831095
this.recordComponents = recordComponents;
1096+
this.recordAnnotations = recordAnnotations;
1097+
this.recordAnnotatedType = recordAnnotatedType;
10841098
}
10851099
}
10861100

@@ -1218,11 +1232,21 @@ Method[] privateGetPublicMethods() {
12181232
@TargetElement(onlyWith = JDK16OrLater.class)
12191233
private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() {
12201234
Object[] result = rd.recordComponents;
1235+
Map<String, Annotation[]> annotations = rd.recordAnnotations;
1236+
Map<String, AnnotatedType> annotatedTypes = rd.recordAnnotatedType;
12211237
if (result == null) {
12221238
/* See ReflectionDataBuilder.buildRecordComponents() for details. */
12231239
throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " +
12241240
"All record component accessor methods of this record class must be included in the reflection configuration at image build time, then this method can be called.");
12251241
}
1242+
// Set component annotations for the the record-components
1243+
for (Object rec : result) {
1244+
Target_java_lang_reflect_RecordComponent recordComp = (Target_java_lang_reflect_RecordComponent) rec;
1245+
Annotation[] annot = annotations.get(recordComp.getName());
1246+
AnnotatedType type = annotatedTypes.get(recordComp.getName());
1247+
recordComp.componentAnnotations = annot;
1248+
recordComp.annotatedType = type;
1249+
}
12261250
return (Target_java_lang_reflect_RecordComponent[]) result;
12271251
}
12281252

@@ -1671,4 +1695,41 @@ final class Target_jdk_internal_reflect_ConstantPool {
16711695

16721696
@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK16OrLater.class)
16731697
final class Target_java_lang_reflect_RecordComponent {
1698+
1699+
@Inject //
1700+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
1701+
volatile Annotation[] componentAnnotations;
1702+
1703+
@Inject //
1704+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
1705+
volatile AnnotatedType annotatedType;
1706+
1707+
@Substitute //
1708+
public Annotation[] getAnnotations() {
1709+
return componentAnnotations;
1710+
}
1711+
1712+
@Substitute //
1713+
public Annotation[] getDeclaredAnnotations() {
1714+
return componentAnnotations;
1715+
}
1716+
1717+
@Substitute //
1718+
public Annotation getAnnotation(Class<?> annotationClass) {
1719+
for (Annotation ann : componentAnnotations) {
1720+
if (annotationClass.isAssignableFrom(ann.annotationType())) {
1721+
return ann;
1722+
}
1723+
}
1724+
return null;
1725+
}
1726+
1727+
@Substitute //
1728+
public AnnotatedType getAnnotatedType() {
1729+
return annotatedType;
1730+
}
1731+
1732+
@Alias //
1733+
public native String getName();
1734+
16741735
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ public ReflectionData getCompleteReflectionData() {
142142
completeReflectionData = new ReflectionData(hub.rd.declaredFields, hub.rd.publicFields, hub.rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]),
143143
newPublicMethods.toArray(new Method[0]),
144144
newDeclaredConstructors.toArray(new Constructor<?>[0]), newPublicConstructors.toArray(new Constructor<?>[0]), hub.rd.nullaryConstructor, hub.rd.declaredPublicFields,
145-
newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor, hub.rd.recordComponents);
145+
newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor,
146+
hub.rd.recordComponents, hub.rd.recordAnnotations, hub.rd.recordAnnotatedType);
146147
}
147148
return completeReflectionData;
148149
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecordSupport.java

+17
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626

2727
// Checkstyle: allow reflection
2828

29+
import java.lang.annotation.Annotation;
30+
import java.lang.reflect.AnnotatedType;
2931
import java.lang.reflect.Constructor;
3032
import java.lang.reflect.Method;
33+
import java.util.Map;
3134

3235
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
3336
import org.graalvm.nativeimage.ImageSingletons;
@@ -60,6 +63,10 @@ public static RecordSupport singleton() {
6063
*/
6164
public abstract Object[] getRecordComponents(Class<?> clazz);
6265

66+
public abstract Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz);
67+
68+
public abstract Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz);
69+
6370
/**
6471
* Returns the {@code RecordComponent.getAccessor} method for each of the
6572
* {@code Class.getRecordComponents()}.
@@ -101,6 +108,16 @@ public Method[] getRecordComponentAccessorMethods(Class<?> clazz) {
101108
public Constructor<?> getCanonicalRecordConstructor(Class<?> clazz) {
102109
throw VMError.shouldNotReachHere();
103110
}
111+
112+
@Override
113+
public Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz) {
114+
throw VMError.shouldNotReachHere();
115+
}
116+
117+
@Override
118+
public Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz) {
119+
throw VMError.shouldNotReachHere();
120+
}
104121
}
105122

106123
@AutomaticFeature

substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java

+22-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import java.lang.annotation.Annotation;
3030
import java.lang.reflect.AnnotatedElement;
31+
import java.lang.reflect.AnnotatedType;
3132
import java.lang.reflect.Constructor;
3233
import java.lang.reflect.Executable;
3334
import java.lang.reflect.Field;
@@ -134,6 +135,8 @@ private static DynamicHub.ReflectionData getArrayReflectionData() {
134135
EMPTY_CLASSES,
135136
EMPTY_CLASSES,
136137
null,
138+
null,
139+
null,
137140
null);
138141
}
139142

@@ -519,7 +522,9 @@ private void processClass(DuringAnalysisAccessImpl access, Class<?> clazz) {
519522
filterClasses(declaredClasses, reflectionClasses, access),
520523
filterClasses(classes, reflectionClasses, access),
521524
enclosingMethodOrConstructor(clazz),
522-
buildRecordComponents(clazz, access));
525+
buildRecordComponents(clazz, access),
526+
buildRecordComponentAnnotations(clazz),
527+
buildRecordAnnotatedTypes(clazz));
523528
}
524529
hub.setReflectionData(reflectionData);
525530
}
@@ -562,6 +567,22 @@ private Object[] buildRecordComponents(Class<?> clazz, DuringAnalysisAccessImpl
562567
}
563568
}
564569

570+
private static Map<String, Annotation[]> buildRecordComponentAnnotations(Class<?> clazz) {
571+
RecordSupport support = RecordSupport.singleton();
572+
if (!support.isRecord(clazz)) {
573+
return null;
574+
}
575+
return support.getRecordComponentsAnnotations(clazz);
576+
}
577+
578+
private static Map<String, AnnotatedType> buildRecordAnnotatedTypes(Class<?> clazz) {
579+
RecordSupport support = RecordSupport.singleton();
580+
if (!support.isRecord(clazz)) {
581+
return null;
582+
}
583+
return support.getRecordComponentAnnotatedType(clazz);
584+
}
585+
565586
private static void reportLinkingErrors(Class<?> clazz, List<Throwable> errors) {
566587
if (errors.isEmpty()) {
567588
return;

0 commit comments

Comments
 (0)