diff --git a/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt b/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt index 909083bc91b..1bd4f9a2793 100644 --- a/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt +++ b/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt @@ -5,6 +5,7 @@ package test import kotlin.reflect.KClass import kotlin.test.assertEquals +import kotlin.test.assertTrue annotation class Nested(val value: String) @@ -66,14 +67,33 @@ fun f(): @Anno( ) Unit {} fun box(): String { + val anno = ::f.returnType.annotations.single() as Anno assertEquals( - "[@test.Anno(b=1, c=x, d=3.14, f=-2.72, i=42424242, j=239239239239239, s=42, z=true, " + + "@test.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, k2=class [I, e=EXPRESSION, a=@test.Nested(value=1), stra=[lmao], " + "ka=[class java.lang.Double, class kotlin.Unit, class [J, class [Ljava.lang.String;], " + - "ea=[TYPEALIAS, FIELD], aa=[@test.Nested(value=2), @test.Nested(value=3)])]", - ::f.returnType.annotations.toString() + "ea=[TYPEALIAS, FIELD], aa=[@test.Nested(value=2), @test.Nested(value=3)])", + anno.toString() ) + // Check that array instances have correct types at runtime and not just Object[]. + assertTrue(anno.ba is ByteArray) + assertTrue(anno.ca is CharArray) + assertTrue(anno.da is DoubleArray) + assertTrue(anno.fa is FloatArray) + assertTrue(anno.ia is IntArray) + assertTrue(anno.ja is LongArray) + assertTrue(anno.sa is ShortArray) + assertTrue(anno.za is BooleanArray) + val stra = anno.stra + assertTrue(stra is Array<*> && stra.isArrayOf()) + val ka = anno.ka + assertTrue(ka is Array<*> && ka.isArrayOf>()) + val ea = anno.ea + assertTrue(ea is Array<*> && ea.isArrayOf()) + val aa = anno.aa + assertTrue(aa is Array<*> && aa.isArrayOf()) + return "OK" } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/constantValues.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/constantValues.kt index 9af294c7c29..10849a1e95e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/constantValues.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/constants/constantValues.kt @@ -55,7 +55,7 @@ class AnnotationValue(value: AnnotationDescriptor) : ConstantValue accept(visitor: AnnotationArgumentVisitor, data: D) = visitor.visitAnnotationValue(this, data) } -class ArrayValue( +open class ArrayValue( value: List>, private val computeType: (ModuleDescriptor) -> KotlinType ) : ConstantValue>>(value) { diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt index 298da63c258..862e4468fff 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt @@ -30,7 +30,6 @@ 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.resolve.descriptorUtil.classId import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType @@ -84,7 +83,7 @@ class AnnotationDeserializer(private val module: ModuleDescriptor, private val n Type.CLASS -> KClassValue(nameResolver.getClassId(value.classId), value.arrayDimensionCount) Type.ENUM -> EnumValue(nameResolver.getClassId(value.classId), nameResolver.getName(value.enumValueId)) Type.ANNOTATION -> AnnotationValue(deserializeAnnotation(value.annotation, nameResolver)) - Type.ARRAY -> ConstantValueFactory.createArrayValue( + Type.ARRAY -> DeserializedArrayValue( value.arrayElementList.map { resolveValue(builtIns.anyType, it, nameResolver) }, expectedType ) diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedArrayValue.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedArrayValue.kt new file mode 100644 index 00000000000..ceb2227948a --- /dev/null +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedArrayValue.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.serialization.deserialization + +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.types.KotlinType + +class DeserializedArrayValue(value: List>, val type: KotlinType) : ArrayValue(value, { type }) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index cf7dc541715..fcae9bb6ea6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -16,6 +16,8 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated @@ -42,6 +44,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext +import org.jetbrains.kotlin.serialization.deserialization.DeserializedArrayValue import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer import java.lang.reflect.Type import kotlin.jvm.internal.FunctionReference @@ -138,7 +141,7 @@ private fun AnnotationDescriptor.toAnnotationInstance(): Annotation? { // TODO: consider throwing exceptions such as AnnotationFormatError/AnnotationTypeMismatchException if a value of unexpected type is found private fun ConstantValue<*>.toRuntimeValue(classLoader: ClassLoader): Any? = when (this) { is AnnotationValue -> value.toAnnotationInstance() - is ArrayValue -> value.map { it.toRuntimeValue(classLoader) }.toTypedArray() + is ArrayValue -> arrayToRuntimeValue(classLoader) is EnumValue -> { val (enumClassId, entryName) = value loadClass(classLoader, enumClassId)?.let { enumClass -> @@ -158,6 +161,39 @@ private fun ConstantValue<*>.toRuntimeValue(classLoader: ClassLoader): Any? = wh else -> value // Primitives and strings } +private fun ArrayValue.arrayToRuntimeValue(classLoader: ClassLoader): Any? { + val type = (this as? DeserializedArrayValue)?.type ?: return null + val values = value.map { it.toRuntimeValue(classLoader) } + + return when (KotlinBuiltIns.getPrimitiveArrayElementType(type)) { + PrimitiveType.BOOLEAN -> BooleanArray(value.size) { values[it] as Boolean } + PrimitiveType.CHAR -> CharArray(value.size) { values[it] as Char } + PrimitiveType.BYTE -> ByteArray(value.size) { values[it] as Byte } + PrimitiveType.SHORT -> ShortArray(value.size) { values[it] as Short } + PrimitiveType.INT -> IntArray(value.size) { values[it] as Int } + PrimitiveType.FLOAT -> FloatArray(value.size) { values[it] as Float } + PrimitiveType.LONG -> LongArray(value.size) { values[it] as Long } + PrimitiveType.DOUBLE -> DoubleArray(value.size) { values[it] as Double } + null -> { + check(KotlinBuiltIns.isArray(type)) { "Not an array type: $type" } + val argType = type.arguments.single().type + val classifier = argType.constructor.declarationDescriptor as? ClassDescriptor ?: error("Not a class type: $argType") + when { + KotlinBuiltIns.isString(argType) -> Array(value.size) { values[it] as String } + KotlinBuiltIns.isKClass(classifier) -> Array(value.size) { values[it] as Class<*> } + else -> { + val argClass = classifier.classId?.let { loadClass(classLoader, it) } ?: return null + + @Suppress("UNCHECKED_CAST") + val array = java.lang.reflect.Array.newInstance(argClass, value.size) as Array + repeat(values.size) { array[it] = values[it] } + array + } + } + } + } +} + // TODO: wrap other exceptions internal inline fun reflectionCall(block: () -> R): R = try {