Kapt: Support all kinds of constant types (KT-35536)

This commit is contained in:
Yan Zhulanow
2019-12-25 20:29:09 +09:00
parent 532e879d5c
commit 520f9a6da5
4 changed files with 185 additions and 54 deletions
@@ -31,44 +31,44 @@ class KaptAnonymousTypeTransformer(
return convertPossiblyAnonymousType(type)
}
}
private fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
internal fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
convertPossiblyAnonymousType(elementTypeProjection.type)
)
}
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
convertPossiblyAnonymousType(elementTypeProjection.type)
)
}
val actualType = when {
isAnonymousObject(declaration) || DescriptorUtils.isLocal(declaration) -> {
if (type.constructor.supertypes.size == 1) {
convertPossiblyAnonymousType(type.constructor.supertypes.iterator().next())
} else {
/*
Frontend reports an error on public properties in this case,
but we ignore errors when making stubs, so there should be a reasonable fallback.
*/
type.builtIns.anyType
}
}
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection)
return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
}
return actualType.replace(newArguments = arguments)
}
val actualType = when {
isAnonymousObject(declaration) || DescriptorUtils.isLocal(declaration) -> {
if (type.constructor.supertypes.size == 1) {
convertPossiblyAnonymousType(type.constructor.supertypes.iterator().next())
} else {
/*
Frontend reports an error on public properties in this case,
but we ignore errors when making stubs, so there should be a reasonable fallback.
*/
type.builtIns.anyType
}
}
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection)
return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
}
return actualType.replace(newArguments = arguments)
}
@@ -25,6 +25,7 @@ import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.tree.TreeScanner
import kotlinx.kapt.KaptIgnored
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_PARAMETER_NAME
import org.jetbrains.kotlin.codegen.needsExperimentalCoroutinesWrapper
import org.jetbrains.kotlin.config.LanguageFeature
@@ -47,9 +48,7 @@ import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAM
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.RETURN_TYPE
import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
@@ -667,7 +666,10 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
val trace = DelegatingBindingTrace(kaptContext.bindingContext, "Kapt")
val const = evaluator.evaluateExpression(propertyInitializer, trace, propertyType)
if (const != null && !const.isError && const.canBeUsedInAnnotations && !const.usesNonConstValAsConstant) {
return convertConstantValueArguments(const.getValue(propertyType), listOf(propertyInitializer))
val asmValue = mapConstantValueToAsmRepresentation(const.toConstantValue(propertyType))
if (asmValue !== UnknownConstantValue) {
return convertConstantValueArguments(asmValue, listOf(propertyInitializer))
}
}
}
@@ -679,6 +681,64 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
return null
}
private object UnknownConstantValue
private fun mapConstantValueToAsmRepresentation(value: ConstantValue<*>): Any? {
return when (value) {
is ByteValue -> value.value
is CharValue -> value.value
is IntValue -> value.value
is LongValue -> value.value
is ShortValue -> value.value
is UByteValue -> value.value
is UShortValue -> value.value
is UIntValue -> value.value
is ULongValue -> value.value
is AnnotationValue -> {
val annotationDescriptor = value.value
val annotationNode = AnnotationNode(typeMapper.mapType(annotationDescriptor.type).descriptor)
val values = ArrayList<Any?>(annotationDescriptor.allValueArguments.size * 2)
for ((name, arg) in annotationDescriptor.allValueArguments) {
val mapped = mapConstantValueToAsmRepresentation(arg)
if (mapped === UnknownConstantValue) {
return UnknownConstantValue
}
values += name.asString()
values += mapped
}
annotationNode.values = values
return annotationNode
}
is ArrayValue -> {
val children = value.value
val result = ArrayList<Any?>(children.size)
for (child in children) {
val mapped = mapConstantValueToAsmRepresentation(child)
if (mapped === UnknownConstantValue) {
return UnknownConstantValue
}
result += mapped
}
return result
}
is BooleanValue -> value.value
is DoubleValue -> value.value
is EnumValue -> {
val (classId, name) = value.value
val enumType = AsmUtil.asmTypeByClassId(classId)
return arrayOf(enumType.descriptor, name.asString())
}
is FloatValue -> value.value
is StringValue -> value.value
is NullValue -> null
else -> {
// KClassValue is intentionally omitted as incompatible with Java
UnknownConstantValue
}
}
}
private fun convertMethod(
method: MethodNode,
containingClass: ClassNode,
@@ -1196,21 +1256,6 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
treeMaker.Select(treeMaker.Type(enumType), treeMaker.name(valueName))
}
is Pair<*, *> -> {
assert(value.first is ClassId)
assert(value.second is Name)
val valueName = with((value.second as Name).identifier) {
if (isValidIdentifier(this)) {
this
} else {
kaptContext.compiler.log.report(kaptContext.kaptError("'$this' is an invalid Java enum value name"))
"InvalidFieldName"
}
}
val asSingleFqName = (value.first as ClassId).asSingleFqName()
treeMaker.Select(treeMaker.FqName(asSingleFqName), treeMaker.name(value.second.toString()))
}
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapJList(value) { convertLiteralExpression(it) })
is Type -> treeMaker.Select(treeMaker.Type(value), treeMaker.name("class"))
@@ -25,6 +25,17 @@ object Boo {
class HavingState {
val state = State.START
val stateArray = arrayOf(State.START)
val stringArray = arrayOf("foo")
val stringList = listOf("foo")
val intArray = arrayOf(1)
val intList = listOf(1)
val uint = 1U
val uintArray = arrayOf(1U)
val uintList = listOf(1U)
val clazz = State::class
val javaClass = State::class.java
val anonymous = (object {})::class
}
enum class State {
@@ -126,12 +126,87 @@ import java.lang.System;
public final class HavingState {
@org.jetbrains.annotations.NotNull()
private final State state = State.START;
@org.jetbrains.annotations.NotNull()
private final State[] stateArray = {State.START};
@org.jetbrains.annotations.NotNull()
private final java.lang.String[] stringArray = {"foo"};
@org.jetbrains.annotations.NotNull()
private final java.util.List<java.lang.String> stringList = null;
@org.jetbrains.annotations.NotNull()
private final java.lang.Integer[] intArray = {1};
@org.jetbrains.annotations.NotNull()
private final java.util.List<java.lang.Integer> intList = null;
private final int uint = 1;
@org.jetbrains.annotations.NotNull()
private final kotlin.UInt[] uintArray = {1};
@org.jetbrains.annotations.NotNull()
private final java.util.List<kotlin.UInt> uintList = null;
@org.jetbrains.annotations.NotNull()
private final kotlin.reflect.KClass<State> clazz = null;
@org.jetbrains.annotations.NotNull()
private final java.lang.Class<State> javaClass = null;
@org.jetbrains.annotations.NotNull()
private final kotlin.reflect.KClass<? extends java.lang.Object> anonymous = null;
@org.jetbrains.annotations.NotNull()
public final State getState() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final State[] getStateArray() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.lang.String[] getStringArray() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.util.List<java.lang.String> getStringList() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.lang.Integer[] getIntArray() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.util.List<java.lang.Integer> getIntList() {
return null;
}
public final int getUint() {
return 0;
}
@org.jetbrains.annotations.NotNull()
public final kotlin.UInt[] getUintArray() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.util.List<kotlin.UInt> getUintList() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final kotlin.reflect.KClass<State> getClazz() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final java.lang.Class<State> getJavaClass() {
return null;
}
@org.jetbrains.annotations.NotNull()
public final kotlin.reflect.KClass<? extends java.lang.Object> getAnonymous() {
return null;
}
public HavingState() {
super();
}