From cbc92bc9a199919ea3d4b9c32bbe8badf100cefc Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 3 Aug 2018 13:23:53 +0200 Subject: [PATCH] Add KClass.sealedSubclasses to get direct subclasses of sealed class #KT-14657 Fixed --- .../reflection/classes/sealedSubclasses.kt | 41 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++ .../LightAnalysisModeTestGenerated.java | 5 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++ core/builtins/src/kotlin/reflect/KClass.kt | 6 +++ .../kotlin/reflect/jvm/internal/KClassImpl.kt | 13 ++++++ .../IrJsCodegenBoxTestGenerated.java | 5 +++ .../semantics/JsCodegenBoxTestGenerated.java | 5 +++ .../js/src/kotlin/reflect/KClassImpl.kt | 2 + .../kotlin/jvm/internal/ClassReference.kt | 4 ++ .../reference-public-api/kotlin-runtime.txt | 2 + .../kotlin-stdlib-runtime-merged.txt | 2 + 12 files changed, 95 insertions(+) create mode 100644 compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt diff --git a/compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt b/compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt new file mode 100644 index 00000000000..029368c8e75 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt @@ -0,0 +1,41 @@ +// IGNORE_BACKEND: JS, JS_IR, JVM_IR, NATIVE +// WITH_REFLECT + +import kotlin.reflect.KClass +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals + +// -- + +sealed class SealedClassWithTopLevelSubclasses { + class NotASealedSubclass : TL2() +} +object TL1 : SealedClassWithTopLevelSubclasses() +open class TL2 : SealedClassWithTopLevelSubclasses() + +// -- + +sealed class SealedClassWithNestedSubclasses { + data class N1(val x: Unit) : SealedClassWithNestedSubclasses() + object N2 : SealedClassWithNestedSubclasses() +} + +// -- + +sealed class SealedClassWithNoSubclasses + +// -- + +fun sealedSubclassNames(c: KClass<*>) = c.sealedSubclasses.map { it.simpleName ?: throw AssertionError("Unnamed class: ${it.java}") }.sorted() + +fun box(): String { + assertEquals(listOf("TL1", "TL2"), sealedSubclassNames(SealedClassWithTopLevelSubclasses::class)) + assertEquals(listOf("N1", "N2"), sealedSubclassNames(SealedClassWithNestedSubclasses::class)) + assertEquals(emptyList(), sealedSubclassNames(SealedClassWithNoSubclasses::class)) + + assertEquals(emptyList(), sealedSubclassNames(String::class)) + assertEquals(emptyList(), sealedSubclassNames(Thread::class)) + assertEquals(emptyList(), sealedSubclassNames(FloatArray::class)) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 16255e24df2..3a1d9a30778 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -17844,6 +17844,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt"); } + @TestMetadata("sealedSubclasses.kt") + public void testSealedSubclasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt"); + } + @TestMetadata("starProjectedType.kt") public void testStarProjectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/classes/starProjectedType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 955bd28862b..c553aa90ee9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17844,6 +17844,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt"); } + @TestMetadata("sealedSubclasses.kt") + public void testSealedSubclasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt"); + } + @TestMetadata("starProjectedType.kt") public void testStarProjectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/classes/starProjectedType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4e5471577d4..fbbf0cc7bfb 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17844,6 +17844,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt"); } + @TestMetadata("sealedSubclasses.kt") + public void testSealedSubclasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt"); + } + @TestMetadata("starProjectedType.kt") public void testStarProjectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/classes/starProjectedType.kt"); diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 5003d67a445..0e318047776 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -76,6 +76,12 @@ public interface KClass : KDeclarationContainer, KAnnotatedElement, KCl @SinceKotlin("1.1") public val supertypes: List + /** + * The list of the immediate subclasses if this class is a sealed class, or an empty list otherwise. + */ + @SinceKotlin("1.3") + public val sealedSubclasses: List> + /** * Visibility of this class, or `null` if its visibility cannot be represented in Kotlin. */ diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 32d5948bd8c..7c70d799c42 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -147,6 +147,14 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration result.compact() } + val sealedSubclasses: List> by ReflectProperties.lazySoft { + descriptor.sealedSubclasses.mapNotNull { subclass -> + @Suppress("UNCHECKED_CAST") + val jClass = (subclass as ClassDescriptor).toJavaClass() as Class? + jClass?.let { KClassImpl(it) } + } + } + val declaredNonStaticMembers: Collection> by ReflectProperties.lazySoft { getMembers(memberScope, DECLARED) } private val declaredStaticMembers: Collection> @@ -239,6 +247,11 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration override val supertypes: List get() = data().supertypes + /** + * The list of the immediate subclasses if this class is a sealed class, or an empty list otherwise. + */ + override val sealedSubclasses: List> get() = data().sealedSubclasses + override val visibility: KVisibility? get() = descriptor.visibility.toKVisibility() diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 4acd3c2b316..3678383ab4f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -16009,6 +16009,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt"); } + @TestMetadata("sealedSubclasses.kt") + public void testSealedSubclasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt"); + } + @TestMetadata("starProjectedType.kt") public void testStarProjectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/classes/starProjectedType.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index b230fa4ab23..71d5b45bf5e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -17069,6 +17069,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt"); } + @TestMetadata("sealedSubclasses.kt") + public void testSealedSubclasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/classes/sealedSubclasses.kt"); + } + @TestMetadata("starProjectedType.kt") public void testStarProjectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/classes/starProjectedType.kt"); diff --git a/libraries/stdlib/js/src/kotlin/reflect/KClassImpl.kt b/libraries/stdlib/js/src/kotlin/reflect/KClassImpl.kt index b6279f7ef5d..5cb63dcb9f6 100644 --- a/libraries/stdlib/js/src/kotlin/reflect/KClassImpl.kt +++ b/libraries/stdlib/js/src/kotlin/reflect/KClassImpl.kt @@ -40,6 +40,8 @@ internal abstract class KClassImpl( get() = TODO() override val typeParameters: List get() = TODO() + override val sealedSubclasses: List> + get() = TODO() override val visibility: KVisibility? get() = TODO() diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt index fd9db187426..1052b77772d 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt @@ -40,6 +40,10 @@ public class ClassReference(override val jClass: Class<*>) : KClass, ClassB override val supertypes: List get() = error() + @SinceKotlin("1.3") + override val sealedSubclasses: List> + get() = error() + @SinceKotlin("1.1") override val visibility: KVisibility? get() = error() diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt index 323d11ad5ba..840f1ac3abf 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt @@ -455,6 +455,7 @@ public final class kotlin/jvm/internal/ClassReference : kotlin/jvm/internal/Clas public fun getNestedClasses ()Ljava/util/Collection; public fun getObjectInstance ()Ljava/lang/Object; public fun getQualifiedName ()Ljava/lang/String; + public fun getSealedSubclasses ()Ljava/util/List; public fun getSimpleName ()Ljava/lang/String; public fun getSupertypes ()Ljava/util/List; public fun getTypeParameters ()Ljava/util/List; @@ -1173,6 +1174,7 @@ public abstract interface class kotlin/reflect/KClass : kotlin/reflect/KAnnotate public abstract fun getNestedClasses ()Ljava/util/Collection; public abstract fun getObjectInstance ()Ljava/lang/Object; public abstract fun getQualifiedName ()Ljava/lang/String; + public abstract fun getSealedSubclasses ()Ljava/util/List; public abstract fun getSimpleName ()Ljava/lang/String; public abstract fun getSupertypes ()Ljava/util/List; public abstract fun getTypeParameters ()Ljava/util/List; diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index c927d711813..9434ae3739c 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3293,6 +3293,7 @@ public final class kotlin/jvm/internal/ClassReference : kotlin/jvm/internal/Clas public fun getNestedClasses ()Ljava/util/Collection; public fun getObjectInstance ()Ljava/lang/Object; public fun getQualifiedName ()Ljava/lang/String; + public fun getSealedSubclasses ()Ljava/util/List; public fun getSimpleName ()Ljava/lang/String; public fun getSupertypes ()Ljava/util/List; public fun getTypeParameters ()Ljava/util/List; @@ -4292,6 +4293,7 @@ public abstract interface class kotlin/reflect/KClass : kotlin/reflect/KAnnotate public abstract fun getNestedClasses ()Ljava/util/Collection; public abstract fun getObjectInstance ()Ljava/lang/Object; public abstract fun getQualifiedName ()Ljava/lang/String; + public abstract fun getSealedSubclasses ()Ljava/util/List; public abstract fun getSimpleName ()Ljava/lang/String; public abstract fun getSupertypes ()Ljava/util/List; public abstract fun getTypeParameters ()Ljava/util/List;