Reflection: add KClass.{supertypes,superclasses,allSupertypes,allSuperclasses}
Some things are not implemented yet for allSupertypes, such as KType->Type mapping of types and generic substitution of built-in types
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// WITH_REFLECT
|
||||
// FULL_JDK
|
||||
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.TypeVariable
|
||||
import kotlin.reflect.jvm.javaType
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
open class Klass
|
||||
interface Interface<T, U>
|
||||
interface Interface2
|
||||
|
||||
class A<Z> : Interface<String, Z>, Klass(), Interface2
|
||||
|
||||
fun box(): String {
|
||||
val (i, k, i2) = A::class.supertypes.map { it.javaType }
|
||||
|
||||
i as? ParameterizedType ?: fail("Not a parameterized type: $i")
|
||||
assertEquals(Interface::class.java, i.rawType)
|
||||
val args = i.actualTypeArguments
|
||||
assertEquals(String::class.java, args[0], "Not String: ${args[0]}")
|
||||
assertTrue(args[1].let { it is TypeVariable<*> && it.name == "Z" && it.genericDeclaration == A::class.java }, "Not Z: ${args[1]}")
|
||||
|
||||
assertEquals(Klass::class.java, k)
|
||||
|
||||
assertEquals(Interface2::class.java, i2)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
inline fun <reified T : Any> check(vararg callables: KCallable<*>) {
|
||||
val types = callables.map { it.returnType }
|
||||
assertEquals(types, T::class.supertypes)
|
||||
assertEquals(types.map { it.classifier as KClass<*> }, T::class.superclasses)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> checkAll(vararg callables: KCallable<*>) {
|
||||
val types = callables.map { it.returnType }
|
||||
// Calling toSet because the order of returned types/classes is not specified
|
||||
assertEquals(types.toSet(), T::class.allSupertypes.toSet())
|
||||
assertEquals(types.map { it.classifier as KClass<*> }.toSet(), T::class.allSuperclasses.toSet())
|
||||
}
|
||||
|
||||
fun comparableOfString(): Comparable<String> = null!!
|
||||
fun charSequence(): CharSequence = null!!
|
||||
fun serializable(): Serializable = null!!
|
||||
fun any(): Any = null!!
|
||||
fun number(): Number = null!!
|
||||
fun comparableOfInt(): Comparable<Int> = null!!
|
||||
fun cloneable(): Cloneable = null!!
|
||||
|
||||
fun box(): String {
|
||||
check<Any>()
|
||||
checkAll<Any>()
|
||||
|
||||
check<String>(::comparableOfString, ::charSequence, ::serializable)
|
||||
checkAll<String>(::comparableOfString, ::charSequence, ::serializable, ::any)
|
||||
|
||||
check<Int>(::number, ::comparableOfInt, ::serializable)
|
||||
checkAll<Int>(::number, ::comparableOfInt, ::serializable, ::any)
|
||||
|
||||
check<Array<Any>>(::cloneable, ::serializable)
|
||||
checkAll<Array<Any>>(::cloneable, ::serializable, ::any)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.allSupertypes
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
interface A<A1, A2>
|
||||
interface B<B1, B2> : A<B2, B1>
|
||||
interface C<C1> : B<C1, String>
|
||||
interface D : C<Int>
|
||||
|
||||
interface StringList : List<String>
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(
|
||||
listOf(String::class, Int::class),
|
||||
D::class.allSupertypes.single { it.classifier == A::class }.arguments.map { it.type!!.classifier }
|
||||
)
|
||||
|
||||
val collectionType = StringList::class.allSupertypes.single { it.classifier == Collection::class }
|
||||
val arg = collectionType.arguments.single().type!!
|
||||
// TODO: this does not work currently because for some reason two different instances of TypeParameterDescriptor are created for List
|
||||
// assertEquals(String::class, arg.classifier)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
open class Simple
|
||||
class OneClass : Simple()
|
||||
|
||||
interface Interface
|
||||
interface Interface2
|
||||
class ClassAndTwoInterfaces : Interface, Simple(), Interface2
|
||||
|
||||
fun any(): Any = null!!
|
||||
fun simple(): Simple = null!!
|
||||
fun interface_(): Interface = null!!
|
||||
fun interface2(): Interface2 = null!!
|
||||
|
||||
fun box(): String {
|
||||
with(Simple::class) {
|
||||
assertEquals(listOf(::any.returnType), supertypes)
|
||||
assertEquals(listOf(Any::class), superclasses)
|
||||
// Calling toSet because the order of returned types/classes is not specified
|
||||
assertEquals(setOf(::any.returnType), allSupertypes.toSet())
|
||||
assertEquals(setOf(Any::class), allSuperclasses.toSet())
|
||||
}
|
||||
|
||||
with (OneClass::class) {
|
||||
assertEquals(listOf(::simple.returnType), supertypes)
|
||||
assertEquals(listOf(Simple::class), superclasses)
|
||||
assertEquals(setOf(::simple.returnType, ::any.returnType), allSupertypes.toSet())
|
||||
assertEquals(setOf(Simple::class, Any::class), allSuperclasses.toSet())
|
||||
}
|
||||
|
||||
with (Interface::class) {
|
||||
assertEquals(listOf(::any.returnType), supertypes)
|
||||
assertEquals(listOf(Any::class), superclasses)
|
||||
assertEquals(setOf(::any.returnType), allSupertypes.toSet())
|
||||
assertEquals(setOf(Any::class), allSuperclasses.toSet())
|
||||
}
|
||||
|
||||
with (ClassAndTwoInterfaces::class) {
|
||||
assertEquals(listOf(::interface_.returnType, ::simple.returnType, ::interface2.returnType), supertypes)
|
||||
assertEquals(listOf(Interface::class, Simple::class, Interface2::class), superclasses)
|
||||
assertEquals(setOf(::interface_.returnType, ::simple.returnType, ::interface2.returnType, ::any.returnType), allSupertypes.toSet())
|
||||
assertEquals(setOf(Interface::class, Simple::class, Interface2::class, Any::class), allSuperclasses.toSet())
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -12473,6 +12473,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("supertypes.kt")
|
||||
public void testSupertypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/supertypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunctions.kt")
|
||||
public void testTopLevelFunctions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/topLevelFunctions.kt");
|
||||
@@ -12967,6 +12973,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/reflection/supertypes")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Supertypes extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInSupertypes() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/supertypes"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("builtInClassSupertypes.kt")
|
||||
public void testBuiltInClassSupertypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/builtInClassSupertypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericSubstitution.kt")
|
||||
public void testGenericSubstitution() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/genericSubstitution.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleSupertypes.kt")
|
||||
public void testSimpleSupertypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/simpleSupertypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/reflection/typeParameters")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -63,6 +63,11 @@ public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KCl
|
||||
*/
|
||||
public val typeParameters: List<KTypeParameter>
|
||||
|
||||
/**
|
||||
* The list of immediate supertypes of this class, in the order they are listed in the source code.
|
||||
*/
|
||||
public val supertypes: List<KType>
|
||||
|
||||
/**
|
||||
* Returns `true` if [other] is a [KClass] instance representing the same class on a given platform.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
package kotlin.reflect
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import kotlin.reflect.jvm.internal.KClassImpl
|
||||
import kotlin.reflect.jvm.internal.KFunctionImpl
|
||||
import kotlin.reflect.jvm.internal.KTypeImpl
|
||||
@@ -170,3 +173,53 @@ val <T : Any> KClass<T>.declaredMemberExtensionProperties: Collection<KProperty2
|
||||
.getMembers(memberScope, declaredOnly = true, nonExtensions = false, extensions = true)
|
||||
.filterIsInstance<KProperty2<T, *, *>>()
|
||||
.toList()
|
||||
|
||||
|
||||
/**
|
||||
* Immediate superclasses of this class, in the order they are listed in the source code.
|
||||
* Includes superclasses and superinterfaces of the class, but does not include the class itself.
|
||||
*/
|
||||
val KClass<*>.superclasses: List<KClass<*>>
|
||||
get() = supertypes.mapNotNull { it.classifier as? KClass<*> }
|
||||
|
||||
/**
|
||||
* All supertypes of this class, including indirect ones, in no particular order.
|
||||
* There is not more than one type in the returned collection that has any given classifier.
|
||||
*/
|
||||
val KClass<*>.allSupertypes: Collection<KType>
|
||||
get() = DFS.dfs(
|
||||
supertypes,
|
||||
DFS.Neighbors { current ->
|
||||
val klass = current.classifier as? KClass<*> ?: throw KotlinReflectionInternalError("Supertype not a class: $current")
|
||||
val supertypes = klass.supertypes
|
||||
val typeArguments = current.arguments
|
||||
if (typeArguments.isEmpty()) supertypes
|
||||
else TypeSubstitutor.create((current as KTypeImpl).type).let { substitutor ->
|
||||
supertypes.map { supertype ->
|
||||
val substituted = substitutor.substitute((supertype as KTypeImpl).type, Variance.INVARIANT)
|
||||
?: throw KotlinReflectionInternalError("Type substitution failed: $supertype ($current)")
|
||||
KTypeImpl(substituted) {
|
||||
// TODO
|
||||
TODO("Java type for supertype")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
DFS.VisitedWithSet(),
|
||||
object : DFS.NodeHandlerWithListResult<KType, KType>() {
|
||||
override fun beforeChildren(current: KType): Boolean {
|
||||
result.add(current)
|
||||
return true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* All superclasses of this class, including indirect ones, in no particular order.
|
||||
* Includes superclasses and superinterfaces of the class, but does not include the class itself.
|
||||
* The returned collection does not contain more than one instance of any given class.
|
||||
*/
|
||||
val KClass<*>.allSuperclasses: Collection<KClass<*>>
|
||||
get() = allSupertypes.map { supertype ->
|
||||
supertype.classifier as? KClass<*> ?: throw KotlinReflectionInternalError("Supertype not a class: $supertype")
|
||||
}
|
||||
|
||||
@@ -139,6 +139,26 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
|
||||
override val typeParameters: List<KTypeParameter>
|
||||
get() = descriptor.declaredTypeParameters.map(::KTypeParameterImpl)
|
||||
|
||||
override val supertypes: List<KType>
|
||||
get() = descriptor.typeConstructor.supertypes.map { kotlinType ->
|
||||
KTypeImpl(kotlinType) {
|
||||
val superClass = kotlinType.constructor.declarationDescriptor
|
||||
if (superClass !is ClassDescriptor) throw KotlinReflectionInternalError("Supertype not a class: $superClass")
|
||||
|
||||
val superJavaClass = superClass.toJavaClass()
|
||||
?: throw KotlinReflectionInternalError("Unsupported superclass of $this: $superClass")
|
||||
|
||||
if (jClass.superclass == superJavaClass) {
|
||||
jClass.genericSuperclass
|
||||
}
|
||||
else {
|
||||
val index = jClass.interfaces.indexOf(superJavaClass)
|
||||
if (index < 0) throw KotlinReflectionInternalError("No superclass of $this in Java reflection for $superClass")
|
||||
jClass.genericInterfaces[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is KClassImpl<*> && jClass == other.jClass
|
||||
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package kotlin.jvm.internal
|
||||
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KTypeParameter
|
||||
import kotlin.reflect.*
|
||||
|
||||
class ClassReference(override val jClass: Class<*>) : KClass<Any>, ClassBasedDeclarationContainer {
|
||||
override val simpleName: String?
|
||||
@@ -46,6 +43,9 @@ class ClassReference(override val jClass: Class<*>) : KClass<Any>, ClassBasedDec
|
||||
override val typeParameters: List<KTypeParameter>
|
||||
get() = error()
|
||||
|
||||
override val supertypes: List<KType>
|
||||
get() = error()
|
||||
|
||||
private fun error(): Nothing = throw KotlinReflectionNotSupportedError()
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
|
||||
Reference in New Issue
Block a user