[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 {
|
||||
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
|
||||
|
||||
+12
@@ -146,6 +146,18 @@ public class IrBlackBoxModernJdkCodegenTestGenerated extends AbstractIrBlackBoxC
|
||||
public void testSealedJavaClassViaJavaReflection() throws Exception {
|
||||
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
|
||||
|
||||
+48
-2
@@ -123,10 +123,12 @@ class ReflectJavaClass(
|
||||
get() = false
|
||||
|
||||
override val isSealed: Boolean
|
||||
get() = false
|
||||
get() = Java16SealedRecordLoader.loadIsSealed(klass) ?: false
|
||||
|
||||
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
|
||||
|
||||
@@ -134,3 +136,47 @@ class ReflectJavaClass(
|
||||
|
||||
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