diff --git a/compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt b/compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt new file mode 100644 index 00000000000..1301713379c --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt @@ -0,0 +1,28 @@ +// WITH_REFLECT + +import kotlin.reflect.jvm.javaType +import kotlin.test.assertEquals + +class Outer { + inner class Inner { + inner class Innermost + } +} + +fun foo(): Outer.Inner.Innermost = null!! + +fun box(): String { + assertEquals( + listOf( + Any::class.java, + Any::class.java, + String::class.java, + Float::class.javaObjectType, + Int::class.javaObjectType, + Number::class.java + ), + ::foo.returnType.arguments.map { it.type!!.javaType } + ) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/types/innerGenericArguments.kt b/compiler/testData/codegen/box/reflection/types/innerGenericArguments.kt new file mode 100644 index 00000000000..a7ac61165da --- /dev/null +++ b/compiler/testData/codegen/box/reflection/types/innerGenericArguments.kt @@ -0,0 +1,32 @@ +// WITH_REFLECT + +import kotlin.test.* + +class Outer { + inner class Inner { + inner class Innermost + } +} + +fun foo(): Outer.Inner.Innermost = null!! + +fun box(): String { + val types = ::foo.returnType.arguments.map { it.type!! } + + assertEquals( + listOf( + Any::class, + Any::class, + String::class, + Float::class, + Int::class, + Number::class + ), + types.map { it.classifier } + ) + + assertFalse(types[0].isMarkedNullable) + assertTrue(types[1].isMarkedNullable) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index ff3cc0b8826..915acee0701 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12464,6 +12464,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("innerGenericTypeArgument.kt") + public void testInnerGenericTypeArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt"); + doTest(fileName); + } + @TestMetadata("memberFunctions.kt") public void testMemberFunctions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt"); @@ -13173,6 +13179,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("innerGenericArguments.kt") + public void testInnerGenericArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/innerGenericArguments.kt"); + doTest(fileName); + } + @TestMetadata("jvmErasureOfClass.kt") public void testJvmErasureOfClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt"); diff --git a/core/builtins/src/kotlin/reflect/KType.kt b/core/builtins/src/kotlin/reflect/KType.kt index 98cb9c07f80..9643d3405fc 100644 --- a/core/builtins/src/kotlin/reflect/KType.kt +++ b/core/builtins/src/kotlin/reflect/KType.kt @@ -32,6 +32,10 @@ public interface KType { /** * Type arguments passed for the parameters of the classifier in this type. * For example, in the type `Array` the only type argument is `out Number`. + * + * In case this type is based on an inner class, the returned list contains the type arguments provided for the innermost class first, + * then its outer class, and so on. + * For example, in the type `Outer.Inner` the returned list is `[C, D, A, B]`. */ public val arguments: List diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaClassifierType.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaClassifierType.kt index 18ce6853700..eb2d0508678 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaClassifierType.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaClassifierType.kt @@ -47,9 +47,7 @@ class ReflectJavaClassifierType(public override val reflectType: Type) : Reflect get() = with(reflectType) { this is Class<*> && getTypeParameters().isNotEmpty() } override val typeArguments: List - get() = generateSequence({ reflectType as? ParameterizedType }, { it.ownerType as? ParameterizedType }).flatMap { - it.actualTypeArguments.asSequence().map { ReflectJavaType.create(it) } - }.toList() + get() = reflectType.parameterizedTypeArguments.map(ReflectJavaType.Factory::create) override val annotations: Collection get() { diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/reflectClassUtil.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/reflectClassUtil.kt index 344e7e3e50e..b7512b13cb7 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/reflectClassUtil.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/reflectClassUtil.kt @@ -20,6 +20,8 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import java.lang.reflect.Array +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type val Class<*>.safeClassLoader: ClassLoader get() = classLoader ?: ClassLoader.getSystemClassLoader() @@ -74,3 +76,16 @@ val Class<*>.desc: String fun Class<*>.createArrayType(): Class<*> = Array.newInstance(this, 0).javaClass + +/** + * @return all arguments of a parameterized type, including those of outer classes in case this type represents an inner generic. + * The returned list starts with the arguments to the innermost class, then continues with those of its outer class, and so on. + * For example, for the type `Outer.Inner` the result would be `[C, D, A, B]`. + */ +val Type.parameterizedTypeArguments: List + get() { + if (this !is ParameterizedType) return emptyList() + if (ownerType == null) return actualTypeArguments.toList() + + return generateSequence(this) { it.ownerType as? ParameterizedType }.flatMap { it.actualTypeArguments.asSequence() }.toList() + } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt index 1dc8dd31f45..00d668fee5d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType +import org.jetbrains.kotlin.load.java.structure.reflect.parameterizedTypeArguments import org.jetbrains.kotlin.load.java.structure.reflect.primitiveByWrapper import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -75,12 +76,7 @@ internal class KTypeImpl( val typeArguments = type.arguments if (typeArguments.isEmpty()) return emptyList() - // Lazy because it's not needed to compute javaType right away, only inside the lazy value for each argument, - // and also because sometimes (e.g. in case of star projections), this won't be needed at all. - // Note that this instance is created before the loop because ParameterizedType#actualTypeArguments clones the array - val javaTypeArguments by lazy(PUBLICATION) { - (javaType as ParameterizedType).actualTypeArguments - } + val parameterizedTypeArguments by lazy(PUBLICATION) { javaType.parameterizedTypeArguments } return typeArguments.mapIndexed { i, typeProjection -> if (typeProjection.isStarProjection) { @@ -100,7 +96,7 @@ internal class KTypeImpl( javaType.genericComponentType } is ParameterizedType -> { - val argument = javaTypeArguments[i] + val argument = parameterizedTypeArguments[i] // In "Foo", the JVM type of the first type argument should be "Bar", not "? extends Bar" if (argument !is WildcardType) argument else argument.lowerBounds.firstOrNull() ?: argument.upperBounds.first()