Support type annotations

#KT-35843 Fixed
This commit is contained in:
Mikhael Bogdanov
2019-12-30 09:12:28 +01:00
parent fde9b21a40
commit 2ed0cb2a89
52 changed files with 1728 additions and 10 deletions
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.*;
@@ -627,4 +628,38 @@ public abstract class AnnotationCodegen {
private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
return av == null ? NO_ANNOTATION_VISITOR : av;
}
public static void writeTypeAnnotations(
@NotNull MethodVisitor mv,
@NotNull GenerationState state,
int parameterIndex,
@Nullable KotlinType type,
@NotNull InnerClassConsumer innerClassConsumer
) {
if (type == null ||
state.getTarget() == JvmTarget.JVM_1_6 ||
!state.getConfiguration().getBoolean(JVMConfigurationKeys.EMIT_JVM_TYPE_ANNOTATIONS)) {
return;
}
Iterable<TypePathInfo> infos =
new TypeAnnotationCollector().collectTypeAnnotations(type, TypeReference.METHOD_FORMAL_PARAMETER);
for (TypePathInfo info : infos) {
for (AnnotationDescriptor annotationDescriptor : info.getAnnotations()) {
TypeReference typeReference = parameterIndex != -1 ?
TypeReference.newFormalParameterReference(parameterIndex)
: TypeReference.newTypeReference(TypeReference.METHOD_RETURN);
AnnotationCodegen codegen = new AnnotationCodegen(innerClassConsumer, state) {
@NotNull
@Override
AnnotationVisitor visitAnnotation(String descr, boolean visible) {
return safe(mv.visitTypeAnnotation(typeReference.getValue(), info.getPath(), descr, visible));
}
};
codegen.genAnnotation(annotationDescriptor);
}
}
}
}
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
import org.jetbrains.kotlin.config.JvmDefaultMode;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
@@ -56,7 +55,10 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
@@ -531,7 +533,7 @@ public class FunctionCodegen {
continue;
}
Annotated annotated =
ParameterDescriptor annotated =
kind == JvmMethodParameterKind.VALUE
? iterator.next()
: kind == JvmMethodParameterKind.RECEIVER
@@ -540,10 +542,15 @@ public class FunctionCodegen {
if (annotated != null) {
//noinspection ConstantConditions
AnnotationCodegen.forParameter(i - syntheticParameterCount, mv, innerClassConsumer, state)
int parameterIndex = i - syntheticParameterCount;
AnnotationCodegen.forParameter(parameterIndex, mv, innerClassConsumer, state)
.genAnnotations(annotated, parameterSignature.getAsmType());
AnnotationCodegen.writeTypeAnnotations(mv, state, parameterIndex, annotated.getType(), innerClassConsumer);
}
}
AnnotationCodegen.writeTypeAnnotations(mv, state, -1, functionDescriptor.getReturnType(), innerClassConsumer);
}
@Nullable
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.types.*
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.TypePath
class TypePathInfo(
val path: TypePath?,
val annotations: List<AnnotationDescriptor>
)
private class State(val type: Int, val path: MutableList<String>) {
val results = arrayListOf<TypePathInfo>()
fun addStep(step: String) {
path.add(step)
}
fun removeStep(step: String) {
path.removeAt(path.lastIndex)
}
fun rememberAnnotations(annotations: List<AnnotationDescriptor>) {
results.add(TypePathInfo(TypePath.fromString(path.joinToString("")), annotations))
}
}
class TypeAnnotationCollector {
private lateinit var state: State
fun collectTypeAnnotations(kotlinType: KotlinType, annotationType: Int): ArrayList<TypePathInfo> {
state = State(annotationType, arrayListOf())
kotlinType.collectTypeAnnotations()
return state.results
}
private fun KotlinType.collectTypeAnnotations() {
if (isFlexible()) {
return upperIfFlexible().collectTypeAnnotations()
} else if ((this.constructor.declarationDescriptor as? ClassDescriptor)?.isInner == true) {
//skip inner classes for now it's not clear should type annotations on outer be supported or not
return
}
typeAnnotations.takeIf { it.isNotEmpty() }?.let { state.rememberAnnotations(it) }
arguments.forEachIndexed { index, type ->
//skip in/out variance for now it's not clear should type annotations on wildcard bound be supported or not
if (type.projectionKind == Variance.INVARIANT) {
when {
KotlinBuiltIns.isArray(this) -> type.type.process("[")
else -> type.type.process("$index;")
}
}
}
}
fun KotlinType.process(step: String) {
state.addStep(step)
this.collectTypeAnnotations()
state.removeStep(step)
}
private val KotlinType.typeAnnotations
get() = annotations.filter {
//We only generate annotations which have the TYPE_USE Java target.
// Those are type annotations which were compiled with JVM target bytecode version 1.8 or greater
(it.annotationClass as? DeserializedClassDescriptor)?.let { classDescriptor ->
((classDescriptor.source as? KotlinJvmBinarySourceElement)?.binaryClass as? FileBasedKotlinClass)?.classVersion ?: 0 >= Opcodes.V1_8
} ?: true
}
}