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:
+5
-19
@@ -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)
|
||||
|
||||
+2
-27
@@ -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) {
|
||||
|
||||
+5
-17
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user