From 153e837a8491f08ecba9d8b58fe4bdc2077e9c95 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 12 Jul 2016 16:26:52 +0300 Subject: [PATCH] Reflection: add KType.jvmErasure #KT-8998 In Progress --- .../box/reflection/types/jvmErasureOfClass.kt | 20 +++++++++ .../types/jvmErasureOfTypeParameter.kt | 45 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 +++++ .../src/kotlin/reflect/jvm/KTypesJvm.kt | 45 +++++++++++++++++++ .../kotlin/reflect/jvm/internal/KTypeImpl.kt | 23 ++++------ 5 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt create mode 100644 compiler/testData/codegen/box/reflection/types/jvmErasureOfTypeParameter.kt create mode 100644 core/reflection.jvm/src/kotlin/reflect/jvm/KTypesJvm.kt diff --git a/compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt b/compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt new file mode 100644 index 00000000000..a83263f85aa --- /dev/null +++ b/compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt @@ -0,0 +1,20 @@ +// WITH_REFLECT + +import kotlin.reflect.jvm.jvmErasure +import kotlin.test.assertEquals + +fun string(): String = null!! +fun array(): Array = null!! + +fun collection(): Collection = null!! +fun mutableCollection(): MutableCollection = null!! + +fun box(): String { + assertEquals(String::class, ::string.returnType.jvmErasure) + assertEquals(Array::class, ::array.returnType.jvmErasure) + + assertEquals(Collection::class, ::collection.returnType.jvmErasure) + assertEquals(MutableCollection::class, ::mutableCollection.returnType.jvmErasure) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/types/jvmErasureOfTypeParameter.kt b/compiler/testData/codegen/box/reflection/types/jvmErasureOfTypeParameter.kt new file mode 100644 index 00000000000..3a1c8b0d439 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/types/jvmErasureOfTypeParameter.kt @@ -0,0 +1,45 @@ +// WITH_REFLECT + +import kotlin.reflect.jvm.jvmErasure +import kotlin.reflect.KClass +import kotlin.test.assertEquals + +open class O + +class A { + fun simple(): T = null!! + fun string(): T = null!! + fun nullableString(): T = null!! + fun otherTypeParameter(): T = null!! + fun > otherTypeParameterWithBound(): T = null!! + + fun twoInterfaces1(): T where T : Comparable<*> = null!! + fun > twoInterfaces2(): T where T : Cloneable = null!! + fun interfaceAndClass1(): T where T : O = null!! + fun interfaceAndClass2(): T where T : Cloneable = null!! + + fun arrayOfAny(): Array = null!! + fun arrayOfNumber(): Array = null!! + fun arrayOfArrayOfCloneable(): Array> where T : Cloneable, T : Comparable<*> = null!! +} + +fun get(name: String): KClass<*> = A::class.members.single { it.name == name }.returnType.jvmErasure + +fun box(): String { + assertEquals(Any::class, get("simple")) + assertEquals(String::class, get("string")) + assertEquals(String::class, get("nullableString")) + assertEquals(Any::class, get("otherTypeParameter")) + assertEquals(List::class, get("otherTypeParameterWithBound")) + + assertEquals(Cloneable::class, get("twoInterfaces1")) + assertEquals(Comparable::class, get("twoInterfaces2")) + assertEquals(O::class, get("interfaceAndClass1")) + assertEquals(O::class, get("interfaceAndClass2")) + + assertEquals(Array::class, get("arrayOfAny")) + assertEquals(Array::class, get("arrayOfNumber")) + assertEquals(Array>::class, get("arrayOfArrayOfCloneable")) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 946ea48b6be..d15f3d594cd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13008,6 +13008,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("jvmErasureOfClass.kt") + public void testJvmErasureOfClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/jvmErasureOfClass.kt"); + doTest(fileName); + } + + @TestMetadata("jvmErasureOfTypeParameter.kt") + public void testJvmErasureOfTypeParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/jvmErasureOfTypeParameter.kt"); + doTest(fileName); + } + @TestMetadata("platformTypeClassifier.kt") public void testPlatformTypeClassifier() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/platformTypeClassifier.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/KTypesJvm.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/KTypesJvm.kt new file mode 100644 index 00000000000..163ab3d6c52 --- /dev/null +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/KTypesJvm.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("KTypesJvm") +package kotlin.reflect.jvm + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS +import org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE +import kotlin.reflect.* +import kotlin.reflect.jvm.internal.KTypeImpl + +/** + * Returns the [KClass] instance representing the runtime class to which this type is erased to on JVM. + */ +val KType.jvmErasure: KClass<*> + get() = classifier?.jvmErasure ?: throw KotlinReflectionInternalError("Cannot calculate JVM erasure for type: $this") + +internal val KClassifier.jvmErasure: KClass<*> + get() = when (this) { + is KClass<*> -> this + is KTypeParameter -> { + // See getRepresentativeUpperBound in typeSignatureMapping.kt + val bounds = upperBounds + val representativeBound = bounds.firstOrNull { + val classDescriptor = (it as KTypeImpl).type.constructor.declarationDescriptor as? ClassDescriptor + classDescriptor != null && classDescriptor.kind != INTERFACE && classDescriptor.kind != ANNOTATION_CLASS + } ?: bounds.firstOrNull() + representativeBound?.jvmErasure ?: Any::class + } + else -> throw KotlinReflectionInternalError("Cannot calculate JVM erasure for type: $this") + } 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 059c4d0ed28..1dc8dd31f45 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KTypeImpl.kt @@ -29,7 +29,11 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.lang.reflect.WildcardType import kotlin.LazyThreadSafetyMode.PUBLICATION -import kotlin.reflect.* +import kotlin.reflect.KClassifier +import kotlin.reflect.KType +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KotlinReflectionInternalError +import kotlin.reflect.jvm.jvmErasure internal class KTypeImpl( val type: KotlinType, @@ -48,19 +52,10 @@ internal class KTypeImpl( if (jClass.isArray) { // There may be no argument if it's a primitive array (such as IntArray) val argument = type.arguments.singleOrNull()?.type ?: return KClassImpl(jClass) - - val elementClassifier = convert(argument) - val elementType = when (elementClassifier) { - is KClass<*> -> elementClassifier - is KTypeParameter -> { - // For arrays of type parameters (`Array`) we return the KClass representing `Array` - // since there's no other sensible option - // TODO: return `Array` - Any::class - } - else -> TODO("Arrays of type alias classifiers are not yet supported") - } - return KClassImpl(elementType.java.createArrayType()) + val elementClassifier = + convert(argument) + ?: throw KotlinReflectionInternalError("Cannot determine classifier for array element type: $this") + return KClassImpl(elementClassifier.jvmErasure.java.createArrayType()) } if (!TypeUtils.isNullableType(type)) {