Annotation mapper is introduced to map java annotation targets to kotlin targets + a set of new / fixed tests

This commit is contained in:
Mikhail Glukhikh
2015-07-15 19:47:05 +03:00
parent 3ec00114c0
commit 1309c1f95f
17 changed files with 485 additions and 6 deletions
@@ -38,6 +38,7 @@ public final class JvmAnnotationNames {
public static final String KIND_FIELD_NAME = "kind";
public static final String DATA_FIELD_NAME = "data";
public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value");
public static final Name TARGET_ANNOTATION_MEMBER_NAME = Name.identifier("allowedTargets");
public static final FqName JETBRAINS_NOT_NULL_ANNOTATION = new FqName("org.jetbrains.annotations.NotNull");
public static final FqName JETBRAINS_NULLABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Nullable");
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.load.java.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument
import org.jetbrains.kotlin.load.java.structure.JavaArrayAnnotationArgument
import org.jetbrains.kotlin.load.java.structure.JavaEnumValueAnnotationArgument
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.storage.StorageManager
import java.lang.annotation.Target
import java.util.*
import kotlin.annotation
public object JavaAnnotationMapper {
private val targetClassId = ClassId.topLevel(FqName(javaClass<Target>().getCanonicalName()))
public fun mapJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? =
when (annotation.classId) {
targetClassId -> JavaTargetAnnotationDescriptor(annotation, c, c.module.builtIns)
else -> null
}
public val kotlinToJavaNameMap: Map<FqName, FqName> =
mapOf(KotlinBuiltIns.FQ_NAMES.target to FqName(javaClass<Target>().getCanonicalName()))
}
class JavaTargetAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext,
val builtIns: KotlinBuiltIns
): AnnotationDescriptor {
private val valueArguments = c.storageManager.createLazyValue {
val firstArgument = annotation.arguments.firstOrNull()
val targetArgument = when (firstArgument) {
is JavaArrayAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(firstArgument.getElements(), builtIns)
is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(listOf(firstArgument), builtIns)
else -> return@createLazyValue emptyMap<ValueParameterDescriptor, ConstantValue<*>>()
}
val parameterDescriptor = builtIns.targetAnnotation.constructors.firstOrNull()?.valueParameters?.firstOrNull()
parameterDescriptor?.let { mapOf(it to targetArgument) } ?: emptyMap()
}
override fun getAllValueArguments() = valueArguments()
override fun getType() = builtIns.targetAnnotation.defaultType
}
public object JavaAnnotationTargetMapper {
private val targetNameLists = mapOf("PACKAGE" to EnumSet.of(AnnotationTarget.PACKAGE),
"TYPE" to EnumSet.of(AnnotationTarget.CLASSIFIER),
"ANNOTATION_TYPE" to EnumSet.of(AnnotationTarget.ANNOTATION_CLASS),
"TYPE_PARAMETER" to EnumSet.of(AnnotationTarget.TYPE_PARAMETER),
"FIELD" to EnumSet.of(AnnotationTarget.FIELD),
"LOCAL_VARIABLE" to EnumSet.of(AnnotationTarget.LOCAL_VARIABLE),
"PARAMETER" to EnumSet.of(AnnotationTarget.VALUE_PARAMETER),
"CONSTRUCTOR" to EnumSet.of(AnnotationTarget.CONSTRUCTOR),
"METHOD" to EnumSet.of(AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER),
"TYPE_USE" to EnumSet.of(AnnotationTarget.TYPE)
)
public fun mapJavaTargetArgumentByName(argumentName: String?): Set<AnnotationTarget> = targetNameLists[argumentName] ?: emptySet()
public fun mapJavaTargetArguments(arguments: List<JavaAnnotationArgument>, builtIns: KotlinBuiltIns): ConstantValue<*>? {
// Map arguments: java.lang.annotation.Target -> kotlin.annotation.target
val kotlinTargets = arguments.filterIsInstance<JavaEnumValueAnnotationArgument>()
.flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) }
.map { builtIns.getAnnotationTargetEnumEntry(Name.identifier(it.name())) }
.filterNotNull()
.map { EnumValue(it) }
val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName(JvmAnnotationNames.TARGET_ANNOTATION_MEMBER_NAME,
builtIns.targetAnnotation)
return ArrayValue(kotlinTargets, parameterDescriptor?.type ?: ErrorUtils.createErrorType("Error: AnnotationTarget[]"), builtIns)
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.load.java.lazy
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper
import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationOwner
@@ -29,17 +30,18 @@ class LazyJavaAnnotations(
) : Annotations {
private val annotationDescriptors = c.storageManager.createMemoizedFunctionWithNullableValues {
annotation: JavaAnnotation ->
c.resolveAnnotation(annotation)
JavaAnnotationMapper.mapJavaAnnotation(annotation, c) ?: c.resolveAnnotation(annotation)
}
override fun findAnnotation(fqName: FqName) =
annotationOwner.findAnnotation(fqName)?.let(annotationDescriptors)
?: JavaAnnotationMapper.kotlinToJavaNameMap[fqName]?.let { annotationOwner.findAnnotation(it)?.let(annotationDescriptors) }
override fun findExternalAnnotation(fqName: FqName) =
c.externalAnnotationResolver.findExternalAnnotation(annotationOwner, fqName)?.let(annotationDescriptors)
override fun iterator() =
annotationOwner.getAnnotations().asSequence().map(annotationDescriptors).filterNotNull().iterator()
annotationOwner.annotations.asSequence().map(annotationDescriptors).filterNotNull().iterator()
override fun isEmpty() = !iterator().hasNext()
}
@@ -101,6 +101,7 @@ public class KotlinBuiltIns {
private final ModuleDescriptorImpl builtInsModule;
private final BuiltinsPackageFragment builtinsPackageFragment;
private final BuiltinsPackageFragment annotationPackageFragment;
private final Map<PrimitiveType, JetType> primitiveTypeToArrayJetType;
private final Map<JetType, JetType> primitiveJetTypeToJetArrayType;
@@ -130,6 +131,7 @@ public class KotlinBuiltIns {
builtInsModule.setDependencies(builtInsModule);
builtinsPackageFragment = (BuiltinsPackageFragment) single(packageFragmentProvider.getPackageFragments(BUILT_INS_PACKAGE_FQ_NAME));
annotationPackageFragment = (BuiltinsPackageFragment) single(packageFragmentProvider.getPackageFragments(ANNOTATION_PACKAGE_FQ_NAME));
primitiveTypeToArrayJetType = new EnumMap<PrimitiveType, JetType>(PrimitiveType.class);
primitiveJetTypeToJetArrayType = new HashMap<JetType, JetType>();
@@ -178,6 +180,7 @@ public class KotlinBuiltIns {
public final FqName extension = fqName("extension");
public final FqName target = annotationName("target");
public final FqName annotation = annotationName("annotation");
public final FqName annotationTarget = annotationName("AnnotationTarget");
public final FqNameUnsafe kClass = new FqName("kotlin.reflect.KClass").toUnsafe();
@@ -231,6 +234,14 @@ public class KotlinBuiltIns {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@NotNull
private ClassDescriptor getAnnotationClassByName(@NotNull Name simpleName) {
ClassifierDescriptor classifier = annotationPackageFragment.getMemberScope().getClassifier(simpleName);
assert classifier instanceof ClassDescriptor : "Must be a class descriptor " + simpleName + ", but was " +
(classifier == null ? "null" : classifier.toString());
return (ClassDescriptor) classifier;
}
@NotNull
public ClassDescriptor getBuiltInClassByName(@NotNull Name simpleName) {
ClassDescriptor classDescriptor = getBuiltInClassByNameNullable(simpleName);
@@ -377,6 +388,22 @@ public class KotlinBuiltIns {
return getBuiltInClassByName("deprecated");
}
@NotNull
public ClassDescriptor getTargetAnnotation() {
return getAnnotationClassByName(FQ_NAMES.target.shortName());
}
@NotNull
public ClassDescriptor getAnnotationTargetEnum() {
return getAnnotationClassByName(FQ_NAMES.annotationTarget.shortName());
}
@Nullable
public ClassDescriptor getAnnotationTargetEnumEntry(@NotNull Name name) {
ClassifierDescriptor result = getAnnotationTargetEnum().getUnsubstitutedInnerClassesScope().getClassifier(name);
return result instanceof ClassDescriptor ? (ClassDescriptor) result : null;
}
@NotNull
public ClassDescriptor getString() {
return getBuiltInClassByName("String");