Deserialize receiver parameter annotations for extension functions

This commit is contained in:
Yan Zhulanow
2015-08-03 21:10:41 +03:00
parent 397da8746a
commit 08c678ef46
12 changed files with 184 additions and 11 deletions
@@ -0,0 +1,22 @@
package test
target(AnnotationTarget.VALUE_PARAMETER)
annotation class Ann
class A {
@receiver:Ann
fun String.myLength(@Ann q:String): Int {
return length()
}
@receiver:Ann
val String.myLength2: Int
get() = length()
@receiver:Ann
var String.myLength3: Int
get() = length()
set(v) {}
}
@@ -0,0 +1,15 @@
package test
internal final class A {
/*primary*/ public constructor A()
@receiver:test.Ann() internal final val kotlin.String.myLength2: kotlin.Int
internal final fun kotlin.String.<get-myLength2>(): kotlin.Int
@receiver:test.Ann() internal final var kotlin.String.myLength3: kotlin.Int
internal final fun kotlin.String.<get-myLength3>(): kotlin.Int
internal final fun kotlin.String.<set-myLength3>(/*0*/ v: kotlin.Int): kotlin.Unit
@receiver:test.Ann() internal final fun kotlin.String.myLength(/*0*/ test.Ann() q: kotlin.String): kotlin.Int
}
kotlin.annotation.target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) kotlin.annotation.annotation() internal final class Ann : kotlin.Annotation {
/*primary*/ public constructor Ann()
}
@@ -2361,6 +2361,21 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
doTestCompiledKotlin(fileName);
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WithUseSiteTarget extends AbstractLoadJavaTest {
public void testAllFilesPresentInWithUseSiteTarget() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("ReceiverTarget.kt")
public void testReceiverTarget() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt");
doTestCompiledKotlin(fileName);
}
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/class")
@@ -470,6 +470,21 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD
doTest(fileName);
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WithUseSiteTarget extends AbstractJvmRuntimeDescriptorLoaderTest {
public void testAllFilesPresentInWithUseSiteTarget() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("ReceiverTarget.kt")
public void testReceiverTarget() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/class")
@@ -132,6 +132,22 @@ public abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C
return listOf()
}
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
callable: ProtoBuf.Callable,
nameResolver: NameResolver,
kind: AnnotatedCallableKind
): List<A> {
if (!callable.hasReceiverType()) return emptyList()
val methodSignature = getCallableSignature(callable, nameResolver, kind)
if (methodSignature != null) {
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, 0)
return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature)
}
return emptyList()
}
override fun loadTypeAnnotations(type: ProtoBuf.Type, nameResolver: NameResolver): List<A> {
return type.getExtension(JvmProtoBuf.typeAnnotation).map { loadTypeAnnotation(it, nameResolver) }
}
@@ -58,6 +58,13 @@ class BuiltInsAnnotationAndConstantLoader(
return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) }
}
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
callable: ProtoBuf.Callable,
nameResolver: NameResolver,
kind: AnnotatedCallableKind
): List<AnnotationDescriptor> = emptyList()
override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List<AnnotationDescriptor> {
// TODO: support type annotations in built-ins
return listOf()
@@ -47,6 +47,14 @@ public interface AnnotationAndConstantLoader<A, C> {
@NotNull ProtoBuf.Callable.ValueParameter proto
);
@NotNull
List<A> loadExtensionReceiverParameterAnnotations(
@NotNull ProtoContainer container,
@NotNull ProtoBuf.Callable callable,
@NotNull NameResolver nameResolver,
@NotNull AnnotatedCallableKind kind
);
@NotNull
List<A> loadTypeAnnotations(
@NotNull ProtoBuf.Type type,
@@ -17,6 +17,9 @@
package org.jetbrains.kotlin.serialization.deserialization
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
@@ -29,6 +32,7 @@ import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAL
import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAR
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
import org.jetbrains.kotlin.utils.toReadOnlyList
import org.jetbrains.kotlin.utils.sure
public class MemberDeserializer(private val c: DeserializationContext) {
public fun loadCallable(proto: Callable): CallableMemberDescriptor {
@@ -43,9 +47,16 @@ public class MemberDeserializer(private val c: DeserializationContext) {
private fun loadProperty(proto: Callable): PropertyDescriptor {
val flags = proto.getFlags()
val hasGetter = Flags.HAS_GETTER.get(flags)
val propertyAnnotations = if (hasGetter)
getAnnotationsWithReceiverTargeted(proto, AnnotatedCallableKind.PROPERTY, AnnotatedCallableKind.PROPERTY_GETTER)
else
getAnnotations(proto, flags, AnnotatedCallableKind.PROPERTY)
val property = DeserializedPropertyDescriptor(
c.containingDeclaration, null,
getAnnotations(proto, flags, AnnotatedCallableKind.PROPERTY),
propertyAnnotations,
modality(Flags.MODALITY.get(flags)),
visibility(Flags.VISIBILITY.get(flags)),
Flags.CALLABLE_KIND.get(flags) == Callable.CallableKind.VAR,
@@ -63,7 +74,7 @@ public class MemberDeserializer(private val c: DeserializationContext) {
if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null
)
val getter = if (Flags.HAS_GETTER.get(flags)) {
val getter = if (hasGetter) {
val getterFlags = proto.getGetterFlags()
val isNotDefault = proto.hasGetterFlags() && Flags.IS_NOT_DEFAULT.get(getterFlags)
val getter = if (isNotDefault) {
@@ -128,11 +139,13 @@ public class MemberDeserializer(private val c: DeserializationContext) {
}
private fun loadFunction(proto: Callable): CallableMemberDescriptor {
val annotations = getAnnotations(proto, proto.getFlags(), AnnotatedCallableKind.FUNCTION)
val annotations = getAnnotationsWithReceiverTargeted(proto, AnnotatedCallableKind.FUNCTION)
val function = DeserializedSimpleFunctionDescriptor.create(c.containingDeclaration, proto, c.nameResolver, annotations)
val local = c.childContext(function, proto.getTypeParameterList())
val receiverParameterType = if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null
function.initialize(
if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null,
receiverParameterType,
getDispatchReceiverParameter(),
local.typeDeserializer.ownTypeParameters,
local.memberDeserializer.valueParameters(proto, AnnotatedCallableKind.FUNCTION),
@@ -163,6 +176,29 @@ public class MemberDeserializer(private val c: DeserializationContext) {
return descriptor
}
private fun getAnnotationsWithReceiverTargeted(
proto: Callable,
kind: AnnotatedCallableKind,
receiverTargetedKind: AnnotatedCallableKind = kind
): Annotations {
return DeserializedAnnotationsWithPossibleTargets(c.storageManager) {
val annotations = arrayListOf<AnnotationWithTarget>()
val container = c.containingDeclaration.asProtoContainer()
annotations += c.components.annotationAndConstantLoader
.loadCallableAnnotations(container, proto, c.nameResolver, kind)
.map { AnnotationWithTarget(it, null) }
if (proto.hasReceiverType()) {
annotations += c.components.annotationAndConstantLoader
.loadExtensionReceiverParameterAnnotations(container, proto, c.nameResolver, receiverTargetedKind)
.map { AnnotationWithTarget(it, AnnotationUseSiteTarget.RECEIVER) }
}
annotations
}
}
private fun getAnnotations(proto: Callable, flags: Int, kind: AnnotatedCallableKind): Annotations {
if (!Flags.HAS_ANNOTATIONS.get(flags)) {
return Annotations.EMPTY
@@ -28,22 +28,32 @@ import org.jetbrains.kotlin.utils.toReadOnlyList
class DeserializedAnnotations(
storageManager: StorageManager,
compute: () -> List<AnnotationDescriptor>
) : DeserializedAnnotationsWithPossibleTargets(
storageManager,
{ compute().map { AnnotationWithTarget(it, null) } })
open class DeserializedAnnotationsWithPossibleTargets(
storageManager: StorageManager,
compute: () -> List<AnnotationWithTarget>
) : Annotations {
private val annotations = storageManager.createLazyValue { compute().toReadOnlyList() }
override fun isEmpty(): Boolean = annotations().isEmpty()
override fun findAnnotation(fqName: FqName) = annotations().firstOrNull {
annotation ->
val descriptor = annotation.getType().getConstructor().getDeclarationDescriptor()
annotationWithTarget ->
if (annotationWithTarget.target != null) return@firstOrNull false
val descriptor = annotationWithTarget.annotation.type.constructor.declarationDescriptor
descriptor is ClassDescriptor && fqName.equalsTo(DescriptorUtils.getFqName(descriptor))
}
}?.annotation
override fun findExternalAnnotation(fqName: FqName) = null
override fun getUseSiteTargetedAnnotations() = emptyList<AnnotationWithTarget>()
override fun getUseSiteTargetedAnnotations() = annotations().filter { it.target != null }
override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) }
override fun getAllAnnotations() = annotations()
override fun iterator(): Iterator<AnnotationDescriptor> = annotations().iterator()
}
override fun iterator(): Iterator<AnnotationDescriptor> {
return annotations().asSequence().filter { it.target == null }.map { it.annotation }.iterator()
}
}
@@ -49,6 +49,13 @@ public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndCon
): List<ClassId> =
proto.getExtension(JsProtoBuf.parameterAnnotation).orEmpty().map { nameResolver.getClassId(it.getId()) }
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
callable: ProtoBuf.Callable,
nameResolver: NameResolver,
kind: AnnotatedCallableKind
): List<ClassId> = emptyList()
override fun loadTypeAnnotations(
proto: ProtoBuf.Type,
nameResolver: NameResolver
@@ -468,6 +468,21 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WithUseSiteTarget extends AbstractResolveByStubTest {
public void testAllFilesPresentInWithUseSiteTarget() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("ReceiverTarget.kt")
public void testReceiverTarget() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlin/class")
@@ -57,6 +57,13 @@ class KotlinJavascriptAnnotationAndConstantLoader(
return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) }
}
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
callable: ProtoBuf.Callable,
nameResolver: NameResolver,
kind: AnnotatedCallableKind
): List<AnnotationDescriptor> = emptyList()
override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List<AnnotationDescriptor> {
val annotations = proto.getExtension(JsProtoBuf.typeAnnotation).orEmpty()
return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) }