From 5b780ec56c367dc7695e7fe77e79727e28a24be6 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 28 Oct 2016 05:24:47 +0300 Subject: [PATCH] Kapt3: Parse generic signatures of methods and fields --- .../jetbrains/kotlin/kapt3/JCTreeConverter.kt | 58 ++++--- .../kotlin/kapt3/SignatureParserVisitor.kt | 151 ++++++++++++++++-- .../src/org/jetbrains/kotlin/kapt3/utils.kt | 10 ++ .../kapt3/testData/converter/annotations.txt | 4 +- .../kapt3/testData/converter/functions.txt | 9 +- .../kapt3/testData/converter/genericSimple.kt | 9 +- .../testData/converter/genericSimple.txt | 15 ++ .../testData/converter/nestedClasses.txt | 2 +- 8 files changed, 220 insertions(+), 38 deletions(-) diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt index cd1c969204f..23e87fb429e 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.kapt3 +import com.sun.tools.javac.code.Flags import com.sun.tools.javac.code.TypeTag import com.sun.tools.javac.file.JavacFileManager import com.sun.tools.javac.tree.JCTree @@ -44,17 +45,19 @@ class JCTreeConverter( val origins: Map ) { private companion object { - private val VISIBILITY_MODIFIERS = Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED - private val MODALITY_MODIFIERS = Opcodes.ACC_FINAL or Opcodes.ACC_ABSTRACT + private val VISIBILITY_MODIFIERS = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).toLong() + private val MODALITY_MODIFIERS = (Opcodes.ACC_FINAL or Opcodes.ACC_ABSTRACT).toLong() private val CLASS_MODIFIERS = VISIBILITY_MODIFIERS or MODALITY_MODIFIERS or - Opcodes.ACC_DEPRECATED or Opcodes.ACC_INTERFACE or Opcodes.ACC_ANNOTATION or Opcodes.ACC_ENUM or Opcodes.ACC_STATIC + (Opcodes.ACC_DEPRECATED or Opcodes.ACC_INTERFACE or Opcodes.ACC_ANNOTATION or Opcodes.ACC_ENUM or Opcodes.ACC_STATIC).toLong() private val METHOD_MODIFIERS = VISIBILITY_MODIFIERS or MODALITY_MODIFIERS or - Opcodes.ACC_SYNCHRONIZED or Opcodes.ACC_STATIC or Opcodes.ACC_NATIVE or Opcodes.ACC_DEPRECATED + (Opcodes.ACC_SYNCHRONIZED or Opcodes.ACC_STATIC or Opcodes.ACC_NATIVE or Opcodes.ACC_DEPRECATED).toLong() private val FIELD_MODIFIERS = VISIBILITY_MODIFIERS or MODALITY_MODIFIERS or - Opcodes.ACC_STATIC or Opcodes.ACC_VOLATILE or Opcodes.ACC_TRANSIENT or Opcodes.ACC_ENUM + (Opcodes.ACC_STATIC or Opcodes.ACC_VOLATILE or Opcodes.ACC_TRANSIENT or Opcodes.ACC_ENUM).toLong() + + private val PARAMETER_MODIFIERS = FIELD_MODIFIERS or Flags.PARAMETER or Flags.VARARGS or Opcodes.ACC_FINAL.toLong() private val BLACKLISTED_ANNOTATATIONS = listOf( "java.lang.Deprecated", "kotlin.Deprecated", // Deprecated annotations @@ -170,7 +173,7 @@ class JCTreeConverter( val typeExpression = if (isEnum(field.access)) { treeMaker.convertSimpleName(type.className.substringAfterLast('.')) } else { - treeMaker.convertType(type) + signatureParser.parseFieldSignature(field.signature, treeMaker.convertType(type)) } val value = field.value @@ -196,27 +199,33 @@ class JCTreeConverter( } val modifiers = convertModifiers( - if (containingClass.isEnum()) (method.access and VISIBILITY_MODIFIERS.inv()) else method.access, + if (containingClass.isEnum()) (method.access.toLong() and VISIBILITY_MODIFIERS.inv()) else method.access.toLong(), ElementKind.METHOD, visibleAnnotations, method.invisibleAnnotations) val isConstructor = method.name == "" val name = treeMaker.name(method.name) - val typeParameters = JavacList.nil() - val receiverParameter = null val returnType = Type.getReturnType(method.desc) - val returnTypeExpr = if (isConstructor) null else treeMaker.convertType(returnType) + val jcReturnType = if (isConstructor) null else treeMaker.convertType(returnType) val parametersInfo = method.getParametersInfo(containingClass) @Suppress("NAME_SHADOWING") - val parameters = mapValues(parametersInfo) { info -> - val modifiers = convertModifiers(info.access, ElementKind.PARAMETER, info.visibleAnnotations, info.invisibleAnnotations) + val parameters = mapValuesIndexed(parametersInfo) { index, info -> + val lastParameter = index == parametersInfo.lastIndex + val isArrayType = info.type.sort == Type.ARRAY + + val varargs = if (lastParameter && isArrayType && method.isVarargs()) Flags.VARARGS else 0L + val modifiers = convertModifiers( + info.flags or varargs or Flags.PARAMETER, + ElementKind.PARAMETER, info.visibleAnnotations, info.invisibleAnnotations) val name = treeMaker.name(info.name) val type = treeMaker.convertType(info.type) treeMaker.VarDef(modifiers, name, type, null) } - val thrown = mapValues(method.exceptions) { treeMaker.convertFqName(it) } + val exceptionTypes = mapValues(method.exceptions) { treeMaker.convertFqName(it) } + + val genericType = signatureParser.parseMethodSignature(method.signature, parameters, exceptionTypes, jcReturnType) val defaultValue = method.annotationDefault?.let { convertLiteralExpression(it) } @@ -250,15 +259,25 @@ class JCTreeConverter( treeMaker.Block(0, JavacList.of(returnStatement)) } - return treeMaker.MethodDef(modifiers, name, returnTypeExpr, - typeParameters, receiverParameter, parameters, thrown, body, defaultValue) + return treeMaker.MethodDef( + modifiers, name, genericType.returnType, genericType.typeParameters, + genericType.parameterTypes, genericType.exceptionTypes, + body, defaultValue) } - private fun convertModifiers( + @Suppress("NOTHING_TO_INLINE") + private inline fun convertModifiers( access: Int, kind: ElementKind, visibleAnnotations: List?, invisibleAnnotations: List? + ): JCModifiers = convertModifiers(access.toLong(), kind, visibleAnnotations, invisibleAnnotations) + + private fun convertModifiers( + access: Long, + kind: ElementKind, + visibleAnnotations: List?, + invisibleAnnotations: List? ): JCModifiers { var annotations = visibleAnnotations?.fold(JavacList.nil()) { list, anno -> convertAnnotation(anno)?.let { list.prepend(it) } ?: list @@ -271,10 +290,10 @@ class JCTreeConverter( ElementKind.CLASS -> access and CLASS_MODIFIERS ElementKind.METHOD -> access and METHOD_MODIFIERS ElementKind.FIELD -> access and FIELD_MODIFIERS - ElementKind.PARAMETER -> access and Opcodes.ACC_FINAL + ElementKind.PARAMETER -> access and PARAMETER_MODIFIERS else -> throw IllegalArgumentException("Invalid element kind: $kind") } - return treeMaker.Modifiers(flags.toLong(), annotations) + return treeMaker.Modifiers(flags, annotations) } private fun convertAnnotation(annotation: AnnotationNode): JCAnnotation? { @@ -331,7 +350,7 @@ class JCTreeConverter( } private class ParameterInfo( - val access: Int, + val flags: Long, val name: String, val type: Type, val visibleAnnotations: List?, @@ -378,5 +397,6 @@ private fun isStatic(access: Int) = (access and Opcodes.ACC_STATIC) > 0 private fun isAbstract(access: Int) = (access and Opcodes.ACC_ABSTRACT) > 0 private fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 0 private fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) > 0 +private fun MethodNode.isVarargs() = (access and Opcodes.ACC_VARARGS) > 0 private fun List?.isNullOrEmpty() = this == null || this.isEmpty() \ No newline at end of file diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/SignatureParserVisitor.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/SignatureParserVisitor.kt index c62565a6f36..964a1dc5e4f 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/SignatureParserVisitor.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/SignatureParserVisitor.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.kapt3 import com.sun.tools.javac.code.BoundKind +import com.sun.tools.javac.code.TypeTag import com.sun.tools.javac.tree.JCTree.* import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor @@ -27,19 +28,36 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureReader import com.sun.tools.javac.util.List as JavacList /* - TopLevel + Root (Class) * FormalTypeParameter + SuperClass * Interface - FormalTypeParameter < TopLevel + Root (Method) + * TypeParameter + * ParameterType + + ReturnType + * ExceptionType + + Root (Field) + + SuperClass + + TypeParameter < Root + ClassBound * InterfaceBound - ClassBound < FormalTypeParameter + ParameterType < Root + + Type + + ReturnType < Root + + Type + + Type :: ClassType | TypeVariable | PrimitiveType | ArrayType + + ClassBound < TypeParameter + ClassType - InterfaceBound < FormalTypeParameter + InterfaceBound < TypeParameter ? ClassType ? TypeVariable @@ -59,7 +77,8 @@ import com.sun.tools.javac.util.List as JavacList */ enum class ElementKind { - Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, ClassType, TypeArgument, TypeVariable + Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, TypeArgument, ParameterType, ReturnType, ExceptionType, + ClassType, TypeVariable, PrimitiveType, ArrayType } class SignatureNode(val kind: ElementKind, val name: String? = null) { @@ -71,6 +90,13 @@ class SignatureParser(val treeMaker: KaptTreeMaker) { val typeParameters: JavacList, val superClass: JCExpression, val interfaces: JavacList) + + class MethodGenericSignature( + val typeParameters: JavacList, + val parameterTypes: JavacList, + val exceptionTypes: JavacList, + val returnType: JCExpression? + ) fun parseClassSignature( signature: String?, @@ -88,9 +114,51 @@ class SignatureParser(val treeMaker: KaptTreeMaker) { root.split(typeParameters, TypeParameter, superClasses, SuperClass, interfaces, Interface) val jcTypeParameters = mapValues(typeParameters) { parseTypeParameter(it) } - val superClassType = parseType(superClasses.single().children.single()) - val interfaceTypes = mapValues(interfaces) { parseType(it.children.single()) } - return ClassGenericSignature(jcTypeParameters, superClassType, interfaceTypes) + val jcSuperClass = parseType(superClasses.single().children.single()) + val jcInterfaces = mapValues(interfaces) { parseType(it.children.single()) } + return ClassGenericSignature(jcTypeParameters, jcSuperClass, jcInterfaces) + } + + fun parseMethodSignature( + signature: String?, + rawParameters: JavacList, + rawExceptionTypes: JavacList, + rawReturnType: JCExpression? + ): MethodGenericSignature { + if (signature == null) { + return MethodGenericSignature(JavacList.nil(), rawParameters, rawExceptionTypes, rawReturnType) + } + + val root = parse(signature) + val typeParameters = smartList() + val parameterTypes = smartList() + val exceptionTypes = smartList() + val returnTypes = smartList() + root.split(typeParameters, TypeParameter, parameterTypes, ParameterType, exceptionTypes, ExceptionType, returnTypes, ReturnType) + + val jcTypeParameters = mapValues(typeParameters) { parseTypeParameter(it) } + assert(rawParameters.size >= parameterTypes.size) + val offset = rawParameters.size - parameterTypes.size + val jcParameters = mapValuesIndexed(parameterTypes) { index, it -> + val rawParameter = rawParameters[index + offset] + treeMaker.VarDef(rawParameter.modifiers, rawParameter.getName(), parseType(it.children.single()), rawParameter.initializer) + } + val jcExceptionTypes = mapValues(exceptionTypes) { parseType(it) } + val jcReturnType = if (rawReturnType == null) null else parseType(returnTypes.single().children.single()) + return MethodGenericSignature(jcTypeParameters, jcParameters, jcExceptionTypes, jcReturnType) + } + + fun parseFieldSignature( + signature: String?, + rawType: JCExpression + ): JCExpression { + if (signature == null) return rawType + + val root = parse(signature) + val superClass = root.children.single() + assert(superClass.kind == SuperClass) + + return parseType(superClass.children.single()) } private fun parseTypeParameter(node: SignatureNode): JCTypeParameter { @@ -135,6 +203,22 @@ class SignatureParser(val treeMaker: KaptTreeMaker) { }) } TypeVariable -> treeMaker.convertSimpleName(node.name!!) + ArrayType -> treeMaker.TypeArray(parseType(node.children.single())) + PrimitiveType -> { + val typeTag = when (node.name!!.single()) { + 'V' -> TypeTag.VOID + 'Z' -> TypeTag.BOOLEAN + 'C' -> TypeTag.CHAR + 'B' -> TypeTag.BYTE + 'S' -> TypeTag.SHORT + 'I' -> TypeTag.INT + 'F' -> TypeTag.FLOAT + 'J' -> TypeTag.LONG + 'D' -> TypeTag.DOUBLE + else -> error("Illegal primitive type ${node.name}") + } + treeMaker.TypeIdent(typeTag) + } else -> error("Unsupported type: $node") } } @@ -165,7 +249,8 @@ private fun SignatureNode.split( l2: MutableList, e2: ElementKind, l3: MutableList, - e3: ElementKind) { + e3: ElementKind +) { for (child in children) { val kind = child.kind when (kind) { @@ -177,6 +262,28 @@ private fun SignatureNode.split( } } +private fun SignatureNode.split( + l1: MutableList, + e1: ElementKind, + l2: MutableList, + e2: ElementKind, + l3: MutableList, + e3: ElementKind, + l4: MutableList, + e4: ElementKind +) { + for (child in children) { + val kind = child.kind + when (kind) { + e1 -> l1 += child + e2 -> l2 += child + e3 -> l3 += child + e4 -> l4 += child + else -> error("Unknown kind: $kind") + } + } +} + private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) { val root = SignatureNode(Root) private val stack = ArrayDeque(5).apply { add(root) } @@ -231,7 +338,31 @@ private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) { } override fun visitTypeVariable(name: String) { - push(TypeVariable, parent = InterfaceBound, name = name) + push(TypeVariable, name = name) + } + + override fun visitParameterType(): SignatureVisitor { + push(ParameterType, parent = Root) + return super.visitParameterType() + } + + override fun visitReturnType(): SignatureVisitor { + push(ReturnType, parent = Root) + return super.visitReturnType() + } + + override fun visitExceptionType(): SignatureVisitor { + push(ExceptionType, parent = Root) + return super.visitExceptionType() + } + + override fun visitBaseType(descriptor: Char) { + push(PrimitiveType, name = descriptor.toString()) + } + + override fun visitArrayType(): SignatureVisitor { + push(ArrayType) + return super.visitArrayType() } override fun visitEnd() { diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/utils.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/utils.kt index d8663c5b865..c2dad949426 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/utils.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/utils.kt @@ -27,6 +27,16 @@ internal inline fun mapValues(values: Iterable?, f: (T) -> R?): JavacL return result.reverse() } +internal inline fun mapValuesIndexed(values: Iterable?, f: (Int, T) -> R?): JavacList { + if (values == null) return JavacList.nil() + + var result = JavacList.nil() + values.forEachIndexed { index, item -> + f(index, item)?.let { result = result.prepend(it) } + } + return result.reverse() +} + internal inline fun mapPairedValues(valuePairs: List?, f: (String, Any) -> T?): JavacList { if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil() diff --git a/plugins/kapt3/testData/converter/annotations.txt b/plugins/kapt3/testData/converter/annotations.txt index f82e16f0882..750b7483a06 100644 --- a/plugins/kapt3/testData/converter/annotations.txt +++ b/plugins/kapt3/testData/converter/annotations.txt @@ -31,9 +31,9 @@ public abstract @interface Anno2 { public abstract Colors[] colors() default {Colors.BLACK, Colors.WHITE}; - public abstract java.lang.Class clazz(); + public abstract java.lang.Class clazz(); - public abstract java.lang.Class[] classes(); + public abstract java.lang.Class[] classes(); } //////////////////// diff --git a/plugins/kapt3/testData/converter/functions.txt b/plugins/kapt3/testData/converter/functions.txt index 63dd17835b3..00e1d8867f7 100644 --- a/plugins/kapt3/testData/converter/functions.txt +++ b/plugins/kapt3/testData/converter/functions.txt @@ -1,11 +1,10 @@ - public final class FunctionsTest { - - public final kotlin.reflect.KProperty1 f() { + + public final kotlin.reflect.KProperty1 f() { return null; } - public final kotlin.jvm.functions.Function2 f2() { + public final kotlin.jvm.functions.Function2 f2() { return null; } @@ -19,4 +18,4 @@ public final class FunctionsTest { public FunctionsTest() { super(); } -} \ No newline at end of file +} diff --git a/plugins/kapt3/testData/converter/genericSimple.kt b/plugins/kapt3/testData/converter/genericSimple.kt index 22cd42fa66b..6591df8dcf3 100644 --- a/plugins/kapt3/testData/converter/genericSimple.kt +++ b/plugins/kapt3/testData/converter/genericSimple.kt @@ -5,4 +5,11 @@ interface Intf interface Intf2, M : T> interface OtherIntf open class BaseClass -class MyClass : Intf, OtherIntf, BaseClass() \ No newline at end of file +class MyClass : Intf, OtherIntf, BaseClass() { + val fld: List>? = null +} + +interface ABC { + fun abc(item: T, items: List, vararg otherItems: T): List + fun bcd(vararg a: Char): Int +} \ No newline at end of file diff --git a/plugins/kapt3/testData/converter/genericSimple.txt b/plugins/kapt3/testData/converter/genericSimple.txt index 0b7a3773d0c..c99684fa2a6 100644 --- a/plugins/kapt3/testData/converter/genericSimple.txt +++ b/plugins/kapt3/testData/converter/genericSimple.txt @@ -27,8 +27,23 @@ public class BaseClass { public final class MyClass extends BaseClass implements Intf, OtherIntf { + private final java.util.List> fld = null; + + public final java.util.List> getFld() { + return null; + } public MyClass() { super(); } } + +//////////////////// + + +public abstract interface ABC { + + public abstract java.util.List abc(T item, java.util.List items, T... otherItems); + + public abstract int bcd(char... a); +} diff --git a/plugins/kapt3/testData/converter/nestedClasses.txt b/plugins/kapt3/testData/converter/nestedClasses.txt index dd4d3a5f750..1980fb066c8 100644 --- a/plugins/kapt3/testData/converter/nestedClasses.txt +++ b/plugins/kapt3/testData/converter/nestedClasses.txt @@ -20,7 +20,7 @@ public final class Test { public final class Inner { - public Inner(Test $outer) { + public Inner() { super(); } }