Support type annotations
#KT-35843 Fixed
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user