Implementation of Kotlin's 'target' annotation mapping to Java's 'Target' annotation + tests
This commit is contained in:
@@ -24,8 +24,10 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget;
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.AnnotationTargetChecker;
|
||||
import org.jetbrains.kotlin.resolve.constants.*;
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
||||
import org.jetbrains.kotlin.types.Flexibility;
|
||||
@@ -34,8 +36,10 @@ import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.TypesPackage;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getClassObjectType;
|
||||
@@ -123,6 +127,7 @@ public abstract class AnnotationCodegen {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) annotated;
|
||||
if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) {
|
||||
generateRetentionAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
|
||||
generateTargetAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,6 +171,47 @@ public abstract class AnnotationCodegen {
|
||||
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass);
|
||||
}
|
||||
|
||||
private static final Map<AnnotationTarget, ElementType> annotationTargetMap =
|
||||
new EnumMap<AnnotationTarget, ElementType>(AnnotationTarget.class);
|
||||
|
||||
static {
|
||||
annotationTargetMap.put(AnnotationTarget.PACKAGE, ElementType.PACKAGE);
|
||||
annotationTargetMap.put(AnnotationTarget.CLASSIFIER, ElementType.TYPE);
|
||||
annotationTargetMap.put(AnnotationTarget.ANNOTATION_CLASS, ElementType.ANNOTATION_TYPE);
|
||||
annotationTargetMap.put(AnnotationTarget.CONSTRUCTOR, ElementType.CONSTRUCTOR);
|
||||
annotationTargetMap.put(AnnotationTarget.LOCAL_VARIABLE, ElementType.LOCAL_VARIABLE);
|
||||
annotationTargetMap.put(AnnotationTarget.FUNCTION, ElementType.METHOD);
|
||||
annotationTargetMap.put(AnnotationTarget.PROPERTY_GETTER, ElementType.METHOD);
|
||||
annotationTargetMap.put(AnnotationTarget.PROPERTY_SETTER, ElementType.METHOD);
|
||||
annotationTargetMap.put(AnnotationTarget.FIELD, ElementType.FIELD);
|
||||
annotationTargetMap.put(AnnotationTarget.VALUE_PARAMETER, ElementType.PARAMETER);
|
||||
}
|
||||
|
||||
private void generateTargetAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
|
||||
String descriptor = Type.getType(Target.class).getDescriptor();
|
||||
if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
|
||||
Set<AnnotationTarget> targets = AnnotationTargetChecker.INSTANCE$.possibleTargetSet(classDescriptor);
|
||||
Set<ElementType> javaTargets;
|
||||
if (targets == null) {
|
||||
javaTargets = getJavaTargetList(classDescriptor);
|
||||
if (javaTargets == null) return;
|
||||
}
|
||||
else {
|
||||
javaTargets = EnumSet.noneOf(ElementType.class);
|
||||
for (AnnotationTarget target : targets) {
|
||||
if (annotationTargetMap.get(target) == null) continue;
|
||||
javaTargets.add(annotationTargetMap.get(target));
|
||||
}
|
||||
}
|
||||
AnnotationVisitor visitor = visitAnnotation(descriptor, true);
|
||||
AnnotationVisitor arrayVisitor = visitor.visitArray("value");
|
||||
for (ElementType javaTarget : javaTargets) {
|
||||
arrayVisitor.visitEnum(null, Type.getType(ElementType.class).getDescriptor(), javaTarget.name());
|
||||
}
|
||||
arrayVisitor.visitEnd();
|
||||
visitor.visitEnd();
|
||||
}
|
||||
|
||||
private void generateRetentionAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
|
||||
RetentionPolicy policy = getRetentionPolicy(classDescriptor);
|
||||
String descriptor = Type.getType(Retention.class).getDescriptor();
|
||||
@@ -337,18 +383,46 @@ public abstract class AnnotationCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<ElementType> getJavaTargetList(ClassDescriptor descriptor) {
|
||||
AnnotationDescriptor targetAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Target.class.getName()));
|
||||
if (targetAnnotation != null) {
|
||||
Collection<ConstantValue<?>> valueArguments = targetAnnotation.getAllValueArguments().values();
|
||||
if (!valueArguments.isEmpty()) {
|
||||
ConstantValue<?> compileTimeConstant = valueArguments.iterator().next();
|
||||
if (compileTimeConstant instanceof ArrayValue) {
|
||||
List<? extends ConstantValue<?>> values = ((ArrayValue) compileTimeConstant).getValue();
|
||||
Set<ElementType> result = EnumSet.noneOf(ElementType.class);
|
||||
for (ConstantValue<?> value : values) {
|
||||
if (value instanceof EnumValue) {
|
||||
ClassDescriptor enumEntry = ((EnumValue) value).getValue();
|
||||
JetType classObjectType = getClassObjectType(enumEntry);
|
||||
if (classObjectType != null) {
|
||||
if ("java/lang/annotation/ElementType".equals(typeMapper.mapType(classObjectType).getInternalName())) {
|
||||
result.add(ElementType.valueOf(enumEntry.getName().asString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
|
||||
AnnotationDescriptor kotlinAnnotation = descriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.annotation);
|
||||
if (kotlinAnnotation != null) {
|
||||
for (Map.Entry<ValueParameterDescriptor, ConstantValue<?>> argument: kotlinAnnotation.getAllValueArguments().entrySet()) {
|
||||
for (Map.Entry<ValueParameterDescriptor, ConstantValue<?>> argument : kotlinAnnotation.getAllValueArguments().entrySet()) {
|
||||
if ("retention".equals(argument.getKey().getName().asString()) && argument.getValue() instanceof EnumValue) {
|
||||
ClassDescriptor enumEntry = ((EnumValue) argument.getValue()).getValue();
|
||||
JetType classObjectType = getClassObjectType(enumEntry);
|
||||
if (classObjectType != null) {
|
||||
if ("kotlin/annotation/AnnotationRetention".equals(typeMapper.mapType(classObjectType).getInternalName())) {
|
||||
String entryName = enumEntry.getName().asString();
|
||||
for (KotlinRetention retention: KotlinRetention.values()) {
|
||||
for (KotlinRetention retention : KotlinRetention.values()) {
|
||||
if (retention.name().equals(entryName)) return retention.mapped;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user