Skip to content

Commit

Permalink
Implement annotation support for RecordComponents
Browse files Browse the repository at this point in the history
  • Loading branch information
jerboaa authored and zakkak committed Mar 1, 2022
1 parent aab8c89 commit aac4af0
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@

// Checkstyle: allow reflection

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.util.GuardedAnnotationAccess;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.jdk.RecordSupport;
Expand Down Expand Up @@ -70,6 +75,27 @@ public Constructor<?> getCanonicalRecordConstructor(Class<?> clazz) {
throw VMError.shouldNotReachHere("Malformed record class that does not declare a canonical constructor: " + clazz.getTypeName());
}
}

@Override
public Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz) {
Map<String, Annotation[]> componentAnnotations = new HashMap<>();
Arrays.stream(clazz.getRecordComponents())
.forEach(t -> {
componentAnnotations.put(t.getName(), GuardedAnnotationAccess.getAnnotations(t));
});
return componentAnnotations;
}

@Override
public Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz) {
Map<String, AnnotatedType> componentAnnotTypes = new HashMap<>();
Arrays.stream(clazz.getRecordComponents())
.forEach(t -> {
componentAnnotTypes.put(t.getName(), t.getAnnotatedType());
});
return componentAnnotTypes;
}

}

@AutomaticFeature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
Expand All @@ -67,7 +68,9 @@
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.Hybrid;
import com.oracle.svm.core.annotate.Inject;
import com.oracle.svm.core.annotate.KeepOriginal;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
Expand Down Expand Up @@ -1024,25 +1027,32 @@ public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
*/
public static final class ReflectionData {
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],
new Method[0], new Class<?>[0], new Class<?>[0], null, null);
new Method[0], new Class<?>[0], new Class<?>[0], null, null, null, null);

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

if (z(declaredFields) && z(publicFields) && z(publicUnhiddenFields) && z(declaredMethods) && z(publicMethods) && z(declaredConstructors) &&
z(publicConstructors) && nullaryConstructor == null && z(declaredPublicFields) && z(declaredPublicMethods) && z(declaredClasses) &&
z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents))) {
z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents)) &&
(recordAnnotations == null || e(recordAnnotations)) && (recordAnnotatedType == null || e(recordAnnotatedType))) {
return EMPTY; // avoid redundant objects in image heap
}
return new ReflectionData(declaredFields, publicFields, publicUnhiddenFields, declaredMethods, publicMethods, declaredConstructors, publicConstructors, nullaryConstructor,
declaredPublicFields, declaredPublicMethods, declaredClasses, publicClasses, enclosingMethodOrConstructor, recordComponents);
declaredPublicFields, declaredPublicMethods, declaredClasses, publicClasses, enclosingMethodOrConstructor, recordComponents, recordAnnotations,
recordAnnotatedType);
}

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

private static boolean e(Map<String, ?> map) {
return map.isEmpty();
}

final Field[] declaredFields;
final Field[] publicFields;
final Field[] publicUnhiddenFields;
Expand All @@ -1056,6 +1066,8 @@ private static boolean z(Object[] array) { // for better readability above
final Class<?>[] declaredClasses;
final Class<?>[] publicClasses;
final Object[] recordComponents;
final Map<String, Annotation[]> recordAnnotations; // component name => annotations
final Map<String, AnnotatedType> recordAnnotatedType; // component name => annotated type

/**
* The result of {@link Class#getEnclosingMethod()} or
Expand All @@ -1066,7 +1078,7 @@ private static boolean z(Object[] array) { // for better readability above
ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor<?>[] declaredConstructors,
Constructor<?>[] publicConstructors, Constructor<?> nullaryConstructor, Field[] declaredPublicFields, Method[] declaredPublicMethods, Class<?>[] declaredClasses,
Class<?>[] publicClasses, Executable enclosingMethodOrConstructor,
Object[] recordComponents) {
Object[] recordComponents, Map<String, Annotation[]> recordAnnotations, Map<String, AnnotatedType> recordAnnotatedType) {
this.declaredFields = declaredFields;
this.publicFields = publicFields;
this.publicUnhiddenFields = publicUnhiddenFields;
Expand All @@ -1081,6 +1093,8 @@ private static boolean z(Object[] array) { // for better readability above
this.publicClasses = publicClasses;
this.enclosingMethodOrConstructor = enclosingMethodOrConstructor;
this.recordComponents = recordComponents;
this.recordAnnotations = recordAnnotations;
this.recordAnnotatedType = recordAnnotatedType;
}
}

Expand Down Expand Up @@ -1218,11 +1232,21 @@ Method[] privateGetPublicMethods() {
@TargetElement(onlyWith = JDK16OrLater.class)
private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() {
Object[] result = rd.recordComponents;
Map<String, Annotation[]> annotations = rd.recordAnnotations;
Map<String, AnnotatedType> annotatedTypes = rd.recordAnnotatedType;
if (result == null) {
/* See ReflectionDataBuilder.buildRecordComponents() for details. */
throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " +
"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.");
}
// Set component annotations for the the record-components
for (Object rec : result) {
Target_java_lang_reflect_RecordComponent recordComp = (Target_java_lang_reflect_RecordComponent) rec;
Annotation[] annot = annotations.get(recordComp.getName());
AnnotatedType type = annotatedTypes.get(recordComp.getName());
recordComp.componentAnnotations = annot;
recordComp.annotatedType = type;
}
return (Target_java_lang_reflect_RecordComponent[]) result;
}

Expand Down Expand Up @@ -1671,4 +1695,41 @@ final class Target_jdk_internal_reflect_ConstantPool {

@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK16OrLater.class)
final class Target_java_lang_reflect_RecordComponent {

@Inject //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
volatile Annotation[] componentAnnotations;

@Inject //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
volatile AnnotatedType annotatedType;

@Substitute //
public Annotation[] getAnnotations() {
return componentAnnotations;
}

@Substitute //
public Annotation[] getDeclaredAnnotations() {
return componentAnnotations;
}

@Substitute //
public Annotation getAnnotation(Class<?> annotationClass) {
for (Annotation ann : componentAnnotations) {
if (annotationClass.isAssignableFrom(ann.annotationType())) {
return ann;
}
}
return null;
}

@Substitute //
public AnnotatedType getAnnotatedType() {
return annotatedType;
}

@Alias //
public native String getName();

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ public ReflectionData getCompleteReflectionData() {
completeReflectionData = new ReflectionData(hub.rd.declaredFields, hub.rd.publicFields, hub.rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]),
newPublicMethods.toArray(new Method[0]),
newDeclaredConstructors.toArray(new Constructor<?>[0]), newPublicConstructors.toArray(new Constructor<?>[0]), hub.rd.nullaryConstructor, hub.rd.declaredPublicFields,
newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor, hub.rd.recordComponents);
newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor,
hub.rd.recordComponents, hub.rd.recordAnnotations, hub.rd.recordAnnotatedType);
}
return completeReflectionData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@

// Checkstyle: allow reflection

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;

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

public abstract Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz);

public abstract Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz);

/**
* Returns the {@code RecordComponent.getAccessor} method for each of the
* {@code Class.getRecordComponents()}.
Expand Down Expand Up @@ -101,6 +108,16 @@ public Method[] getRecordComponentAccessorMethods(Class<?> clazz) {
public Constructor<?> getCanonicalRecordConstructor(Class<?> clazz) {
throw VMError.shouldNotReachHere();
}

@Override
public Map<String, Annotation[]> getRecordComponentsAnnotations(Class<?> clazz) {
throw VMError.shouldNotReachHere();
}

@Override
public Map<String, AnnotatedType> getRecordComponentAnnotatedType(Class<?> clazz) {
throw VMError.shouldNotReachHere();
}
}

@AutomaticFeature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
Expand Down Expand Up @@ -134,6 +135,8 @@ private static DynamicHub.ReflectionData getArrayReflectionData() {
EMPTY_CLASSES,
EMPTY_CLASSES,
null,
null,
null,
null);
}

Expand Down Expand Up @@ -519,7 +522,9 @@ private void processClass(DuringAnalysisAccessImpl access, Class<?> clazz) {
filterClasses(declaredClasses, reflectionClasses, access),
filterClasses(classes, reflectionClasses, access),
enclosingMethodOrConstructor(clazz),
buildRecordComponents(clazz, access));
buildRecordComponents(clazz, access),
buildRecordComponentAnnotations(clazz),
buildRecordAnnotatedTypes(clazz));
}
hub.setReflectionData(reflectionData);
}
Expand Down Expand Up @@ -562,6 +567,22 @@ private Object[] buildRecordComponents(Class<?> clazz, DuringAnalysisAccessImpl
}
}

private static Map<String, Annotation[]> buildRecordComponentAnnotations(Class<?> clazz) {
RecordSupport support = RecordSupport.singleton();
if (!support.isRecord(clazz)) {
return null;
}
return support.getRecordComponentsAnnotations(clazz);
}

private static Map<String, AnnotatedType> buildRecordAnnotatedTypes(Class<?> clazz) {
RecordSupport support = RecordSupport.singleton();
if (!support.isRecord(clazz)) {
return null;
}
return support.getRecordComponentAnnotatedType(clazz);
}

private static void reportLinkingErrors(Class<?> clazz, List<Throwable> errors) {
if (errors.isEmpty()) {
return;
Expand Down

0 comments on commit aac4af0

Please sign in to comment.