[Reflection] Support java sealed classes in kotlin reflection
^KT-46778
This commit is contained in:
committed by
teamcityserver
parent
fa1d09c778
commit
8dad8fa813
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
// FILE: Base.java
|
||||||
|
public sealed class Base permits O, K {}
|
||||||
|
|
||||||
|
// FILE: O.java
|
||||||
|
public final class O extends Base {}
|
||||||
|
|
||||||
|
// FILE: K.java
|
||||||
|
public non-sealed class K extends Base {}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val klass = Base::class
|
||||||
|
if (!klass.isSealed) return "Error: Base is not sealed"
|
||||||
|
if (klass.isAbstract) return "Error: Base is not abstract"
|
||||||
|
return klass.sealedSubclasses
|
||||||
|
.joinToString("") { it.simpleName ?: "_No name provided_" }
|
||||||
|
.takeIf { it.isNotBlank() }
|
||||||
|
?: "_No sealed subclasses found_"
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
// FILE: Base.java
|
||||||
|
public sealed interface Base permits O, K {}
|
||||||
|
|
||||||
|
// FILE: O.java
|
||||||
|
public non-sealed interface O extends Base {}
|
||||||
|
|
||||||
|
// FILE: K.java
|
||||||
|
public non-sealed interface K extends Base {}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val klass = Base::class
|
||||||
|
if (!klass.isSealed) return "Error: Base is not sealed"
|
||||||
|
if (klass.isAbstract) return "Error: Base is abstract"
|
||||||
|
return klass.sealedSubclasses
|
||||||
|
.joinToString("") { it.simpleName ?: "_No name provided_" }
|
||||||
|
.takeIf { it.isNotBlank() }
|
||||||
|
?: "_No sealed subclasses found_"
|
||||||
|
}
|
||||||
+12
@@ -146,6 +146,18 @@ public class BlackBoxModernJdkCodegenTestGenerated extends AbstractBlackBoxCodeg
|
|||||||
public void testSealedJavaClassViaJavaReflection() throws Exception {
|
public void testSealedJavaClassViaJavaReflection() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaJavaReflection.kt");
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaJavaReflection.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("sealedJavaClassViaKotlinReflection.kt")
|
||||||
|
public void testSealedJavaClassViaKotlinReflection() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaKotlinReflection.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("sealedJavaInterfaceViaKotlinReflection.kt")
|
||||||
|
public void testSealedJavaInterfaceViaKotlinReflection() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaInterfaceViaKotlinReflection.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
+12
@@ -146,6 +146,18 @@ public class IrBlackBoxModernJdkCodegenTestGenerated extends AbstractIrBlackBoxC
|
|||||||
public void testSealedJavaClassViaJavaReflection() throws Exception {
|
public void testSealedJavaClassViaJavaReflection() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaJavaReflection.kt");
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaJavaReflection.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("sealedJavaClassViaKotlinReflection.kt")
|
||||||
|
public void testSealedJavaClassViaKotlinReflection() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaClassViaKotlinReflection.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("sealedJavaInterfaceViaKotlinReflection.kt")
|
||||||
|
public void testSealedJavaInterfaceViaKotlinReflection() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava17/sealedJavaInterfaceViaKotlinReflection.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
+48
-2
@@ -123,10 +123,12 @@ class ReflectJavaClass(
|
|||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
override val isSealed: Boolean
|
override val isSealed: Boolean
|
||||||
get() = false
|
get() = Java16SealedRecordLoader.loadIsSealed(klass) ?: false
|
||||||
|
|
||||||
override val permittedTypes: Collection<JavaClassifierType>
|
override val permittedTypes: Collection<JavaClassifierType>
|
||||||
get() = emptyList()
|
get() = Java16SealedRecordLoader.loadGetPermittedSubclasses(klass)
|
||||||
|
?.map(::ReflectJavaClassifierType)
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
override fun equals(other: Any?) = other is ReflectJavaClass && klass == other.klass
|
override fun equals(other: Any?) = other is ReflectJavaClass && klass == other.klass
|
||||||
|
|
||||||
@@ -134,3 +136,47 @@ class ReflectJavaClass(
|
|||||||
|
|
||||||
override fun toString() = this::class.java.name + ": " + klass
|
override fun toString() = this::class.java.name + ": " + klass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object Java16SealedRecordLoader {
|
||||||
|
class Cache(
|
||||||
|
val isSealed: Method?,
|
||||||
|
val getPermittedSubclasses: Method?,
|
||||||
|
)
|
||||||
|
|
||||||
|
private var _cache: Cache? = null
|
||||||
|
|
||||||
|
private fun buildCache(): Cache {
|
||||||
|
val clazz = Class::class.java
|
||||||
|
|
||||||
|
return try {
|
||||||
|
Cache(
|
||||||
|
clazz.getMethod("isSealed"),
|
||||||
|
clazz.getMethod("getPermittedSubclasses"),
|
||||||
|
)
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
Cache(null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initCache(): Cache {
|
||||||
|
var cache = this._cache
|
||||||
|
if (cache == null) {
|
||||||
|
cache = buildCache()
|
||||||
|
this._cache = cache
|
||||||
|
}
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadIsSealed(clazz: Class<*>): Boolean? {
|
||||||
|
val cache = initCache()
|
||||||
|
val isSealed = cache.isSealed ?: return null
|
||||||
|
return isSealed.invoke(clazz) as Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadGetPermittedSubclasses(clazz: Class<*>): Array<Class<*>>? {
|
||||||
|
val cache = initCache()
|
||||||
|
val getPermittedSubclasses = cache.getPermittedSubclasses ?: return null
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return getPermittedSubclasses.invoke(clazz) as Array<Class<*>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user