Refactor KClassValue to store ClassLiteralValue internally

Only invariant array projections and non-null element types will be
supported soon (see KT-26568), so it makes no sense to store the
complete type in KClassValue. What we need is only the ClassId of the
class, and the number of times it's wrapped into kotlin/Array, which is
exactly what ClassLiteralValue represents.

This change helps in decoupling annotation values from
descriptors/types. The only constant value that depends on descriptors
is now AnnotationValue.

 #KT-26582 Fixed
This commit is contained in:
Alexander Udalov
2018-09-05 13:22:28 +03:00
parent bad30a4b99
commit c1ab08c8ce
23 changed files with 131 additions and 124 deletions
@@ -439,7 +439,7 @@ public abstract class AnnotationCodegen {
@Override
public Void visitKClassValue(KClassValue value, Void data) {
annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
annotationVisitor.visit(name, typeMapper.mapType(value.getArgumentType(module)));
return null;
}
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
@@ -1111,7 +1112,7 @@ public class FunctionCodegen {
}
@NotNull
public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper mapper) {
public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper typeMapper) {
AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
if (annotation == null) {
annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.jvm.Throws"));
@@ -1130,9 +1131,10 @@ public class FunctionCodegen {
arrayValue.getValue(),
(ConstantValue<?> constant) -> {
if (constant instanceof KClassValue) {
KClassValue classValue = (KClassValue) constant;
ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue());
return mapper.mapClass(classDescriptor).getInternalName();
ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(
((KClassValue) constant).getArgumentType(DescriptorUtilsKt.getModule(function))
);
return typeMapper.mapClass(classDescriptor).getInternalName();
}
return null;
}
@@ -287,6 +287,10 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
String elementDesc = nestedness == 0 ? typeDesc : type.getElementType().getDescriptor();
JvmPrimitiveType primType = JvmPrimitiveType.getByDesc(elementDesc);
if (primType != null) {
if (nestedness > 0) {
// "int[][]" should be loaded as "Array<IntArray>", not as "Array<Array<Int>>"
return new ClassLiteralValue(ClassId.topLevel(primType.getPrimitiveType().getArrayTypeFqName()), nestedness - 1);
}
return new ClassLiteralValue(ClassId.topLevel(primType.getPrimitiveType().getTypeFqName()), nestedness);
}
ClassId javaClassId = resolveNameByDesc(elementDesc, innerClasses);
@@ -53,7 +53,8 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
for (annotationClass in annotationClasses) {
val classDescriptor =
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor as? ClassDescriptor ?: continue
(annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor
?: continue
val experimentality = with(ExperimentalUsageChecker) {
classDescriptor.loadExperimentalityForMarkerAnnotation()
}
@@ -189,7 +189,9 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
if (descriptor?.fqName == USE_EXPERIMENTAL_FQ_NAME) {
val annotationClasses = descriptor.allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
annotationClasses is ArrayValue && annotationClasses.value.any { annotationClass ->
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor?.fqNameSafe == annotationFqName
annotationClass is KClassValue && annotationClass.value.let { (classId, arrayDimensions) ->
classId.asSingleFqName() == annotationFqName && arrayDimensions == 0
}
}
} else false
}
@@ -888,7 +888,9 @@ private class ConstantExpressionEvaluatorVisitor(
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
val type = trace.getType(expression)!!
if (type.isError) return null
return KClassValue(type).wrap()
val descriptor = type.constructor.declarationDescriptor
if (descriptor !is ClassDescriptor || !KotlinBuiltIns.isKClass(descriptor)) return null
return KClassValue.create(type.arguments.first().type)?.wrap()
}
private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: KotlinType): List<CompileTimeConstant<*>?> {
@@ -8,12 +8,12 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module
sealed class SinceKotlinAccessibility {
object Accessible : SinceKotlinAccessibility()
@@ -102,13 +102,13 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue?
return result
}
private fun Annotated.loadWasExperimentalMarkerClasses(): List<ClassDescriptor> {
private fun DeclarationDescriptor.loadWasExperimentalMarkerClasses(): List<ClassDescriptor> {
val wasExperimental = annotations.findAnnotation(ExperimentalUsageChecker.WAS_EXPERIMENTAL_FQ_NAME)
if (wasExperimental != null) {
val annotationClasses = wasExperimental.allValueArguments[ExperimentalUsageChecker.WAS_EXPERIMENTAL_ANNOTATION_CLASS]
if (annotationClasses is ArrayValue) {
return annotationClasses.value.mapNotNull { annotationClass ->
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor as? ClassDescriptor
(annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor
}
}
}
@@ -85,7 +85,7 @@ class ConstantValueGenerator(
is AnnotationValue -> generateAnnotationConstructorCall(constantValue.value)
is KClassValue -> {
val classifierKtType = constantValue.value
val classifierKtType = constantValue.getArgumentType(moduleDescriptor)
val classifierDescriptor = classifierKtType.constructor.declarationDescriptor
?: throw AssertionError("Unexpected KClassValue: $classifierKtType")
@@ -16,8 +16,6 @@
package org.jetbrains.kotlin.serialization
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.metadata.ProtoBuf
@@ -100,22 +98,11 @@ class AnnotationSerializer(private val stringTable: DescriptorAwareStringTable)
}
override fun visitKClassValue(value: KClassValue, data: Unit) {
var kotlinType = value.value
var arrayDimensions = 0
while (KotlinBuiltIns.isArray(kotlinType)) {
// We only support invariant projections and non-null array element types, see KT-26568
kotlinType = kotlinType.arguments.single().type
arrayDimensions++
}
val descriptor = kotlinType.constructor.declarationDescriptor as? ClassDescriptor
?: throw UnsupportedOperationException("Class literal annotation argument should be a class: $value")
type = Type.CLASS
classId = stringTable.getFqNameIndex(descriptor)
classId = stringTable.getQualifiedClassNameIndex(value.classId)
if (arrayDimensions > 0) {
arrayDimensionCount = arrayDimensions
if (value.arrayDimensions > 0) {
arrayDimensionCount = value.arrayDimensions
}
}
@@ -27,6 +27,7 @@ annotation class Anno(
val za: BooleanArray,
val str: String,
val k: KClass<*>,
val k2: KClass<*>,
val e: AnnotationTarget,
val a: Nested,
val stra: Array<String>,
@@ -54,10 +55,11 @@ fun f(): @Anno(
[false, true],
"lol",
Number::class,
IntArray::class,
AnnotationTarget.EXPRESSION,
Nested("1"),
["lmao"],
// [Double::class, Unit::class],
// [Double::class, Unit::class, LongArray::class],
[AnnotationTarget.TYPEALIAS, AnnotationTarget.FIELD],
[Nested("2"), Nested("3")]
) Unit {}
@@ -66,7 +68,7 @@ fun box(): String {
assertEquals(
"[@Anno(b=1, c=x, d=3.14, f=-2.72, i=42424242, j=239239239239239, s=42, z=true, " +
"ba=[-1], ca=[y], da=[-3.14159], fa=[2.7218], ia=[424242], ja=[239239239239], sa=[-43], za=[false, true], " +
"str=lol, k=class java.lang.Number, e=EXPRESSION, a=@Nested(value=1), " +
"str=lol, k=class java.lang.Number, k2=class [I, e=EXPRESSION, a=@Nested(value=1), " +
"stra=[lmao], ea=[TYPEALIAS, FIELD], aa=[@Nested(value=2), @Nested(value=3)])]",
::f.returnType.annotations.toString()
)
@@ -2,8 +2,8 @@ package
@Foo(a = {}) public fun test1(): kotlin.Unit
@Foo(a = {kotlin.Int::class, kotlin.String::class}) public fun test2(): kotlin.Unit
@Foo(a = {kotlin.Array<*>::class}) public fun test3(): kotlin.Unit
@Foo(a = {Gen<kotlin.Int>::class}) public fun test4(): kotlin.Unit
@Foo(a = {kotlin.Array<kotlin.Any>::class}) public fun test3(): kotlin.Unit
@Foo(a = {Gen::class}) public fun test4(): kotlin.Unit
@Foo(a = {""}) public fun test5(): kotlin.Unit
@Foo(a = {kotlin.Int::class, 1}) public fun test6(): kotlin.Unit
@Bar public fun test7(): kotlin.Unit
@@ -8,7 +8,7 @@ public open class ClassObjectArrayInParam {
public final val value: kotlin.Array<kotlin.reflect.KClass<*>>
}
@test.ClassObjectArrayInParam.Anno(value = {test.ClassObjectArrayInParam::class, test.ClassObjectArrayInParam.Nested::class, kotlin.String::class, kotlin.collections.(Mutable)List<(raw) kotlin.Any?>::class, kotlin.Array<(out) kotlin.Array<(out) kotlin.String!>!>::class, kotlin.Array<(out) kotlin.IntArray!>::class}) public open class Nested {
@test.ClassObjectArrayInParam.Anno(value = {test.ClassObjectArrayInParam::class, test.ClassObjectArrayInParam.Nested::class, kotlin.String::class, kotlin.collections.MutableList::class, kotlin.Array<kotlin.Array<kotlin.String>>::class, kotlin.Array<kotlin.IntArray>::class}) public open class Nested {
public constructor Nested()
}
}
@@ -3,8 +3,8 @@ package test
public final class A {
/*primary*/ public constructor A()
public final fun arrays(/*0*/ s: @test.Ann(klass = kotlin.Array<kotlin.Int>::class) kotlin.Array<kotlin.Int>, /*1*/ t: @test.Ann(klass = kotlin.Array<kotlin.IntArray>::class) kotlin.Array<kotlin.IntArray>, /*2*/ u: @test.Ann(klass = kotlin.Array<kotlin.Array<kotlin.Int>>::class) kotlin.Array<kotlin.Array<kotlin.Int>>, /*3*/ v: @test.Ann(klass = kotlin.Array<kotlin.Array<kotlin.Array<kotlin.String>>>::class) kotlin.Array<kotlin.Array<kotlin.Array<kotlin.String>>>): kotlin.Unit
public final fun generic(/*0*/ s: @test.Ann(klass = test.Generic<*>::class) kotlin.String): kotlin.Unit
public final fun innerGeneric(/*0*/ s: @test.Ann(klass = test.InnerGeneric<*, *>.Inner<*, *>::class) kotlin.String): kotlin.Unit
public final fun generic(/*0*/ s: @test.Ann(klass = test.Generic::class) kotlin.String): kotlin.Unit
public final fun innerGeneric(/*0*/ s: @test.Ann(klass = test.InnerGeneric.Inner::class) kotlin.String): kotlin.Unit
public final fun simple(/*0*/ s: @test.Ann(klass = test.Simple::class) kotlin.String): kotlin.Unit
}
@@ -87,7 +87,7 @@ public class AnnotationDescriptorResolveTest extends AbstractAnnotationDescripto
public void testJavaClassAnnotation() throws Exception {
String content = getContent("AnnClass(MyClass::class)");
String expectedAnnotation = "@AnnClass(a = MyClass::class)";
String expectedAnnotation = "@AnnClass(a = test.MyClass::class)";
doTest(content, expectedAnnotation);
}
}
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.load.java.lazy.descriptors
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
import org.jetbrains.kotlin.load.java.components.TypeUsage
@@ -33,9 +31,10 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.isError
class LazyJavaAnnotationDescriptor(
private val c: LazyJavaResolverContext,
@@ -101,21 +100,8 @@ class LazyJavaAnnotationDescriptor(
return EnumValue(enumClassId, entryName)
}
private fun resolveFromJavaClassObjectType(javaType: JavaType): ConstantValue<*>? {
// Class type is never nullable in 'Foo.class' in Java
val type = TypeUtils.makeNotNullable(c.typeResolver.transformJavaType(
javaType,
TypeUsage.COMMON.toAttributes())
)
val jlClass = c.module.resolveTopLevelClass(FqName("java.lang.Class"), NoLookupLocation.FOR_NON_TRACKED_SCOPE) ?: return null
val arguments = listOf(TypeProjectionImpl(type))
val javaClassObjectType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, jlClass, arguments)
return KClassValue(javaClassObjectType)
}
private fun resolveFromJavaClassObjectType(javaType: JavaType): ConstantValue<*>? =
KClassValue.create(c.typeResolver.transformJavaType(javaType, TypeUsage.COMMON.toAttributes()))
override fun toString(): String {
return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderAnnotation(this)
@@ -16,11 +16,9 @@
package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
import org.jetbrains.kotlin.metadata.ProtoBuf
@@ -30,9 +28,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.compact
import java.util.*
@@ -94,8 +89,7 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
}
override fun visitClassLiteral(name: Name, value: ClassLiteralValue) {
arguments[name] = value.toClassValue() ?:
ErrorValue.create("Error value of annotation argument: $name: class ${value.classId.asSingleFqName()} not found")
arguments[name] = KClassValue(value)
}
override fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name) {
@@ -115,11 +109,7 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
}
override fun visitClassLiteral(value: ClassLiteralValue) {
elements.add(
value.toClassValue() ?: ErrorValue.create(
"Error array element value of annotation argument: $name: class ${value.classId.asSingleFqName()} not found"
)
)
elements.add(KClassValue(value))
}
override fun visitEnd() {
@@ -153,21 +143,6 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
}
}
private fun ClassLiteralValue.toClassValue(): KClassValue? =
module.findClassAcrossModuleDependencies(classId)?.let { classDescriptor ->
var currentType = classDescriptor.defaultType
for (i in 0 until arrayNestedness) {
val nextWrappedType =
(if (i == 0) module.builtIns.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(currentType) else null)
?: module.builtIns.getArrayType(Variance.INVARIANT, currentType)
currentType = nextWrappedType
}
val kClass = resolveClass(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.kClass.toSafe()))
val arguments = listOf(TypeProjectionImpl(currentType))
val type = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, arguments)
KClassValue(type)
}
private fun resolveClass(classId: ClassId): ClassDescriptor {
return module.findNonGenericClassAcrossDependencies(classId, notFoundClasses)
}
@@ -607,6 +607,11 @@ public abstract class KotlinBuiltIns {
return getBuiltInClassByName("Annotation");
}
@NotNull
public ClassDescriptor getKClass() {
return getBuiltInClassByFqName(FQ_NAMES.kClass.toSafe());
}
@NotNull
private ClassDescriptor getCollectionClassByName(@NotNull String simpleName) {
return getBuiltInClassByName(simpleName, packageFragments.invoke().collectionsPackageFragment);
@@ -458,7 +458,11 @@ internal class DescriptorRendererImpl(
return when (value) {
is ArrayValue -> value.value.joinToString(", ", "{", "}") { renderConstant(it) }
is AnnotationValue -> renderAnnotation(value.value).removePrefix("@")
is KClassValue -> renderType(value.value) + "::class"
is KClassValue -> {
var type = value.classId.asSingleFqName().asString()
repeat(value.arrayDimensions) { type = "kotlin.Array<$type>" }
"$type::class"
}
else -> value.toString()
}
}
@@ -17,15 +17,18 @@
package org.jetbrains.kotlin.resolve.constants
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
abstract class ConstantValue<out T>(open val value: T) {
abstract fun getType(module: ModuleDescriptor): KotlinType
@@ -154,13 +157,47 @@ class IntValue(value: Int) : IntegerValueConstant<Int>(value) {
override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitIntValue(this, data)
}
class KClassValue(private val type: KotlinType) : ConstantValue<KotlinType>(type) {
override fun getType(module: ModuleDescriptor): KotlinType = type
class KClassValue(value: ClassLiteralValue) : ConstantValue<ClassLiteralValue>(value) {
val classId: ClassId get() = value.classId
val arrayDimensions: Int get() = value.arrayNestedness
override val value: KotlinType
get() = type.arguments.single().type
constructor(classId: ClassId, arrayDimensions: Int) : this(ClassLiteralValue(classId, arrayDimensions))
override fun getType(module: ModuleDescriptor): KotlinType =
KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, module.builtIns.kClass, listOf(TypeProjectionImpl(getArgumentType(module))))
fun getArgumentType(module: ModuleDescriptor): KotlinType {
val descriptor = module.findClassAcrossModuleDependencies(classId)
?: return ErrorUtils.createErrorType("Unresolved type: $classId (arrayDimensions=$arrayDimensions)")
// If this value refers to a class named test.Foo.Bar where both Foo and Bar have generic type parameters,
// we're constructing a type `test.Foo<*>.Bar<*>` below
var type = descriptor.defaultType.replaceArgumentsWithStarProjections()
repeat(arrayDimensions) {
type = module.builtIns.getArrayType(Variance.INVARIANT, type)
}
return type
}
override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitKClassValue(this, data)
companion object {
fun create(argumentType: KotlinType): ConstantValue<*>? {
if (argumentType.isError) return null
var type = argumentType
var arrayDimensions = 0
while (KotlinBuiltIns.isArray(type)) {
type = type.arguments.single().type
arrayDimensions++
}
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val classId = descriptor.classId ?: return null
return KClassValue(classId, arrayDimensions)
}
}
}
class LongValue(value: Long) : IntegerValueConstant<Long>(value) {
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.metadata.ProtoBuf.Annotation
import org.jetbrains.kotlin.metadata.ProtoBuf.Annotation.Argument
import org.jetbrains.kotlin.metadata.ProtoBuf.Annotation.Argument.Value
@@ -31,9 +30,11 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
class AnnotationDeserializer(private val module: ModuleDescriptor, private val notFoundClasses: NotFoundClasses) {
private val builtIns: KotlinBuiltIns
@@ -79,7 +80,7 @@ class AnnotationDeserializer(private val module: ModuleDescriptor, private val n
StringValue(nameResolver.getString(value.stringValue))
}
Type.CLASS -> {
resolveClassLiteralValue(nameResolver.getClassId(value.classId), value.arrayDimensionCount)
KClassValue(nameResolver.getClassId(value.classId), value.arrayDimensionCount)
}
Type.ENUM -> {
EnumValue(nameResolver.getClassId(value.classId), nameResolver.getName(value.enumValueId))
@@ -128,19 +129,6 @@ class AnnotationDeserializer(private val module: ModuleDescriptor, private val n
private inline fun <T, R> T.letIf(predicate: Boolean, f: (T) -> R, g: (T) -> R): R =
if (predicate) f(this) else g(this)
private fun resolveClassLiteralValue(classId: ClassId, arrayDimensions: Int): ConstantValue<*> {
// If value refers to a class named test.Foo.Bar where both Foo and Bar have generic type parameters,
// we're constructing a type `KClass<test.Foo<*>.Bar<*>>` below
var type = resolveClass(classId).defaultType.replaceArgumentsWithStarProjections()
repeat(arrayDimensions) {
// We only support invariant projections and non-null array element types, see KT-26568
type = builtIns.getArrayType(Variance.INVARIANT, type)
}
val kClass = resolveClass(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.kClass.toSafe()))
return KClassValue(KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, listOf(TypeProjectionImpl(type))))
}
private fun resolveArrayElementType(value: Value, nameResolver: NameResolver): SimpleType =
with(builtIns) {
when (value.type) {
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.*
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
@@ -63,16 +63,19 @@ internal fun ClassDescriptor.toJavaClass(): Class<*>? {
else -> {
// If this is neither a Kotlin class nor a Java class, it's likely either a built-in or some fake class descriptor like the one
// that's created for java.io.Serializable in JvmBuiltInsSettings
val classId = JavaToKotlinClassMap.mapKotlinToJava(DescriptorUtils.getFqName(this)) ?: classId ?: return null
val packageName = classId.packageFqName.asString()
val className = classId.relativeClassName.asString()
// All pseudo-classes like kotlin.String.Companion must be accessible from the current class loader
loadClass(javaClass.safeClassLoader, packageName, className)
val classId = classId ?: return null
loadClass(javaClass.safeClassLoader, classId, 0)
}
}
}
internal fun loadClass(classLoader: ClassLoader, packageName: String, className: String): Class<*>? {
private fun loadClass(classLoader: ClassLoader, kotlinClassId: ClassId, arrayDimensions: Int = 0): Class<*>? {
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(kotlinClassId.asSingleFqName().toUnsafe()) ?: kotlinClassId
// All pseudo-classes like kotlin.String.Companion must be accessible from the current class loader
return loadClass(classLoader, javaClassId.packageFqName.asString(), javaClassId.relativeClassName.asString(), arrayDimensions)
}
private fun loadClass(classLoader: ClassLoader, packageName: String, className: String, arrayDimensions: Int): Class<*>? {
if (packageName == "kotlin") {
// See mapBuiltInType() in typeSignatureMapping.kt
when (className) {
@@ -88,7 +91,12 @@ internal fun loadClass(classLoader: ClassLoader, packageName: String, className:
}
}
return classLoader.tryLoadClass("$packageName.${className.replace('.', '$')}")
var fqName = "$packageName.${className.replace('.', '$')}"
repeat(arrayDimensions) {
fqName = "[$fqName"
}
return classLoader.tryLoadClass(fqName)
}
internal fun Visibility.toKVisibility(): KVisibility? =
@@ -128,12 +136,15 @@ private fun ConstantValue<*>.toRuntimeValue(classLoader: ClassLoader): Any? = wh
is ArrayValue -> value.map { it.toRuntimeValue(classLoader) }.toTypedArray()
is EnumValue -> {
val (enumClassId, entryName) = value
loadClass(classLoader, enumClassId.packageFqName.asString(), enumClassId.relativeClassName.asString())?.let { enumClass ->
loadClass(classLoader, enumClassId)?.let { enumClass ->
@Suppress("UNCHECKED_CAST")
Util.getEnumConstantByName(enumClass as Class<out Enum<*>>, entryName.asString())
}
}
is KClassValue -> (value.constructor.declarationDescriptor as? ClassDescriptor)?.toJavaClass()
is KClassValue -> {
val (classId, arrayDimensions) = value
loadClass(classLoader, classId, arrayDimensions)
}
is ErrorValue, is NullValue -> null
else -> value // Primitives and strings
}
@@ -2,4 +2,4 @@ import kotlin.reflect.KClass
annotation class Ann(val value: KClass<*>)
@Ann(Array<<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: String123">String123</error>>::class) class A
@Ann(<error descr="[ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL] An annotation argument must be a class literal (T::class)">Array<<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: String123">String123</error>>::class</error>) class A
@@ -27,18 +27,19 @@ import kotlinx.kapt.KaptIgnored
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.base.plus
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.javac.reportKaptError
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.pairedListToMap
import org.jetbrains.kotlin.kapt3.base.util.TopLevelJava9Aware
import org.jetbrains.kotlin.kapt3.base.plus
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.*
import org.jetbrains.kotlin.kapt3.base.util.TopLevelJava9Aware
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAMETER_TYPE
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.FqName
@@ -1049,7 +1050,7 @@ class ClassFileToSourceStubConverter(
&& asm.size == desc.value.size
&& asm.zip(desc.value).all { (eAsm, eDesc) -> checkIfAnnotationValueMatches(eAsm, eDesc) }
}
is Type -> desc is KClassValue && typeMapper.mapType(desc.value) == asm
is Type -> desc is KClassValue && typeMapper.mapType(desc.getArgumentType(kaptContext.generationState.module)) == asm
is AnnotationNode -> {
val annotationDescriptor = (desc as? AnnotationValue)?.value ?: return false
if (typeMapper.mapType(annotationDescriptor.type).descriptor != asm.desc) return false