JVM_IR. Support type annotations

This commit is contained in:
Mikhael Bogdanov
2020-01-28 13:06:22 +01:00
parent e4258e528f
commit 6c07dbf351
14 changed files with 191 additions and 70 deletions
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.types.FlexibleType;
import org.jetbrains.kotlin.types.FlexibleTypesKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext;
import org.jetbrains.org.objectweb.asm.*;
import java.lang.annotation.*;
@@ -675,9 +676,9 @@ public abstract class AnnotationCodegen {
return;
}
Iterable<TypePathInfo> infos =
new TypeAnnotationCollector().collectTypeAnnotations(type, TypeReference.METHOD_FORMAL_PARAMETER);
for (TypePathInfo info : infos) {
Iterable<TypePathInfo<AnnotationDescriptor>> infos =
new PsiTypeAnnotationCollector().collectTypeAnnotations(type);
for (TypePathInfo<AnnotationDescriptor> info : infos) {
for (AnnotationDescriptor annotationDescriptor : info.getAnnotations()) {
genAnnotation(annotationDescriptor, info.getPath(), true);
}
@@ -5,26 +5,29 @@
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.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeVariance
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.TypePath
class TypePathInfo(
class TypePathInfo<T>(
val path: TypePath?,
val annotations: List<AnnotationDescriptor>
val annotations: List<T>
)
private class State(val type: Int, val path: MutableList<String>) {
private class State<T>(val path: MutableList<String>) {
val results = arrayListOf<TypePathInfo>()
val results = arrayListOf<TypePathInfo<T>>()
fun addStep(step: String) {
@@ -35,56 +38,69 @@ private class State(val type: Int, val path: MutableList<String>) {
path.removeAt(path.lastIndex)
}
fun rememberAnnotations(annotations: List<AnnotationDescriptor>) {
fun rememberAnnotations(annotations: List<T>) {
results.add(TypePathInfo(TypePath.fromString(path.joinToString("")), annotations))
}
}
class TypeAnnotationCollector {
class PsiTypeAnnotationCollector : TypeAnnotationCollector<AnnotationDescriptor>(SimpleClassicTypeSystemContext) {
private lateinit var state: State
override fun KotlinTypeMarker.extractAnnotations(): List<AnnotationDescriptor> {
require(this is KotlinType)
return 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
isCompiledToJvm8OrHigher(it.annotationClass)
}
}
}
fun collectTypeAnnotations(kotlinType: KotlinType, annotationType: Int): ArrayList<TypePathInfo> {
state = State(annotationType, arrayListOf())
kotlinType.collectTypeAnnotations()
abstract class TypeAnnotationCollector<T>(val context: TypeSystemCommonBackendContext) {
private lateinit var state: State<T>
fun collectTypeAnnotations(kotlinType: KotlinTypeMarker): ArrayList<TypePathInfo<T>> {
state = State(arrayListOf())
kotlinType.gatherTypeAnnotations()
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
}
private fun KotlinTypeMarker.gatherTypeAnnotations() {
with(context) {
if (isFlexible()) {
return upperBoundIfFlexible().gatherTypeAnnotations()
} else if (typeConstructor().isInnerClass()) {
//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) }
extractAnnotations().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;")
for (index in 0 until argumentsCount()) {
val type = getArgument(index)
//skip in/out variance for now it's not clear should type annotations on wildcard bound be supported or not
if (type.getVariance() == TypeVariance.INV) {
when {
this@gatherTypeAnnotations.isArrayOrNullableArray() -> type.getType().process("[")
else -> type.getType().process("$index;")
}
}
}
}
}
fun KotlinType.process(step: String) {
fun KotlinTypeMarker.process(step: String) {
state.addStep(step)
this.collectTypeAnnotations()
this.gatherTypeAnnotations()
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
}
abstract fun KotlinTypeMarker.extractAnnotations(): List<T>
fun isCompiledToJvm8OrHigher(descriptor: ClassDescriptor?): Boolean =
(descriptor as? DeserializedClassDescriptor)?.let { classDescriptor ->
((classDescriptor.source as? KotlinJvmBinarySourceElement)?.binaryClass as? FileBasedKotlinClass)?.classVersion ?: 0 >= Opcodes.V1_8
} ?: true
}