Kapt3: Number of bugfixes in JCTreeConverter:
Convert Java primitive and array types properly. Enums: ignore first two synthetic constructor parameters, do not generate super class constructor call, add ACC_ENUM flag to enum values, do not generate "values" and "valueOf" methods. Provide a JavaFileObject for JCCompilationUnit. Handle DefaultImpls, ignore empty DefaultImpls classes. Use a name table from Javac Names.
This commit is contained in:
committed by
Yan Zhulanow
parent
e131763cb0
commit
e4158a5571
@@ -16,23 +16,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.kapt3
|
||||
|
||||
import com.sun.tools.javac.code.TypeTag
|
||||
import com.sun.tools.javac.file.JavacFileManager
|
||||
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.util.Context
|
||||
import com.sun.tools.javac.util.Name
|
||||
import com.sun.tools.javac.util.Names
|
||||
import com.sun.tools.javac.util.SharedNameTable
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import java.util.*
|
||||
import javax.lang.model.element.ElementKind
|
||||
import javax.tools.JavaFileManager
|
||||
import com.sun.tools.javac.util.List as JavacList
|
||||
|
||||
class JCTreeConverter(
|
||||
@@ -52,7 +55,7 @@ class JCTreeConverter(
|
||||
Opcodes.ACC_SYNCHRONIZED or Opcodes.ACC_STATIC or Opcodes.ACC_NATIVE or Opcodes.ACC_DEPRECATED
|
||||
|
||||
private val FIELD_MODIFIERS = VISIBILITY_MODIFIERS or MODALITY_MODIFIERS or
|
||||
Opcodes.ACC_STATIC or Opcodes.ACC_VOLATILE or Opcodes.ACC_TRANSIENT
|
||||
Opcodes.ACC_STATIC or Opcodes.ACC_VOLATILE or Opcodes.ACC_TRANSIENT or Opcodes.ACC_ENUM
|
||||
|
||||
private val BLACKLISTED_ANNOTATATIONS = listOf(
|
||||
"java.lang.Deprecated", "kotlin.Deprecated", // Deprecated annotations
|
||||
@@ -63,8 +66,9 @@ class JCTreeConverter(
|
||||
)
|
||||
}
|
||||
|
||||
private val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
||||
private val treeMaker = TreeMaker.instance(context)
|
||||
private val nameTable = SharedNameTable(Names.instance(context))
|
||||
private val nameTable = Names.instance(context).table
|
||||
|
||||
private var done = false
|
||||
|
||||
@@ -82,7 +86,7 @@ class JCTreeConverter(
|
||||
// Nested classes will be processed during the outer classes conversion
|
||||
if (descriptor.isNested) return null
|
||||
|
||||
val classDeclaration = convertClass(clazz)
|
||||
val classDeclaration = convertClass(clazz, true) ?: return null
|
||||
|
||||
val packageAnnotations = JavacList.nil<JCAnnotation>()
|
||||
val packageName = ktFile.packageFqName.asString()
|
||||
@@ -91,13 +95,16 @@ class JCTreeConverter(
|
||||
val imports = JavacList.nil<JCTree>()
|
||||
val classes = JavacList.of<JCTree>(classDeclaration)
|
||||
|
||||
return treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
|
||||
val topLevel = treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
|
||||
val javaFileObject = SyntheticJavaFileObject(topLevel, classDeclaration, System.currentTimeMillis(), fileManager)
|
||||
topLevel.sourcefile = javaFileObject
|
||||
return topLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false for the inner classe or if the origin for the class was not found.
|
||||
*/
|
||||
private fun convertClass(clazz: ClassNode): JCClassDecl? {
|
||||
private fun convertClass(clazz: ClassNode, isTopLevel: Boolean): JCClassDecl? {
|
||||
if (isSynthetic(clazz.access)) return null
|
||||
|
||||
val descriptor = origins[clazz]?.descriptor as? ClassDescriptor ?: return null
|
||||
@@ -105,17 +112,41 @@ class JCTreeConverter(
|
||||
if (!descriptor.isInner && descriptor.isNested) (clazz.access or Opcodes.ACC_STATIC) else clazz.access,
|
||||
ElementKind.CLASS, clazz.visibleAnnotations, clazz.invisibleAnnotations)
|
||||
|
||||
val simpleName = name(descriptor.name.asString())
|
||||
val isEnum = clazz.isEnum()
|
||||
val isAnnotation = clazz.isAnnotation()
|
||||
|
||||
val isDefaultImpls = clazz.name.endsWith("${descriptor.name.asString()}/DefaultImpls")
|
||||
&& isPublic(clazz.access) && isFinal(clazz.access)
|
||||
&& descriptor.kind == ClassKind.INTERFACE
|
||||
|
||||
// DefaultImpls without any contents don't have INNERCLASS'es inside it (and inside the parent interface)
|
||||
if (isDefaultImpls && (isTopLevel || (clazz.fields.isNullOrEmpty() && clazz.methods.isNullOrEmpty()))) {
|
||||
return null
|
||||
}
|
||||
|
||||
val simpleName = name(if (isDefaultImpls) "DefaultImpls" else descriptor.name.asString())
|
||||
|
||||
val typeParams = JavacList.nil<JCTypeParameter>()
|
||||
val extending = if (clazz.superName == "java/lang/Object" || clazz.isEnum()) null else convertFqName(clazz.superName)
|
||||
val implementing = mapValues(clazz.interfaces) { convertFqName(it) }
|
||||
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
|
||||
convertFqName(it)
|
||||
}
|
||||
|
||||
val fields = mapValues<FieldNode, JCTree>(clazz.fields) { convertField(it) }
|
||||
val methods = mapValues<MethodNode, JCTree>(clazz.methods) { convertMethod(it, clazz, simpleName) }
|
||||
val methods = mapValues<MethodNode, JCTree>(clazz.methods) {
|
||||
if (isEnum) {
|
||||
if (it.name == "values" && it.desc == "()[L${clazz.name};") return@mapValues null
|
||||
if (it.name == "valueOf" && it.desc == "(Ljava/lang/String;)L${clazz.name};") return@mapValues null
|
||||
}
|
||||
|
||||
convertMethod(it, clazz)
|
||||
}
|
||||
val nestedClasses = mapValues<InnerClassNode, JCTree>(clazz.innerClasses) { innerClass ->
|
||||
if (innerClass.outerName != clazz.name) return@mapValues null
|
||||
val innerClassNode = classes.firstOrNull { it.name == innerClass.name } ?: return@mapValues null
|
||||
convertClass(innerClassNode)
|
||||
convertClass(innerClassNode, false)
|
||||
}
|
||||
|
||||
return treeMaker.ClassDef(modifiers, simpleName, typeParams, extending, implementing, fields + methods + nestedClasses)
|
||||
@@ -127,16 +158,26 @@ class JCTreeConverter(
|
||||
val modifiers = convertModifiers(field.access, ElementKind.FIELD, field.visibleAnnotations, field.invisibleAnnotations)
|
||||
val name = name(field.name)
|
||||
val type = Type.getType(field.desc)
|
||||
val typeExpression = convertFqName(type.className)
|
||||
|
||||
// Enum type must be an identifier (Javac requirement)
|
||||
val typeExpression = if (isEnum(field.access)) {
|
||||
convertSimpleName(type.className.substringAfterLast('.'))
|
||||
} else {
|
||||
convertType(type)
|
||||
}
|
||||
|
||||
val value = field.value
|
||||
|
||||
val initializer = when (value) {
|
||||
is Byte, is Boolean, is Char, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
|
||||
is Char -> treeMaker.Literal(TypeTag.CHAR, value.toInt())
|
||||
is Byte, is Boolean, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
|
||||
else -> if (isFinal(field.access)) convertLiteralExpression(getDefaultValue(type)) else null
|
||||
}
|
||||
|
||||
return treeMaker.VarDef(modifiers, name, typeExpression, initializer)
|
||||
}
|
||||
|
||||
private fun convertMethod(method: MethodNode, containingClass: ClassNode, containingClassSimpleName: Name): JCMethodDecl? {
|
||||
private fun convertMethod(method: MethodNode, containingClass: ClassNode): JCMethodDecl? {
|
||||
if (isSynthetic(method.access)) return null
|
||||
val descriptor = origins[method]?.descriptor as? CallableDescriptor ?: return null
|
||||
|
||||
@@ -147,22 +188,24 @@ class JCTreeConverter(
|
||||
method.visibleAnnotations
|
||||
}
|
||||
|
||||
val modifiers = convertModifiers(method.access, ElementKind.METHOD, visibleAnnotations, method.invisibleAnnotations)
|
||||
val modifiers = convertModifiers(
|
||||
if (containingClass.isEnum()) (method.access and VISIBILITY_MODIFIERS.inv()) else method.access,
|
||||
ElementKind.METHOD, visibleAnnotations, method.invisibleAnnotations)
|
||||
|
||||
val isConstructor = method.name == "<init>"
|
||||
val name = if (isConstructor) containingClassSimpleName else name(method.name)
|
||||
val name = name(method.name)
|
||||
val typeParameters = JavacList.nil<JCTypeParameter>()
|
||||
val receiverParameter = null
|
||||
|
||||
val returnType = Type.getReturnType(method.desc)
|
||||
val returnTypeExpr = if (isConstructor) null else convertFqName(returnType.className)
|
||||
val returnTypeExpr = if (isConstructor) null else convertType(returnType)
|
||||
|
||||
val parametersInfo = method.getParametersInfo()
|
||||
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 name = name(info.name)
|
||||
val type = convertFqName(info.type)
|
||||
val type = convertType(info.type)
|
||||
treeMaker.VarDef(modifiers, name, type, null)
|
||||
}
|
||||
|
||||
@@ -172,8 +215,10 @@ class JCTreeConverter(
|
||||
|
||||
val body = if (defaultValue != null) {
|
||||
null
|
||||
} else if ((method.access and Opcodes.ACC_ABSTRACT) > 0) {
|
||||
} else if (isAbstract(method.access)) {
|
||||
null
|
||||
} else if (isConstructor && containingClass.isEnum()) {
|
||||
treeMaker.Block(0, JavacList.nil())
|
||||
} else if (isConstructor) {
|
||||
// We already checked it in convertClass()
|
||||
val declaration = origins[containingClass]?.descriptor as ClassDescriptor
|
||||
@@ -226,10 +271,11 @@ class JCTreeConverter(
|
||||
}
|
||||
|
||||
private fun convertAnnotation(annotation: AnnotationNode): JCAnnotation? {
|
||||
val fqName = Type.getType(annotation.desc).className
|
||||
val annotationType = Type.getType(annotation.desc)
|
||||
val fqName = annotationType.className
|
||||
if (BLACKLISTED_ANNOTATATIONS.any { fqName.startsWith(it) }) return null
|
||||
|
||||
val name = convertFqName(fqName)
|
||||
val name = convertType(annotationType)
|
||||
val values = mapPairedValues<JCExpression>(annotation.values) { key, value ->
|
||||
treeMaker.Assign(convertSimpleName(key), convertLiteralExpression(value))
|
||||
}
|
||||
@@ -238,8 +284,9 @@ class JCTreeConverter(
|
||||
|
||||
private fun convertLiteralExpression(value: Any?): JCExpression {
|
||||
return when (value) {
|
||||
null -> convertSimpleName("null")
|
||||
is Byte, is Boolean, is Char, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
|
||||
null -> treeMaker.Literal(TypeTag.BOT, null)
|
||||
is Char -> treeMaker.Literal(TypeTag.CHAR, value.toInt())
|
||||
is Byte, is Boolean, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
|
||||
|
||||
is ByteArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
is BooleanArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
@@ -249,10 +296,15 @@ class JCTreeConverter(
|
||||
is LongArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
is FloatArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
is DoubleArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
is Array<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
|
||||
is Array<*> -> { // Two-element String array for enumerations ([desc, fieldName])
|
||||
assert(value.size == 2)
|
||||
val enumType = Type.getType(value[0] as String)
|
||||
val valueName = value[1] as String
|
||||
treeMaker.Select(convertType(enumType), name(valueName))
|
||||
}
|
||||
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value) { convertLiteralExpression(it) })
|
||||
|
||||
is Type -> treeMaker.Select(convertFqName(value.className), name("class"))
|
||||
is Type -> treeMaker.Select(convertType(value), name("class"))
|
||||
is AnnotationNode -> convertAnnotation(value) ?: error("Annotation is filtered out")
|
||||
else -> throw IllegalArgumentException("Illegal literal expression value: $value (${value.javaClass.canonicalName})")
|
||||
}
|
||||
@@ -270,6 +322,14 @@ class JCTreeConverter(
|
||||
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())
|
||||
@@ -282,7 +342,23 @@ class JCTreeConverter(
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun convertSimpleName(name: String): JCIdent = treeMaker.Ident(name(name))
|
||||
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)
|
||||
}
|
||||
@@ -290,21 +366,38 @@ class JCTreeConverter(
|
||||
private class ParameterInfo(
|
||||
val access: Int,
|
||||
val name: String,
|
||||
val type: String,
|
||||
val type: Type,
|
||||
val visibleAnnotations: List<AnnotationNode>?,
|
||||
val invisibleAnnotations: List<AnnotationNode>?)
|
||||
|
||||
private fun MethodNode.getParametersInfo(): List<ParameterInfo> {
|
||||
private fun MethodNode.getParametersInfo(containingClass: ClassNode): List<ParameterInfo> {
|
||||
val localVariables = this.localVariables ?: emptyList()
|
||||
val parameters = this.parameters ?: emptyList()
|
||||
val isStatic = (access and Opcodes.ACC_STATIC) > 0
|
||||
val isStatic = isStatic(access)
|
||||
|
||||
// First and second parameters in enum constructors are synthetic, we should ignore them
|
||||
val isEnumConstructor = (name == "<init>") && containingClass.isEnum()
|
||||
val startParameterIndex = if (isEnumConstructor) 2 else 0
|
||||
|
||||
val parameterTypes = Type.getArgumentTypes(desc)
|
||||
return parameterTypes.mapIndexed { index, type ->
|
||||
val name = parameters.getOrNull(index)?.name ?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name ?: "p$index"
|
||||
|
||||
val parameterInfos = ArrayList<ParameterInfo>(parameterTypes.size - startParameterIndex)
|
||||
for (index in startParameterIndex..parameterTypes.lastIndex) {
|
||||
val type = parameterTypes[index]
|
||||
var name = parameters.getOrNull(index - startParameterIndex)?.name
|
||||
?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name
|
||||
?: "p${index - startParameterIndex}"
|
||||
|
||||
// Property setters has bad parameter names
|
||||
if (name.startsWith("<") && name.endsWith(">")) {
|
||||
name = "p${index - startParameterIndex}"
|
||||
}
|
||||
|
||||
val visibleAnnotations = visibleParameterAnnotations?.get(index)
|
||||
val invisibleAnnotations = invisibleParameterAnnotations?.get(index)
|
||||
ParameterInfo(0, name, type.className, visibleAnnotations, invisibleAnnotations)
|
||||
parameterInfos += ParameterInfo(0, name, type, visibleAnnotations, invisibleAnnotations)
|
||||
}
|
||||
return parameterInfos
|
||||
}
|
||||
|
||||
private inline fun <T, R> mapValues(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
|
||||
@@ -340,7 +433,13 @@ private operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList
|
||||
private val ClassDescriptor.isNested: Boolean
|
||||
get() = containingDeclaration is ClassDescriptor
|
||||
|
||||
private fun isEnum(access: Int) = (access and Opcodes.ACC_ENUM) > 0
|
||||
private fun isPublic(access: Int) = (access and Opcodes.ACC_PUBLIC) > 0
|
||||
private fun isSynthetic(access: Int) = (access and Opcodes.ACC_SYNTHETIC) > 0
|
||||
private fun isFinal(access: Int) = (access and Opcodes.ACC_FINAL) > 0
|
||||
private fun isStatic(access: Int) = (access and Opcodes.ACC_STATIC) > 0
|
||||
private fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 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 <T> List<T>.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
@@ -24,10 +24,8 @@ import com.sun.tools.javac.processing.AnnotationProcessingError
|
||||
import com.sun.tools.javac.processing.JavacProcessingEnvironment
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.util.Context
|
||||
import com.sun.tools.javac.util.Log
|
||||
import com.sun.tools.javac.util.Options
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import javax.annotation.processing.Processor
|
||||
import javax.tools.JavaFileManager
|
||||
import com.sun.tools.javac.util.List as JavacList
|
||||
@@ -52,14 +50,16 @@ class KaptError : RuntimeException {
|
||||
|
||||
class KaptRunner {
|
||||
val context = Context()
|
||||
val compiler: KaptJavaCompiler
|
||||
private val options: Options
|
||||
|
||||
init {
|
||||
JavacFileManager.preRegister(context)
|
||||
KaptJavaCompiler.preRegister(context)
|
||||
|
||||
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
||||
|
||||
options = Options.instance(context)
|
||||
context.put(Log.outKey, PrintWriter(System.err, true))
|
||||
}
|
||||
|
||||
fun parseJavaFiles(
|
||||
@@ -69,7 +69,6 @@ class KaptRunner {
|
||||
classpath.forEach { options.put(Option.CLASSPATH, it.canonicalPath) }
|
||||
|
||||
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
||||
val compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
||||
|
||||
try {
|
||||
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
|
||||
@@ -93,7 +92,6 @@ class KaptRunner {
|
||||
options.put(Option.D, classOutputDir.canonicalPath)
|
||||
|
||||
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
|
||||
val compiler = JavaCompiler.instance(context) as KaptJavaCompiler
|
||||
val processingEnvironment = JavacProcessingEnvironment.instance(context)
|
||||
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.file.BaseFileObject
|
||||
import com.sun.tools.javac.file.JavacFileManager
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import javax.lang.model.element.Modifier
|
||||
import javax.lang.model.element.NestingKind
|
||||
import javax.tools.JavaFileObject
|
||||
|
||||
class SyntheticJavaFileObject(
|
||||
val compilationUnit: JCTree.JCCompilationUnit,
|
||||
val clazz: JCTree.JCClassDecl,
|
||||
val timestamp: Long,
|
||||
fileManager: JavacFileManager
|
||||
) : BaseFileObject(fileManager) {
|
||||
override fun getShortName() = clazz.simpleName.toString()
|
||||
|
||||
override fun inferBinaryName(path: MutableIterable<File>?) = throw UnsupportedOperationException()
|
||||
|
||||
override fun isNameCompatible(simpleName: String?, kind: JavaFileObject.Kind?) = true
|
||||
|
||||
override fun getKind() = JavaFileObject.Kind.SOURCE
|
||||
|
||||
override fun getName() = compilationUnit.packageName.toString().replace('.', '/') + clazz.name + ".java"
|
||||
|
||||
override fun getAccessLevel(): Modifier? {
|
||||
val flags = clazz.modifiers.getFlags()
|
||||
if (Modifier.PUBLIC in flags) return Modifier.PUBLIC
|
||||
if (Modifier.PROTECTED in flags) return Modifier.PROTECTED
|
||||
if (Modifier.PRIVATE in flags) return Modifier.PRIVATE
|
||||
return null
|
||||
}
|
||||
|
||||
override fun openInputStream() = getCharContent(false).byteInputStream()
|
||||
|
||||
override fun getCharContent(ignoreEncodingErrors: Boolean) = compilationUnit.toString()
|
||||
|
||||
override fun getNestingKind() = NestingKind.TOP_LEVEL
|
||||
|
||||
override fun toUri(): URI? = URL(name).toURI()
|
||||
|
||||
override fun openReader(ignoreEncodingErrors: Boolean) = getCharContent(ignoreEncodingErrors).reader()
|
||||
|
||||
override fun openWriter() = throw UnsupportedOperationException()
|
||||
|
||||
override fun getLastModified() = timestamp
|
||||
|
||||
override fun openOutputStream() = throw UnsupportedOperationException()
|
||||
|
||||
override fun delete() = throw UnsupportedOperationException()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as SyntheticJavaFileObject
|
||||
|
||||
if (compilationUnit != other.compilationUnit) return false
|
||||
if (clazz != other.clazz) return false
|
||||
if (timestamp != other.timestamp) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = 0
|
||||
result = 31 * result + compilationUnit.hashCode()
|
||||
result = 31 * result + clazz.hashCode()
|
||||
result = 31 * result + timestamp.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.kapt3.test
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.sun.tools.javac.comp.CompileStates
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestUtil
|
||||
import org.jetbrains.kotlin.kapt3.JCTreeConverter
|
||||
@@ -23,6 +25,7 @@ import org.jetbrains.kotlin.kapt3.Kapt3BuilderFactory
|
||||
import org.jetbrains.kotlin.kapt3.KaptRunner
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractJCTreeConverterTest : CodegenTestCase() {
|
||||
@@ -38,10 +41,23 @@ abstract class AbstractJCTreeConverterTest : CodegenTestCase() {
|
||||
val typeMapper = factory.generationState.typeMapper
|
||||
|
||||
val kaptRunner = KaptRunner()
|
||||
val converter = JCTreeConverter(kaptRunner.context, typeMapper, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
|
||||
val javaFiles = converter.convert()
|
||||
try {
|
||||
val converter = JCTreeConverter(kaptRunner.context, typeMapper, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
|
||||
val javaFiles = converter.convert()
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, javaFiles.joinToString("\n\n////////////////////\n\n"))
|
||||
kaptRunner.compiler.enterTrees(javaFiles)
|
||||
|
||||
val actualRaw = javaFiles.joinToString ("\n\n////////////////////\n\n")
|
||||
val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' })).trimTrailingWhitespacesAndAddNewlineAtEOF()
|
||||
|
||||
if (kaptRunner.compiler.shouldStop(CompileStates.CompileState.ENTER)) {
|
||||
error("There were errors during analysis. See errors above. Stubs:\n\n$actual")
|
||||
}
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, actual)
|
||||
} finally {
|
||||
kaptRunner.compiler.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,18 +35,48 @@ public class JCTreeConverterTestGenerated extends AbstractJCTreeConverterTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/kapt3/testData/converter"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/annotations.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataClass.kt")
|
||||
public void testDataClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/dataClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("defaultImpls.kt")
|
||||
public void testDefaultImpls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/defaultImpls.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enums.kt")
|
||||
public void testEnums() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/enums.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functions.kt")
|
||||
public void testFunctions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/functions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inheritanceSimple.kt")
|
||||
public void testInheritanceSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/inheritanceSimple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jvmStatic.kt")
|
||||
public void testJvmStatic() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmStatic.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClasses.kt")
|
||||
public void testNestedClasses() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/nestedClasses.kt");
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
annotation class Anno1
|
||||
enum class Colors { WHITE, BLACK }
|
||||
annotation class Anno2(
|
||||
val i: Int = 5,
|
||||
val s: String = "ABC",
|
||||
val ii: IntArray = intArrayOf(1, 2, 3),
|
||||
val ss: Array<String> = arrayOf("A", "B"),
|
||||
val a: Anno1,
|
||||
val color: Colors = Colors.BLACK,
|
||||
val colors: Array<Colors> = arrayOf(Colors.BLACK, Colors.WHITE),
|
||||
val clazz: kotlin.reflect.KClass<*>,
|
||||
val classes: Array<kotlin.reflect.KClass<*>>
|
||||
)
|
||||
annotation class Anno3(val value: String)
|
||||
|
||||
@Anno1
|
||||
@Anno2(a = Anno1(), clazz = TestAnno::class, classes = arrayOf(TestAnno::class, Anno1::class))
|
||||
@Anno3(value = "value")
|
||||
class TestAnno
|
||||
|
||||
@Anno3("value")
|
||||
@Anno2(i = 6, s = "BCD", ii = intArrayOf(4, 5, 6), ss = arrayOf("Z", "X"),
|
||||
a = Anno1(), color = Colors.WHITE, colors = arrayOf(Colors.WHITE),
|
||||
clazz = TestAnno::class, classes = arrayOf(TestAnno::class, Anno1::class))
|
||||
class TestAnno2 {
|
||||
@Anno1
|
||||
fun a(@Anno3("param-pam-pam") param: String) {}
|
||||
|
||||
@get:Anno3("getter") @set:Anno3("setter") @property:Anno3("property") @field:Anno3("field") @setparam:Anno3("setparam")
|
||||
var b: String = "property initializer"
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
public abstract @interface Anno1 {
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum Colors {
|
||||
/*public static final*/ WHITE /* = null */,
|
||||
/*public static final*/ BLACK /* = null */;
|
||||
|
||||
Colors() {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public abstract @interface Anno2 {
|
||||
|
||||
public abstract int i() default 5;
|
||||
|
||||
public abstract java.lang.String s() default "ABC";
|
||||
|
||||
public abstract int[] ii() default {1, 2, 3};
|
||||
|
||||
public abstract java.lang.String[] ss() default {"A", "B"};
|
||||
|
||||
public abstract Anno1 a();
|
||||
|
||||
public abstract Colors color() default Colors.BLACK;
|
||||
|
||||
public abstract Colors[] colors() default {Colors.BLACK, Colors.WHITE};
|
||||
|
||||
public abstract java.lang.Class clazz();
|
||||
|
||||
public abstract java.lang.Class[] classes();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public abstract @interface Anno3 {
|
||||
|
||||
public abstract java.lang.String value();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
@Anno3(value = "value")
|
||||
@Anno2(a = @Anno1(), clazz = TestAnno.class, classes = {TestAnno.class, Anno1.class})
|
||||
@Anno1()
|
||||
public final class TestAnno {
|
||||
|
||||
public TestAnno() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
@Anno2(i = 6, s = "BCD", ii = {4, 5, 6}, ss = {"Z", "X"}, a = @Anno1(), color = Colors.WHITE, colors = {Colors.WHITE}, clazz = TestAnno.class, classes = {TestAnno.class, Anno1.class})
|
||||
@Anno3(value = "value")
|
||||
public final class TestAnno2 {
|
||||
@Anno3(value = "field")
|
||||
private java.lang.String b;
|
||||
|
||||
@Anno1()
|
||||
public final void a(@Anno3(value = "param-pam-pam")
|
||||
java.lang.String param) {
|
||||
}
|
||||
|
||||
@Anno3(value = "getter")
|
||||
public final java.lang.String getB() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Anno3(value = "setter")
|
||||
public final void setB(@Anno3(value = "setparam")
|
||||
java.lang.String p0) {
|
||||
}
|
||||
|
||||
public TestAnno2() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -19,7 +19,7 @@ public final class User {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public /*missing*/ User(java.lang.String firstName, java.lang.String secondName, int age) {
|
||||
public User(java.lang.String firstName, java.lang.String secondName, int age) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
interface IntfWithoutDefaultImpls
|
||||
|
||||
interface IntfWithDefaultImpls {
|
||||
fun a() {}
|
||||
}
|
||||
|
||||
interface Intf {
|
||||
companion object {
|
||||
val BLACK = 1
|
||||
const val WHITE = 2
|
||||
}
|
||||
|
||||
val color: Int
|
||||
get() = BLACK
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
public abstract interface IntfWithoutDefaultImpls {
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public abstract interface IntfWithDefaultImpls {
|
||||
|
||||
public abstract void a();
|
||||
|
||||
public static final class DefaultImpls {
|
||||
|
||||
public DefaultImpls() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static void a(IntfWithDefaultImpls $this) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public abstract interface Intf {
|
||||
public static final Intf.Companion Companion = null;
|
||||
public static final int WHITE = 2;
|
||||
|
||||
public abstract int getColor();
|
||||
|
||||
public static final class DefaultImpls {
|
||||
|
||||
public DefaultImpls() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static int getColor(Intf $this) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Companion {
|
||||
private static final int BLACK = 1;
|
||||
public static final int WHITE = 2;
|
||||
|
||||
public final int getBLACK() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Companion() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
enum class Enum1 {
|
||||
BLACK, WHITE
|
||||
}
|
||||
|
||||
annotation class Anno1(val value: String)
|
||||
|
||||
enum class Enum2(@Anno1("first") val col: String, @Anno1("second") val col2: Int) {
|
||||
RED("red", 1), WHITE("white", 2);
|
||||
fun color() = col
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
public enum Enum1 {
|
||||
/*public static final*/ BLACK /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
|
||||
Enum1() {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public abstract @interface Anno1 {
|
||||
|
||||
public abstract java.lang.String value();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum Enum2 {
|
||||
/*public static final*/ RED /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
private final java.lang.String col = null;
|
||||
private final int col2 = 0;
|
||||
|
||||
final java.lang.String color() {
|
||||
return null;
|
||||
}
|
||||
|
||||
final java.lang.String getCol() {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int getCol2() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Enum2(@Anno1(value = "first")
|
||||
java.lang.String col, @Anno1(value = "second")
|
||||
int col2) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
class FunctionsTest {
|
||||
fun f() = String::length
|
||||
fun f2() = fun (a: Int, b: Int) = a > b
|
||||
fun f3() = run {}
|
||||
fun f4() = run { 3 }
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
public final class FunctionsTest {
|
||||
|
||||
public final kotlin.reflect.KProperty1 f() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final kotlin.jvm.functions.Function2 f2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void f3() {
|
||||
}
|
||||
|
||||
public final int f4() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public FunctionsTest() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
+11
-28
@@ -1,31 +1,14 @@
|
||||
|
||||
public abstract interface Context {
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public final class Context {
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum Result {
|
||||
;
|
||||
public static final Result SUCCESS = null;
|
||||
public static final Result ERROR = null;
|
||||
|
||||
protected /*missing*/ Result(java.lang.String $enum_name_or_ordinal$0, int $enum_name_or_ordinal$1) {
|
||||
super(null, 0);
|
||||
}
|
||||
|
||||
public static Result[] values() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Result valueOf(java.lang.String p0) {
|
||||
return null;
|
||||
/*public static final*/ SUCCESS /* = null */,
|
||||
/*public static final*/ ERROR /* = null */;
|
||||
|
||||
Result() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +16,10 @@ public enum Result {
|
||||
|
||||
|
||||
public abstract class BaseClass {
|
||||
|
||||
|
||||
public abstract Result doJob();
|
||||
|
||||
public /*missing*/ BaseClass(Context context, int num, boolean bool) {
|
||||
|
||||
public BaseClass(Context context, int num, boolean bool) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -45,13 +28,13 @@ public abstract class BaseClass {
|
||||
|
||||
|
||||
public final class Inheritor extends BaseClass {
|
||||
|
||||
|
||||
@java.lang.Override()
|
||||
public Result doJob() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public /*missing*/ Inheritor(Context context) {
|
||||
|
||||
public Inheritor(Context context) {
|
||||
super(null, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
class JvmStaticTest {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val one = 1
|
||||
|
||||
val two = 2
|
||||
|
||||
const val c: Char = 'C'
|
||||
}
|
||||
|
||||
val three: Byte = 3.toByte()
|
||||
val d: Char = 'D'
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
public final class JvmStaticTest {
|
||||
private final byte three = "3";
|
||||
private final char d = 'D';
|
||||
private static final int one = 1;
|
||||
private static final int two = 2;
|
||||
public static final char c = 'C';
|
||||
public static final JvmStaticTest.Companion Companion = null;
|
||||
|
||||
public final byte getThree() {
|
||||
return "0";
|
||||
}
|
||||
|
||||
public final char getD() {
|
||||
return '\u0000';
|
||||
}
|
||||
|
||||
public JvmStaticTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static final int getOne() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final class Companion {
|
||||
|
||||
public final int getOne() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final int getTwo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Companion() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-20
@@ -1,19 +1,18 @@
|
||||
|
||||
public final class Test {
|
||||
|
||||
public /*missing*/ Test() {
|
||||
public Test() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static final class Nested {
|
||||
|
||||
public /*missing*/ Nested() {
|
||||
public Nested() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static final class NestedNested {
|
||||
|
||||
public /*missing*/ NestedNested() {
|
||||
public NestedNested() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -21,7 +20,7 @@ public final class Test {
|
||||
|
||||
public final class Inner {
|
||||
|
||||
public /*missing*/ Inner(Test $outer) {
|
||||
public Inner(Test $outer) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -29,7 +28,7 @@ public final class Test {
|
||||
public static final class NestedObject {
|
||||
public static final Test.NestedObject INSTANCE = null;
|
||||
|
||||
private /*missing*/ NestedObject() {
|
||||
private NestedObject() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -38,20 +37,10 @@ public final class Test {
|
||||
}
|
||||
|
||||
public static enum NestedEnum {
|
||||
;
|
||||
public static final Test.NestedEnum BLACK = null;
|
||||
public static final Test.NestedEnum WHITE = null;
|
||||
/*public static final*/ BLACK /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
|
||||
protected /*missing*/ NestedEnum(java.lang.String $enum_name_or_ordinal$0, int $enum_name_or_ordinal$1) {
|
||||
super(null, 0);
|
||||
}
|
||||
|
||||
public static Test.NestedEnum[] values() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Test.NestedEnum valueOf(java.lang.String p0) {
|
||||
return null;
|
||||
NestedEnum() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user