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)
This commit is contained in:
Alexander Udalov
2016-08-02 13:28:49 -07:00
parent 89d69bc7eb
commit a7f4037206
7 changed files with 95 additions and 10 deletions
@@ -0,0 +1,28 @@
// WITH_REFLECT
import kotlin.reflect.jvm.javaType
import kotlin.test.assertEquals
class Outer<A, B> {
inner class Inner<C, D> {
inner class Innermost<E, F>
}
}
fun foo(): Outer<Int, Number>.Inner<String, Float>.Innermost<Any, Any?> = 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"
}
@@ -0,0 +1,32 @@
// WITH_REFLECT
import kotlin.test.*
class Outer<A, B> {
inner class Inner<C, D> {
inner class Innermost<E, F>
}
}
fun foo(): Outer<Int, Number>.Inner<String, Float>.Innermost<Any, Any?> = 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"
}
@@ -12464,6 +12464,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); 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") @TestMetadata("memberFunctions.kt")
public void testMemberFunctions() throws Exception { public void testMemberFunctions() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt");
@@ -13173,6 +13179,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); 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") @TestMetadata("jvmErasureOfClass.kt")
public void testJvmErasureOfClass() throws Exception { public void testJvmErasureOfClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt");
@@ -32,6 +32,10 @@ public interface KType {
/** /**
* Type arguments passed for the parameters of the classifier in this type. * Type arguments passed for the parameters of the classifier in this type.
* For example, in the type `Array<out Number>` the only type argument is `out Number`. * For example, in the type `Array<out Number>` 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<A, B>.Inner<C, D>` the returned list is `[C, D, A, B]`.
*/ */
public val arguments: List<KTypeProjection> public val arguments: List<KTypeProjection>
@@ -47,9 +47,7 @@ class ReflectJavaClassifierType(public override val reflectType: Type) : Reflect
get() = with(reflectType) { this is Class<*> && getTypeParameters().isNotEmpty() } get() = with(reflectType) { this is Class<*> && getTypeParameters().isNotEmpty() }
override val typeArguments: List<JavaType> override val typeArguments: List<JavaType>
get() = generateSequence({ reflectType as? ParameterizedType }, { it.ownerType as? ParameterizedType }).flatMap { get() = reflectType.parameterizedTypeArguments.map(ReflectJavaType.Factory::create)
it.actualTypeArguments.asSequence().map { ReflectJavaType.create(it) }
}.toList()
override val annotations: Collection<JavaAnnotation> override val annotations: Collection<JavaAnnotation>
get() { get() {
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import java.lang.reflect.Array import java.lang.reflect.Array
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
val Class<*>.safeClassLoader: ClassLoader val Class<*>.safeClassLoader: ClassLoader
get() = classLoader ?: ClassLoader.getSystemClassLoader() get() = classLoader ?: ClassLoader.getSystemClassLoader()
@@ -74,3 +76,16 @@ val Class<*>.desc: String
fun Class<*>.createArrayType(): Class<*> = fun Class<*>.createArrayType(): Class<*> =
Array.newInstance(this, 0).javaClass 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<A, B>.Inner<C, D>` the result would be `[C, D, A, B]`.
*/
val Type.parameterizedTypeArguments: List<Type>
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()
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType 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.load.java.structure.reflect.primitiveByWrapper
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
@@ -75,12 +76,7 @@ internal class KTypeImpl(
val typeArguments = type.arguments val typeArguments = type.arguments
if (typeArguments.isEmpty()) return emptyList() if (typeArguments.isEmpty()) return emptyList()
// Lazy because it's not needed to compute javaType right away, only inside the lazy value for each argument, val parameterizedTypeArguments by lazy(PUBLICATION) { javaType.parameterizedTypeArguments }
// 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
}
return typeArguments.mapIndexed { i, typeProjection -> return typeArguments.mapIndexed { i, typeProjection ->
if (typeProjection.isStarProjection) { if (typeProjection.isStarProjection) {
@@ -100,7 +96,7 @@ internal class KTypeImpl(
javaType.genericComponentType javaType.genericComponentType
} }
is ParameterizedType -> { is ParameterizedType -> {
val argument = javaTypeArguments[i] val argument = parameterizedTypeArguments[i]
// In "Foo<out Bar>", the JVM type of the first type argument should be "Bar", not "? extends Bar" // In "Foo<out Bar>", the JVM type of the first type argument should be "Bar", not "? extends Bar"
if (argument !is WildcardType) argument if (argument !is WildcardType) argument
else argument.lowerBounds.firstOrNull() ?: argument.upperBounds.first() else argument.lowerBounds.firstOrNull() ?: argument.upperBounds.first()