From bed01b601beebfd142483f568150c9aa2cba5a06 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 15 Oct 2013 20:41:49 +0400 Subject: [PATCH] Annotations supported --- .../jet/lang/resolve/java/lazy/context.kt | 8 +- .../LazyJavaAnnotationDescriptor.kt | 150 ++++++++++++++++++ .../descriptors/LazyJavaClassDescriptor.kt | 5 +- .../jet/lang/resolve/java/lazy/resolvers.kt | 7 +- .../annotations/AnnotationDescriptor.java | 2 + .../org/jetbrains/jet/utils/collections.kt | 21 ++- 6 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/context.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/context.kt index 7f31af54505..e4eb154009f 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/context.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/context.kt @@ -21,6 +21,9 @@ import org.jetbrains.jet.lang.resolve.java.lazy.types.LazyJavaTypeResolver import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import org.jetbrains.jet.lang.resolve.java.structure.JavaTypeParameter import org.jetbrains.jet.lang.resolve.java.JavaClassFinder +import org.jetbrains.jet.lang.resolve.java.structure.JavaAnnotation +import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaAnnotationDescriptor +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor open class LazyJavaResolverContext( val storageManager: StorageManager, @@ -48,4 +51,7 @@ class LazyJavaResolverContextWithTypes( fun LazyJavaResolverContextWithTypes.child( containingDeclaration: DeclarationDescriptor, typeParameters: Set -): LazyJavaResolverContextWithTypes = this.withTypes(LazyJavaTypeParameterResolver(this, containingDeclaration, typeParameters)) \ No newline at end of file +): LazyJavaResolverContextWithTypes = this.withTypes(LazyJavaTypeParameterResolver(this, containingDeclaration, typeParameters)) + +fun LazyJavaResolverContextWithTypes.resolveAnnotations(javaAnnotations: Collection): List + = javaAnnotations.map {jAnnotation -> LazyJavaAnnotationDescriptor(this, jAnnotation)} \ No newline at end of file diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt new file mode 100644 index 00000000000..cd3c89e550b --- /dev/null +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt @@ -0,0 +1,150 @@ +package org.jetbrains.jet.lang.resolve.java.lazy.descriptors + +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant +import org.jetbrains.jet.lang.resolve.java.lazy.LazyJavaResolverContextWithTypes +import org.jetbrains.jet.lang.resolve.java.structure.* +import org.jetbrains.jet.lang.types.ErrorUtils +import org.jetbrains.jet.lang.resolve.constants.* +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.descriptors.VariableDescriptor +import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationArgumentResolver +import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver.* +import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames +import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils +import org.jetbrains.jet.lang.resolve.DescriptorUtils +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor +import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage +import org.jetbrains.jet.lang.types.TypeProjectionImpl +import org.jetbrains.jet.lang.types.JetTypeImpl +import org.jetbrains.jet.lang.types.TypeConstructor +import org.jetbrains.jet.lang.types.TypeProjection +import org.jetbrains.jet.lang.resolve.scopes.JetScope +import org.jetbrains.jet.lang.resolve.java.lazy.types.LazyType +import org.jetbrains.jet.utils.valuesToMap +import org.jetbrains.jet.utils.keysToMap +import org.jetbrains.jet.utils.keysToMapExceptNulls +import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver +import org.jetbrains.jet.renderer.DescriptorRenderer + +class LazyJavaAnnotationDescriptor( + private val c: LazyJavaResolverContextWithTypes, + val javaAnnotation : JavaAnnotation +) : AnnotationDescriptor { + + private val _type = c.storageManager.createLazyValue {() : JetType -> + val fqName = javaAnnotation.getFqName() + if (fqName == null) return@createLazyValue ErrorUtils.createErrorType("No fqName: $javaAnnotation") + val annotationClass = c.javaClassResolver.resolveClassByFqName(fqName) + annotationClass?.getDefaultType() ?: ErrorUtils.createErrorType(fqName.asString()) + } + + override fun getType(): JetType = _type() + + private val _nameToArgument = c.storageManager.createLazyValue { + javaAnnotation.getArguments().valuesToMap { a -> a.getName() } + } + + private val _valueArguments = c.storageManager.createMemoizedFunctionWithNullableValues> { + valueParameter -> + val nameToArg = _nameToArgument() + + var javaAnnotationArgument = nameToArg[valueParameter.getName()] + if (javaAnnotationArgument == null && valueParameter.getName() == JavaAnnotationResolver.DEFAULT_ANNOTATION_MEMBER_NAME) { + javaAnnotationArgument = nameToArg[null] + } + + resolveAnnotationArgument(javaAnnotationArgument) + } + + override fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor) = _valueArguments(valueParameterDescriptor) + + private val _allValueArguments = c.storageManager.createLazyValue { + val constructors = getAnnotationClass().getConstructors() + if (constructors.isEmpty()) mapOf>() + constructors.first().getValueParameters().keysToMapExceptNulls { + vp -> getValueArgument(vp) + } + } + + override fun getAllValueArguments() = _allValueArguments() + + private fun getAnnotationClass() = getType().getConstructor().getDeclarationDescriptor() as ClassDescriptor + + private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? { + return when (argument) { + is JavaLiteralAnnotationArgument -> JavaAnnotationArgumentResolver.resolveCompileTimeConstantValue(argument.getValue(), null) + is JavaReferenceAnnotationArgument -> resolveFromReference(argument.resolve()) + is JavaArrayAnnotationArgument -> resolveFromArray(argument.getName() ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements()) + is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation()) + is JavaClassObjectAnnotationArgument -> resolveFromJavaClassObjectType(argument.getReferencedType()) + else -> null + } + } + + private fun resolveFromAnnotation(javaAnnotation: JavaAnnotation): CompileTimeConstant<*>? { + val fqName = javaAnnotation.getFqName() + if (fqName == null) return null + + if (JavaAnnotationResolver.isSpecialAnnotation(fqName)) { + return null + } + + return AnnotationValue(LazyJavaAnnotationDescriptor(c, javaAnnotation)) + } + + private fun resolveFromArray(argumentName: Name, elements: List): CompileTimeConstant<*>? { + if (getType().isError()) return null + + val valueParameter = DescriptorResolverUtils.getAnnotationParameterByName(argumentName, getAnnotationClass()) + if (valueParameter == null) return null + + val values = elements.map { + argument -> resolveAnnotationArgument(argument) ?: NullValue.NULL + } + return ArrayValue(values, valueParameter.getType()) + } + + private fun resolveFromReference(element: JavaElement?): CompileTimeConstant<*>? { + if (element !is JavaField) return null + + if (!element.isEnumEntry()) return null + + val fqName = element.getContainingClass().getFqName() + if (fqName == null) return null + + val enumClass = c.javaClassResolver.resolveClassByFqName(fqName) + if (enumClass == null) return null + + val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(element.getName()) + if (classifier !is ClassDescriptor) return null + + return EnumValue(classifier) + } + + private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? { + val `type` = c.typeResolver.transformJavaType(javaType, TypeUsage.MEMBER_SIGNATURE_INVARIANT) + val jlClass = c.javaClassResolver.resolveClassByFqName(JavaAnnotationArgumentResolver.JL_CLASS_FQ_NAME) + if (jlClass == null) return null + + val arguments = listOf(TypeProjectionImpl(`type`)) + + val javaClassObjectType = object : LazyType(c.storageManager) { + override fun computeTypeConstructor() = jlClass.getTypeConstructor() + override fun computeArguments() = arguments + override fun computeMemberScope() = jlClass.getMemberScope(arguments) + } + + return JavaClassValue(javaClassObjectType) + } + + + override fun toString(): String { + return DescriptorRenderer.TEXT.renderAnnotation(this) + } +} \ No newline at end of file diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt index a17e664cce9..a73d4908dc6 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt @@ -18,6 +18,7 @@ import org.jetbrains.jet.lang.resolve.java.lazy.LazyJavaResolverContextWithTypes import org.jetbrains.jet.lang.resolve.DescriptorFactory import org.jetbrains.jet.lang.resolve.java.lazy.child import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage +import org.jetbrains.jet.lang.resolve.java.lazy.resolveAnnotations class LazyJavaClassDescriptor( private val c: LazyJavaResolverContextWithTypes, @@ -63,8 +64,8 @@ class LazyJavaClassDescriptor( override fun getClassObjectDescriptor(): ClassDescriptor? = null - // TODO - override fun getAnnotations(): List = Collections.emptyList() + private val _annotations = c.resolveAnnotations(jClass.getAnnotations()) + override fun getAnnotations(): List = _annotations private inner class LazyJavaClassTypeConstructor : TypeConstructor { diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/resolvers.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/resolvers.kt index 9d2046db01b..3aa4fb30760 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/resolvers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/resolvers.kt @@ -20,13 +20,14 @@ import org.jetbrains.jet.lang.resolve.java.structure.JavaClass import org.jetbrains.jet.lang.descriptors.ClassDescriptor import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor import org.jetbrains.jet.lang.resolve.java.structure.JavaTypeParameter -import org.jetbrains.jet.utils.toMap +import org.jetbrains.jet.utils.valuesToMap import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaTypeParameterDescriptor import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor -import org.jetbrains.jet.lang.resolve.java.lazy.types.LazyJavaTypeResolver +import org.jetbrains.jet.lang.resolve.name.FqName trait LazyJavaClassResolver { fun resolveClass(javaClass: JavaClass): ClassDescriptor? + fun resolveClassByFqName(fqName: FqName): ClassDescriptor? } trait TypeParameterResolver { @@ -44,7 +45,7 @@ class TypeParameterResolverImpl( private val parent: TypeParameterResolver = TypeParameterResolver.EMPTY ) : TypeParameterResolver { - private val parameters = _typeParameters.toMap { p -> p.javaTypeParameter } + private val parameters = _typeParameters.valuesToMap { p -> p.javaTypeParameter } override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? { return parameters[javaTypeParameter] ?: parent.resolveTypeParameter(javaTypeParameter) diff --git a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/annotations/AnnotationDescriptor.java b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/annotations/AnnotationDescriptor.java index 4c01c32dbe3..cf143a472a9 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/annotations/AnnotationDescriptor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/annotations/AnnotationDescriptor.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.descriptors.annotations; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ReadOnly; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.types.JetType; @@ -16,5 +17,6 @@ public interface AnnotationDescriptor { CompileTimeConstant getValueArgument(@NotNull ValueParameterDescriptor valueParameterDescriptor); @NotNull + @ReadOnly Map> getAllValueArguments(); } diff --git a/core/util.runtime/src/org/jetbrains/jet/utils/collections.kt b/core/util.runtime/src/org/jetbrains/jet/utils/collections.kt index 199d3fb575a..8ae8156de3b 100644 --- a/core/util.runtime/src/org/jetbrains/jet/utils/collections.kt +++ b/core/util.runtime/src/org/jetbrains/jet/utils/collections.kt @@ -18,10 +18,29 @@ package org.jetbrains.jet.utils import java.util.LinkedHashMap -public fun Iterable.toMap(key: (V) -> K): Map { +public fun Iterable.valuesToMap(key: (V) -> K): Map { val map = LinkedHashMap() for (v in this) { map[key(v)] = v } return map +} + +public fun Iterable.keysToMap(value: (K) -> V): Map { + val map = LinkedHashMap() + for (k in this) { + map[k] = value(k) + } + return map +} + +public fun Iterable.keysToMapExceptNulls(value: (K) -> V?): Map { + val map = LinkedHashMap() + for (k in this) { + val v = value(k) + if (v != null) { + map[k] = v + } + } + return map } \ No newline at end of file