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);
}
}