Kapt: Parse generic signatures with inner class types correctly

This commit is contained in:
Yan Zhulanow
2016-12-20 18:02:04 +03:00
committed by Yan Zhulanow
parent 5f3c1dfc41
commit 142496c00f
4 changed files with 133 additions and 21 deletions
@@ -74,6 +74,10 @@ import com.sun.tools.javac.util.List as JavacList
ClassType < *
* TypeArgument
* InnerClass
InnerClass < ClassType
! TypeArgument
TypeArgument < ClassType
+ ClassType
@@ -81,10 +85,10 @@ import com.sun.tools.javac.util.List as JavacList
internal enum class ElementKind {
Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, TypeArgument, ParameterType, ReturnType, ExceptionType,
ClassType, TypeVariable, PrimitiveType, ArrayType
ClassType, InnerClass, TypeVariable, PrimitiveType, ArrayType
}
private class SignatureNode(val kind: ElementKind, val name: String? = null) {
private class SignatureNode(val kind: ElementKind, var name: String? = null) {
val children: MutableList<SignatureNode> = SmartList<SignatureNode>()
}
@@ -188,23 +192,20 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
val kind = node.kind
return when (kind) {
ClassType -> {
val classFqName = node.name!!.replace('/', '.')
val args = node.children
val fqNameExpression = treeMaker.FqName(classFqName)
if (args.isEmpty()) return fqNameExpression
val typeArgs = mutableListOf<SignatureNode>()
val innerClasses = mutableListOf<SignatureNode>()
node.split(typeArgs, TypeArgument, innerClasses, InnerClass)
treeMaker.TypeApply(fqNameExpression, mapJList(args) { arg ->
assert(arg.kind == TypeArgument) { "Unexpected kind ${arg.kind}, $TypeArgument expected" }
val variance = arg.name ?: return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null)
var expression = makeExpressionForClassTypeWithArguments(treeMaker.FqName(node.name!!.replace('/', '.')), typeArgs)
if (innerClasses.isEmpty()) return expression
val argType = parseType(arg.children.single())
when (variance.single()) {
'=' -> argType
'+' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), argType)
'-' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), argType)
else -> error("Unknown variance, '=', '+' or '-' expected")
}
})
for (innerClass in innerClasses) {
expression = makeExpressionForClassTypeWithArguments(
treeMaker.Select(expression, treeMaker.name(innerClass.name!!)),
innerClass.children)
}
expression
}
TypeVariable -> treeMaker.SimpleName(node.name!!)
ArrayType -> treeMaker.TypeArray(parseType(node.children.single()))
@@ -227,6 +228,23 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
}
}
private fun makeExpressionForClassTypeWithArguments(fqNameExpression: JCExpression, args: List<SignatureNode>): JCExpression {
if (args.isEmpty()) return fqNameExpression
return treeMaker.TypeApply(fqNameExpression, mapJList(args) { arg ->
assert(arg.kind == TypeArgument) { "Unexpected kind ${arg.kind}, $TypeArgument expected" }
val variance = arg.name ?: return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null)
val argType = parseType(arg.children.single())
when (variance.single()) {
'=' -> argType
'+' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), argType)
'-' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), argType)
else -> error("Unknown variance, '=', '+' or '-' expected")
}
})
}
private fun parse(signature: String): SignatureNode {
val parser = SignatureParserVisitor()
SignatureReader(signature).accept(parser)
@@ -292,12 +310,21 @@ private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) {
val root = SignatureNode(Root)
private val stack = ArrayDeque<SignatureNode>(5).apply { add(root) }
private fun push(kind: ElementKind, parent: ElementKind? = null, name: String? = null) {
if (parent != null) {
while (stack.peek().kind != parent) {
private fun popUntil(kind: ElementKind?) {
if (kind != null) {
while (stack.peek().kind != kind) {
stack.pop()
}
}
}
private fun popUntil(vararg kinds: ElementKind) {
while (stack.peek().kind !in kinds) {
stack.pop()
}
}
private fun push(kind: ElementKind, parent: ElementKind? = null, name: String? = null) {
popUntil(parent)
val newNode = SignatureNode(kind, name)
stack.peek().children += newNode
@@ -329,14 +356,20 @@ private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) {
}
override fun visitTypeArgument() {
popUntil(ClassType, InnerClass)
push(TypeArgument, parent = ClassType)
}
override fun visitTypeArgument(variance: Char): SignatureVisitor {
push(TypeArgument, parent = ClassType, name = variance.toString())
popUntil(ClassType, InnerClass)
push(TypeArgument, name = variance.toString())
return super.visitTypeArgument(variance)
}
override fun visitInnerClassType(name: String) {
push(InnerClass, name = name, parent = ClassType)
}
override fun visitParameterType(): SignatureVisitor {
push(ParameterType, parent = Root)
return super.visitParameterType()
@@ -102,6 +102,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
doTest(fileName);
}
@TestMetadata("innerClassesWithTypeParameters.kt")
public void testInnerClassesWithTypeParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt");
doTest(fileName);
}
@TestMetadata("jvmOverloads.kt")
public void testJvmOverloads() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt");
@@ -0,0 +1,16 @@
class Test {
private var a = FilterValueDelegate<Float>()
private inner class FilterValueDelegate<T>
}
class Test2 {
inner class FilterValueDelegate<T> {
private var a = Filter2<String>()
inner class Filter2<X>
}
}
class Test3 {
private var a = FilterValueDelegate<Float>()
private class FilterValueDelegate<T>
}
@@ -0,0 +1,57 @@
public final class Test {
private Test.FilterValueDelegate<java.lang.Float> a;
public Test() {
super();
}
private final class FilterValueDelegate<T extends java.lang.Object> {
public FilterValueDelegate() {
super();
}
}
}
////////////////////
public final class Test2 {
public Test2() {
super();
}
public final class FilterValueDelegate<T extends java.lang.Object> {
private Test2.FilterValueDelegate<T>.Filter2<java.lang.String> a;
public FilterValueDelegate() {
super();
}
public final class Filter2<X extends java.lang.Object> {
public Filter2() {
super();
}
}
}
}
////////////////////
public final class Test3 {
private Test3.FilterValueDelegate<java.lang.Float> a;
public Test3() {
super();
}
private static final class FilterValueDelegate<T extends java.lang.Object> {
public FilterValueDelegate() {
super();
}
}
}