Kapt3: Parse generic signatures of classes.
Refactoring (move TreeMaker helper functions to KaptTreeMaker, mapValues() and others to utils.kt).
This commit is contained in:
committed by
Yan Zhulanow
parent
e4158a5571
commit
efd25de13f
@@ -22,7 +22,6 @@ import com.sun.tools.javac.tree.JCTree
|
|||||||
import com.sun.tools.javac.tree.JCTree.*
|
import com.sun.tools.javac.tree.JCTree.*
|
||||||
import com.sun.tools.javac.tree.TreeMaker
|
import com.sun.tools.javac.tree.TreeMaker
|
||||||
import com.sun.tools.javac.util.Context
|
import com.sun.tools.javac.util.Context
|
||||||
import com.sun.tools.javac.util.Names
|
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -67,8 +66,8 @@ class JCTreeConverter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
private val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
||||||
private val treeMaker = TreeMaker.instance(context)
|
private val treeMaker = TreeMaker.instance(context) as KaptTreeMaker
|
||||||
private val nameTable = Names.instance(context).table
|
private val signatureParser = SignatureParser(treeMaker)
|
||||||
|
|
||||||
private var done = false
|
private var done = false
|
||||||
|
|
||||||
@@ -90,7 +89,7 @@ class JCTreeConverter(
|
|||||||
|
|
||||||
val packageAnnotations = JavacList.nil<JCAnnotation>()
|
val packageAnnotations = JavacList.nil<JCAnnotation>()
|
||||||
val packageName = ktFile.packageFqName.asString()
|
val packageName = ktFile.packageFqName.asString()
|
||||||
val packageClause = if (packageName.isEmpty()) null else convertFqName(packageName)
|
val packageClause = if (packageName.isEmpty()) null else treeMaker.convertFqName(packageName)
|
||||||
|
|
||||||
val imports = JavacList.nil<JCTree>()
|
val imports = JavacList.nil<JCTree>()
|
||||||
val classes = JavacList.of<JCTree>(classDeclaration)
|
val classes = JavacList.of<JCTree>(classDeclaration)
|
||||||
@@ -102,7 +101,7 @@ class JCTreeConverter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false for the inner classe or if the origin for the class was not found.
|
* Returns false for the inner classes or if the origin for the class was not found.
|
||||||
*/
|
*/
|
||||||
private fun convertClass(clazz: ClassNode, isTopLevel: Boolean): JCClassDecl? {
|
private fun convertClass(clazz: ClassNode, isTopLevel: Boolean): JCClassDecl? {
|
||||||
if (isSynthetic(clazz.access)) return null
|
if (isSynthetic(clazz.access)) return null
|
||||||
@@ -124,16 +123,18 @@ class JCTreeConverter(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val simpleName = name(if (isDefaultImpls) "DefaultImpls" else descriptor.name.asString())
|
val simpleName = treeMaker.name(if (isDefaultImpls) "DefaultImpls" else descriptor.name.asString())
|
||||||
|
|
||||||
val typeParams = JavacList.nil<JCTypeParameter>()
|
val interfaces = mapValues(clazz.interfaces) {
|
||||||
val extending = if (clazz.superName == "java/lang/Object" || isEnum) null else convertFqName(clazz.superName)
|
|
||||||
|
|
||||||
val implementing = mapValues(clazz.interfaces) {
|
|
||||||
if (isAnnotation && it == "java/lang/annotation/Annotation") return@mapValues null
|
if (isAnnotation && it == "java/lang/annotation/Annotation") return@mapValues null
|
||||||
convertFqName(it)
|
treeMaker.convertFqName(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val superClass = treeMaker.convertFqName(clazz.superName)
|
||||||
|
|
||||||
|
val hasSuperClass = clazz.superName != "java/lang/Object" && !isEnum
|
||||||
|
val genericType = signatureParser.parseClassSignature(clazz.signature, superClass, interfaces)
|
||||||
|
|
||||||
val fields = mapValues<FieldNode, JCTree>(clazz.fields) { convertField(it) }
|
val fields = mapValues<FieldNode, JCTree>(clazz.fields) { convertField(it) }
|
||||||
val methods = mapValues<MethodNode, JCTree>(clazz.methods) {
|
val methods = mapValues<MethodNode, JCTree>(clazz.methods) {
|
||||||
if (isEnum) {
|
if (isEnum) {
|
||||||
@@ -149,21 +150,27 @@ class JCTreeConverter(
|
|||||||
convertClass(innerClassNode, false)
|
convertClass(innerClassNode, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return treeMaker.ClassDef(modifiers, simpleName, typeParams, extending, implementing, fields + methods + nestedClasses)
|
return treeMaker.ClassDef(
|
||||||
|
modifiers,
|
||||||
|
simpleName,
|
||||||
|
genericType.typeParameters,
|
||||||
|
if (hasSuperClass) genericType.superClass else null,
|
||||||
|
genericType.interfaces,
|
||||||
|
fields + methods + nestedClasses)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertField(field: FieldNode): JCVariableDecl? {
|
private fun convertField(field: FieldNode): JCVariableDecl? {
|
||||||
if (isSynthetic(field.access)) return null
|
if (isSynthetic(field.access)) return null
|
||||||
|
|
||||||
val modifiers = convertModifiers(field.access, ElementKind.FIELD, field.visibleAnnotations, field.invisibleAnnotations)
|
val modifiers = convertModifiers(field.access, ElementKind.FIELD, field.visibleAnnotations, field.invisibleAnnotations)
|
||||||
val name = name(field.name)
|
val name = treeMaker.name(field.name)
|
||||||
val type = Type.getType(field.desc)
|
val type = Type.getType(field.desc)
|
||||||
|
|
||||||
// Enum type must be an identifier (Javac requirement)
|
// Enum type must be an identifier (Javac requirement)
|
||||||
val typeExpression = if (isEnum(field.access)) {
|
val typeExpression = if (isEnum(field.access)) {
|
||||||
convertSimpleName(type.className.substringAfterLast('.'))
|
treeMaker.convertSimpleName(type.className.substringAfterLast('.'))
|
||||||
} else {
|
} else {
|
||||||
convertType(type)
|
treeMaker.convertType(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
val value = field.value
|
val value = field.value
|
||||||
@@ -193,23 +200,23 @@ class JCTreeConverter(
|
|||||||
ElementKind.METHOD, visibleAnnotations, method.invisibleAnnotations)
|
ElementKind.METHOD, visibleAnnotations, method.invisibleAnnotations)
|
||||||
|
|
||||||
val isConstructor = method.name == "<init>"
|
val isConstructor = method.name == "<init>"
|
||||||
val name = name(method.name)
|
val name = treeMaker.name(method.name)
|
||||||
val typeParameters = JavacList.nil<JCTypeParameter>()
|
val typeParameters = JavacList.nil<JCTypeParameter>()
|
||||||
val receiverParameter = null
|
val receiverParameter = null
|
||||||
|
|
||||||
val returnType = Type.getReturnType(method.desc)
|
val returnType = Type.getReturnType(method.desc)
|
||||||
val returnTypeExpr = if (isConstructor) null else convertType(returnType)
|
val returnTypeExpr = if (isConstructor) null else treeMaker.convertType(returnType)
|
||||||
|
|
||||||
val parametersInfo = method.getParametersInfo(containingClass)
|
val parametersInfo = method.getParametersInfo(containingClass)
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val parameters = mapValues(parametersInfo) { info ->
|
val parameters = mapValues(parametersInfo) { info ->
|
||||||
val modifiers = convertModifiers(info.access, ElementKind.PARAMETER, info.visibleAnnotations, info.invisibleAnnotations)
|
val modifiers = convertModifiers(info.access, ElementKind.PARAMETER, info.visibleAnnotations, info.invisibleAnnotations)
|
||||||
val name = name(info.name)
|
val name = treeMaker.name(info.name)
|
||||||
val type = convertType(info.type)
|
val type = treeMaker.convertType(info.type)
|
||||||
treeMaker.VarDef(modifiers, name, type, null)
|
treeMaker.VarDef(modifiers, name, type, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val thrown = mapValues(method.exceptions) { convertFqName(it) }
|
val thrown = mapValues(method.exceptions) { treeMaker.convertFqName(it) }
|
||||||
|
|
||||||
val defaultValue = method.annotationDefault?.let { convertLiteralExpression(it) }
|
val defaultValue = method.annotationDefault?.let { convertLiteralExpression(it) }
|
||||||
|
|
||||||
@@ -229,7 +236,7 @@ class JCTreeConverter(
|
|||||||
val args = mapValues(superClassConstructor.valueParameters) { param ->
|
val args = mapValues(superClassConstructor.valueParameters) { param ->
|
||||||
convertLiteralExpression(getDefaultValue(typeMapper.mapType(param.type)))
|
convertLiteralExpression(getDefaultValue(typeMapper.mapType(param.type)))
|
||||||
}
|
}
|
||||||
val call = treeMaker.Apply(JavacList.nil(), convertSimpleName("super"), args)
|
val call = treeMaker.Apply(JavacList.nil(), treeMaker.convertSimpleName("super"), args)
|
||||||
JavacList.of<JCStatement>(treeMaker.Exec(call))
|
JavacList.of<JCStatement>(treeMaker.Exec(call))
|
||||||
} else {
|
} else {
|
||||||
JavacList.nil<JCStatement>()
|
JavacList.nil<JCStatement>()
|
||||||
@@ -275,9 +282,9 @@ class JCTreeConverter(
|
|||||||
val fqName = annotationType.className
|
val fqName = annotationType.className
|
||||||
if (BLACKLISTED_ANNOTATATIONS.any { fqName.startsWith(it) }) return null
|
if (BLACKLISTED_ANNOTATATIONS.any { fqName.startsWith(it) }) return null
|
||||||
|
|
||||||
val name = convertType(annotationType)
|
val name = treeMaker.convertType(annotationType)
|
||||||
val values = mapPairedValues<JCExpression>(annotation.values) { key, value ->
|
val values = mapPairedValues<JCExpression>(annotation.values) { key, value ->
|
||||||
treeMaker.Assign(convertSimpleName(key), convertLiteralExpression(value))
|
treeMaker.Assign(treeMaker.convertSimpleName(key), convertLiteralExpression(value))
|
||||||
}
|
}
|
||||||
return treeMaker.Annotation(name, values)
|
return treeMaker.Annotation(name, values)
|
||||||
}
|
}
|
||||||
@@ -300,11 +307,11 @@ class JCTreeConverter(
|
|||||||
assert(value.size == 2)
|
assert(value.size == 2)
|
||||||
val enumType = Type.getType(value[0] as String)
|
val enumType = Type.getType(value[0] as String)
|
||||||
val valueName = value[1] as String
|
val valueName = value[1] as String
|
||||||
treeMaker.Select(convertType(enumType), name(valueName))
|
treeMaker.Select(treeMaker.convertType(enumType), treeMaker.name(valueName))
|
||||||
}
|
}
|
||||||
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value) { convertLiteralExpression(it) })
|
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value) { convertLiteralExpression(it) })
|
||||||
|
|
||||||
is Type -> treeMaker.Select(convertType(value), name("class"))
|
is Type -> treeMaker.Select(treeMaker.convertType(value), treeMaker.name("class"))
|
||||||
is AnnotationNode -> convertAnnotation(value) ?: error("Annotation is filtered out")
|
is AnnotationNode -> convertAnnotation(value) ?: error("Annotation is filtered out")
|
||||||
else -> throw IllegalArgumentException("Illegal literal expression value: $value (${value.javaClass.canonicalName})")
|
else -> throw IllegalArgumentException("Illegal literal expression value: $value (${value.javaClass.canonicalName})")
|
||||||
}
|
}
|
||||||
@@ -321,46 +328,6 @@ class JCTreeConverter(
|
|||||||
Type.DOUBLE_TYPE -> 0.0
|
Type.DOUBLE_TYPE -> 0.0
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertType(type: Type): JCExpression {
|
|
||||||
convertBuiltinType(type)?.let { return it }
|
|
||||||
if (type.sort == Type.ARRAY) {
|
|
||||||
return treeMaker.TypeArray(convertType(type.elementType))
|
|
||||||
}
|
|
||||||
return convertFqName(type.className)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun convertFqName(internalOrFqName: String): JCExpression {
|
|
||||||
val path = internalOrFqName.replace('/', '.').split('.')
|
|
||||||
assert(path.isNotEmpty())
|
|
||||||
if (path.size == 1) return convertSimpleName(path.single())
|
|
||||||
|
|
||||||
var expr = treeMaker.Select(convertSimpleName(path[0]), name(path[1]))
|
|
||||||
for (index in 2..path.lastIndex) {
|
|
||||||
expr = treeMaker.Select(expr, name(path[index]))
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun convertBuiltinType(type: Type): JCExpression? {
|
|
||||||
val typeTag = when (type) {
|
|
||||||
Type.BYTE_TYPE -> TypeTag.BYTE
|
|
||||||
Type.BOOLEAN_TYPE -> TypeTag.BOOLEAN
|
|
||||||
Type.CHAR_TYPE -> TypeTag.CHAR
|
|
||||||
Type.SHORT_TYPE -> TypeTag.SHORT
|
|
||||||
Type.INT_TYPE -> TypeTag.INT
|
|
||||||
Type.LONG_TYPE -> TypeTag.LONG
|
|
||||||
Type.FLOAT_TYPE -> TypeTag.FLOAT
|
|
||||||
Type.DOUBLE_TYPE -> TypeTag.DOUBLE
|
|
||||||
Type.VOID_TYPE -> TypeTag.VOID
|
|
||||||
else -> null
|
|
||||||
} ?: return null
|
|
||||||
return treeMaker.TypeIdent(typeTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun convertSimpleName(name: String): JCExpression = treeMaker.Ident(name(name))
|
|
||||||
|
|
||||||
private fun name(name: String) = nameTable.fromString(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParameterInfo(
|
private class ParameterInfo(
|
||||||
@@ -400,36 +367,6 @@ private fun MethodNode.getParametersInfo(containingClass: ClassNode): List<Param
|
|||||||
return parameterInfos
|
return parameterInfos
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <T, R> mapValues(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
|
|
||||||
if (values == null) return JavacList.nil()
|
|
||||||
|
|
||||||
var result = JavacList.nil<R>()
|
|
||||||
for (item in values) {
|
|
||||||
f(item)?.let { result = result.prepend(it) }
|
|
||||||
}
|
|
||||||
return result.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> mapPairedValues(valuePairs: List<Any>?, f: (String, Any) -> T?): JavacList<T> {
|
|
||||||
if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
|
|
||||||
|
|
||||||
val size = valuePairs.size
|
|
||||||
var result = JavacList.nil<T>()
|
|
||||||
assert(size % 2 == 0)
|
|
||||||
var index = 0
|
|
||||||
while (index < size) {
|
|
||||||
val key = valuePairs[index] as String
|
|
||||||
val value = valuePairs[index + 1]
|
|
||||||
f(key, value)?.let { result = result.prepend(it) }
|
|
||||||
index += 2
|
|
||||||
}
|
|
||||||
return result.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
private operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
|
|
||||||
return this.appendList(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val ClassDescriptor.isNested: Boolean
|
private val ClassDescriptor.isNested: Boolean
|
||||||
get() = containingDeclaration is ClassDescriptor
|
get() = containingDeclaration is ClassDescriptor
|
||||||
|
|
||||||
@@ -442,4 +379,4 @@ private fun isAbstract(access: Int) = (access and Opcodes.ACC_ABSTRACT) > 0
|
|||||||
private fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 0
|
private fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 0
|
||||||
private fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) > 0
|
private fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) > 0
|
||||||
|
|
||||||
private fun <T> List<T>.isNullOrEmpty() = this == null || this.isEmpty()
|
private fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||||
@@ -55,6 +55,7 @@ class KaptRunner {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
JavacFileManager.preRegister(context)
|
JavacFileManager.preRegister(context)
|
||||||
|
KaptTreeMaker.preRegister(context)
|
||||||
KaptJavaCompiler.preRegister(context)
|
KaptJavaCompiler.preRegister(context)
|
||||||
|
|
||||||
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.kapt3
|
||||||
|
|
||||||
|
import com.sun.tools.javac.code.TypeTag
|
||||||
|
import com.sun.tools.javac.tree.JCTree
|
||||||
|
import com.sun.tools.javac.tree.TreeMaker
|
||||||
|
import com.sun.tools.javac.util.Context
|
||||||
|
import com.sun.tools.javac.util.Names
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type.*
|
||||||
|
|
||||||
|
class KaptTreeMaker(context: Context?) : TreeMaker(context) {
|
||||||
|
private val nameTable = Names.instance(context).table
|
||||||
|
|
||||||
|
fun convertType(type: Type): JCTree.JCExpression {
|
||||||
|
convertBuiltinType(type)?.let { return it }
|
||||||
|
if (type.sort == ARRAY) {
|
||||||
|
return TypeArray(convertType(type.elementType))
|
||||||
|
}
|
||||||
|
return convertFqName(type.className)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertFqName(internalOrFqName: String): JCTree.JCExpression {
|
||||||
|
val path = internalOrFqName.replace('/', '.').split('.')
|
||||||
|
assert(path.isNotEmpty())
|
||||||
|
if (path.size == 1) return convertSimpleName(path.single())
|
||||||
|
|
||||||
|
var expr = Select(convertSimpleName(path[0]), name(path[1]))
|
||||||
|
for (index in 2..path.lastIndex) {
|
||||||
|
expr = Select(expr, name(path[index]))
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertBuiltinType(type: Type): JCTree.JCExpression? {
|
||||||
|
val typeTag = when (type) {
|
||||||
|
BYTE_TYPE -> TypeTag.BYTE
|
||||||
|
BOOLEAN_TYPE -> TypeTag.BOOLEAN
|
||||||
|
CHAR_TYPE -> TypeTag.CHAR
|
||||||
|
SHORT_TYPE -> TypeTag.SHORT
|
||||||
|
INT_TYPE -> TypeTag.INT
|
||||||
|
LONG_TYPE -> TypeTag.LONG
|
||||||
|
FLOAT_TYPE -> TypeTag.FLOAT
|
||||||
|
DOUBLE_TYPE -> TypeTag.DOUBLE
|
||||||
|
VOID_TYPE -> TypeTag.VOID
|
||||||
|
else -> null
|
||||||
|
} ?: return null
|
||||||
|
return TypeIdent(typeTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertSimpleName(name: String): JCTree.JCExpression = Ident(name(name))
|
||||||
|
|
||||||
|
fun name(name: String) = nameTable.fromString(name)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun preRegister(context: Context) {
|
||||||
|
context.put(treeMakerKey, Context.Factory<TreeMaker>(::KaptTreeMaker))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.kapt3
|
||||||
|
|
||||||
|
import com.sun.tools.javac.code.BoundKind
|
||||||
|
import com.sun.tools.javac.tree.JCTree.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor
|
||||||
|
import java.util.*
|
||||||
|
import org.jetbrains.kotlin.kapt3.ElementKind.*
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
import org.jetbrains.org.objectweb.asm.signature.SignatureReader
|
||||||
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
|
/*
|
||||||
|
TopLevel
|
||||||
|
* FormalTypeParameter
|
||||||
|
+ SuperClass
|
||||||
|
* Interface
|
||||||
|
|
||||||
|
FormalTypeParameter < TopLevel
|
||||||
|
+ ClassBound
|
||||||
|
* InterfaceBound
|
||||||
|
|
||||||
|
ClassBound < FormalTypeParameter
|
||||||
|
+ ClassType
|
||||||
|
|
||||||
|
InterfaceBound < FormalTypeParameter
|
||||||
|
? ClassType
|
||||||
|
? TypeVariable
|
||||||
|
|
||||||
|
TypeVariable < InterfaceBound
|
||||||
|
|
||||||
|
SuperClass < TopLevel
|
||||||
|
+ ClassType
|
||||||
|
|
||||||
|
Interface < TopLevel
|
||||||
|
+ ClassType
|
||||||
|
|
||||||
|
ClassType < *
|
||||||
|
* TypeArgument
|
||||||
|
|
||||||
|
TypeArgument < ClassType
|
||||||
|
+ ClassType
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class ElementKind {
|
||||||
|
Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, ClassType, TypeArgument, TypeVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignatureNode(val kind: ElementKind, val name: String? = null) {
|
||||||
|
val children: MutableList<SignatureNode> = SmartList<SignatureNode>()
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignatureParser(val treeMaker: KaptTreeMaker) {
|
||||||
|
class ClassGenericSignature(
|
||||||
|
val typeParameters: JavacList<JCTypeParameter>,
|
||||||
|
val superClass: JCExpression,
|
||||||
|
val interfaces: JavacList<JCExpression>)
|
||||||
|
|
||||||
|
fun parseClassSignature(
|
||||||
|
signature: String?,
|
||||||
|
rawSuperClass: JCExpression,
|
||||||
|
rawInterfaces: JavacList<JCExpression>
|
||||||
|
): ClassGenericSignature {
|
||||||
|
if (signature == null) {
|
||||||
|
return ClassGenericSignature(JavacList.nil(), rawSuperClass, rawInterfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
val root = parse(signature)
|
||||||
|
val typeParameters = smartList()
|
||||||
|
val superClasses = smartList()
|
||||||
|
val interfaces = smartList()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseTypeParameter(node: SignatureNode): JCTypeParameter {
|
||||||
|
assert(node.kind == TypeParameter)
|
||||||
|
|
||||||
|
val classBounds = smartList()
|
||||||
|
val interfaceBounds = smartList()
|
||||||
|
node.split(classBounds, ClassBound, interfaceBounds, InterfaceBound)
|
||||||
|
assert(classBounds.size <= 1)
|
||||||
|
|
||||||
|
val jcClassBound = classBounds.firstOrNull()?.let { parseBound(it) }
|
||||||
|
val jcInterfaceBounds = mapValues(interfaceBounds) { parseBound(it) }
|
||||||
|
val allBounds = if (jcClassBound != null) jcInterfaceBounds.prepend(jcClassBound) else jcInterfaceBounds
|
||||||
|
return treeMaker.TypeParameter(treeMaker.name(node.name!!), allBounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseBound(node: SignatureNode): JCExpression {
|
||||||
|
assert(node.kind == ClassBound || node.kind == InterfaceBound)
|
||||||
|
return parseType(node.children.single())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseType(node: SignatureNode): JCExpression {
|
||||||
|
val kind = node.kind
|
||||||
|
return when (kind) {
|
||||||
|
ClassType -> {
|
||||||
|
val classFqName = node.name!!.replace('/', '.')
|
||||||
|
val args = node.children
|
||||||
|
val fqNameExpression = treeMaker.convertFqName(classFqName)
|
||||||
|
if (args.isEmpty()) return fqNameExpression
|
||||||
|
|
||||||
|
treeMaker.TypeApply(fqNameExpression, mapValues(args) { arg ->
|
||||||
|
assert(arg.kind == TypeArgument) { "Unexpected kind ${arg.kind}, $TypeArgument expected" }
|
||||||
|
val variance = arg.name ?: return@mapValues 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TypeVariable -> treeMaker.convertSimpleName(node.name!!)
|
||||||
|
else -> error("Unsupported type: $node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parse(signature: String): SignatureNode {
|
||||||
|
val parser = SignatureParserVisitor()
|
||||||
|
SignatureReader(signature).accept(parser)
|
||||||
|
return parser.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun smartList() = SmartList<SignatureNode>()
|
||||||
|
|
||||||
|
private fun SignatureNode.split(l1: MutableList<SignatureNode>, e1: ElementKind, l2: MutableList<SignatureNode>, e2: ElementKind) {
|
||||||
|
for (child in children) {
|
||||||
|
val kind = child.kind
|
||||||
|
when (kind) {
|
||||||
|
e1 -> l1 += child
|
||||||
|
e2 -> l2 += child
|
||||||
|
else -> error("Unknown kind: $kind")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SignatureNode.split(
|
||||||
|
l1: MutableList<SignatureNode>,
|
||||||
|
e1: ElementKind,
|
||||||
|
l2: MutableList<SignatureNode>,
|
||||||
|
e2: ElementKind,
|
||||||
|
l3: MutableList<SignatureNode>,
|
||||||
|
e3: ElementKind) {
|
||||||
|
for (child in children) {
|
||||||
|
val kind = child.kind
|
||||||
|
when (kind) {
|
||||||
|
e1 -> l1 += child
|
||||||
|
e2 -> l2 += child
|
||||||
|
e3 -> l3 += child
|
||||||
|
else -> error("Unknown kind: $kind")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newNode = SignatureNode(kind, name)
|
||||||
|
stack.peek().children += newNode
|
||||||
|
stack.push(newNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSuperclass(): SignatureVisitor {
|
||||||
|
push(SuperClass, parent = Root)
|
||||||
|
return super.visitSuperclass()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitInterface(): SignatureVisitor {
|
||||||
|
push(Interface, parent = Root)
|
||||||
|
return super.visitInterface()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFormalTypeParameter(name: String) {
|
||||||
|
push(TypeParameter, parent = Root, name = name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClassBound(): SignatureVisitor {
|
||||||
|
push(ClassBound, parent = TypeParameter)
|
||||||
|
return super.visitClassBound()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitInterfaceBound(): SignatureVisitor {
|
||||||
|
push(InterfaceBound, parent = TypeParameter)
|
||||||
|
return super.visitInterfaceBound()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTypeArgument() {
|
||||||
|
push(TypeArgument, parent = ClassType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTypeArgument(wildcard: Char): SignatureVisitor {
|
||||||
|
push(TypeArgument, parent = ClassType, name = wildcard.toString())
|
||||||
|
return super.visitTypeArgument(wildcard)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClassType(name: String) {
|
||||||
|
push(ClassType, name = name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTypeVariable(name: String) {
|
||||||
|
push(TypeVariable, parent = InterfaceBound, name = name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitEnd() {
|
||||||
|
while (stack.peek().kind != ClassType) {
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.kapt3
|
||||||
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
|
internal inline fun <T, R> mapValues(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
|
||||||
|
if (values == null) return JavacList.nil()
|
||||||
|
|
||||||
|
var result = JavacList.nil<R>()
|
||||||
|
for (item in values) {
|
||||||
|
f(item)?.let { result = result.prepend(it) }
|
||||||
|
}
|
||||||
|
return result.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun <T> mapPairedValues(valuePairs: List<Any>?, f: (String, Any) -> T?): JavacList<T> {
|
||||||
|
if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
|
||||||
|
|
||||||
|
val size = valuePairs.size
|
||||||
|
var result = JavacList.nil<T>()
|
||||||
|
assert(size % 2 == 0)
|
||||||
|
var index = 0
|
||||||
|
while (index < size) {
|
||||||
|
val key = valuePairs[index] as String
|
||||||
|
val value = valuePairs[index + 1]
|
||||||
|
f(key, value)?.let { result = result.prepend(it) }
|
||||||
|
index += 2
|
||||||
|
}
|
||||||
|
return result.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
|
||||||
|
return this.appendList(other)
|
||||||
|
}
|
||||||
@@ -65,6 +65,12 @@ public class JCTreeConverterTestGenerated extends AbstractJCTreeConverterTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericSimple.kt")
|
||||||
|
public void testGenericSimple() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/genericSimple.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inheritanceSimple.kt")
|
@TestMetadata("inheritanceSimple.kt")
|
||||||
public void testInheritanceSimple() throws Exception {
|
public void testInheritanceSimple() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/inheritanceSimple.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/inheritanceSimple.kt");
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import java.io.Serializable
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
interface Intf<I1, I2 : Serializable>
|
||||||
|
interface Intf2<out T : List<String>, M : T>
|
||||||
|
interface OtherIntf<O : CharSequence>
|
||||||
|
open class BaseClass<B : Any>
|
||||||
|
class MyClass<M1, M2> : Intf<Any, java.util.Date>, OtherIntf<String>, BaseClass<RuntimeException>()
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
public abstract interface Intf<I1 extends java.lang.Object, I2 extends java.io.Serializable> {
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public abstract interface Intf2<T extends java.util.List<? extends java.lang.String>, M extends T> {
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public abstract interface OtherIntf<O extends java.lang.CharSequence> {
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public class BaseClass<B extends java.lang.Object> {
|
||||||
|
|
||||||
|
public BaseClass() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public final class MyClass<M1 extends java.lang.Object, M2 extends java.lang.Object> extends BaseClass<java.lang.RuntimeException> implements Intf<java.lang.Object, java.util.Date>, OtherIntf<java.lang.String> {
|
||||||
|
|
||||||
|
public MyClass() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user