From a7f403720689515d10504e2aa845c8e4b2872f96 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 2 Aug 2016 13:28:49 -0700 Subject: [PATCH] Reflection: change order of arguments of inner generic type As in KClassifier.createType and everywhere in the compiler, specify arguments for the innermost type first. This is more convenient to use because generally the construction/introspection of such type starts from the innermost class anyway (i.e. something like generateSequence can be used, without the need to call .reverse() in the end) --- .../mapping/types/innerGenericTypeArgument.kt | 28 ++++++++++++++++ .../reflection/types/innerGenericArguments.kt | 32 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 +++++++ core/builtins/src/kotlin/reflect/KType.kt | 4 +++ .../reflect/ReflectJavaClassifierType.kt | 4 +-- .../structure/reflect/reflectClassUtil.kt | 15 +++++++++ .../kotlin/reflect/jvm/internal/KTypeImpl.kt | 10 ++---- 7 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt create mode 100644 compiler/testData/codegen/box/reflection/types/innerGenericArguments.kt 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()