From 3806d7ce7824d197b0bf566751c75282a63f2f35 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 24 Apr 2021 15:26:38 +0800 Subject: [PATCH] fix componentName and smartToString --- .../kotlin/network/component/ComponentKey.kt | 43 +++++++++++++------ .../network/component/ComponentKeyTest.kt | 40 ++++++++++++++++- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt b/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt index 212a9a6f8..be0ccd70a 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt @@ -9,14 +9,11 @@ package net.mamoe.mirai.internal.network.component -import kotlin.reflect.KClass -import kotlin.reflect.KClassifier -import kotlin.reflect.KType -import kotlin.reflect.KTypeParameter +import kotlin.reflect.* import kotlin.reflect.full.allSupertypes /** - * A key for specific component [T]. Component are not polymorphic. + * A key for specific component [T]. Components are not polymorphic. * * @param T is a type hint. */ @@ -28,14 +25,36 @@ internal interface ComponentKey { * - If [qualified] is `true`, example: `net.mamoe.mirai.internal.network.components.PacketCodec`. */ fun componentName(qualified: Boolean = false): String { - return getComponentTypeArgumentClassifier().renderClassifier(fullName = qualified) + val argument = getComponentTypeArgument() + argument?.render(qualified)?.let { return it } + return argument?.type?.classifier.renderClassifier(qualified) } fun smartToString(qualified: Boolean = false): String { return "ComponentKey<${componentName(qualified)}>" } - private companion object { + companion object { + // reflection is slow, but it is initialized once only (if memory sufficient). + + private fun KTypeProjection.render( + fullName: Boolean + ): String? { + val projection = this + + projection.type?.classifier?.let { classifier -> + if (classifier is KClass<*>) { + return classifier.run { if (fullName) qualifiedName else simpleName } ?: "?" + } + } + + projection.type?.arguments?.firstOrNull()?.let { argument -> + return argument.render(fullName) + } + + return null + } + private fun KClassifier?.renderClassifier( fullName: Boolean ): String { @@ -53,16 +72,16 @@ internal interface ComponentKey { val upperBounds = upperBounds return when (upperBounds.size) { 0 -> toString() - 1 -> "ComponentKey<${upperBounds[0].renderType(fullName)}>" - else -> "ComponentKey<${upperBounds.joinToString(" & ") { it.renderType(fullName) }}>" + 1 -> upperBounds[0].renderType(fullName) + else -> upperBounds.joinToString(" & ") { it.renderType(fullName) } } } - private fun ComponentKey<*>.getComponentTypeArgumentClassifier(): KClassifier? { + private fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? { val thisType = this::class.allSupertypes.find { it.classifier == COMPONENT_KEY_K_CLASS } - return thisType?.arguments?.firstOrNull()?.type?.classifier + return thisType?.arguments?.firstOrNull() } - val COMPONENT_KEY_K_CLASS = ComponentKey::class + private val COMPONENT_KEY_K_CLASS = ComponentKey::class } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt b/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt index 67b2b11a5..525758529 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt @@ -13,10 +13,19 @@ import net.mamoe.mirai.internal.test.AbstractTest import org.junit.jupiter.api.Test import kotlin.test.assertEquals -private class TestComponent { +private open class TestComponent { companion object : ComponentKey } + +private interface MyInterface + +private open class TestComponentExt : TestComponent(), MyInterface + +private open class Key : ComponentKey where R : TestComponent, R : MyInterface +private class KeyActual : Key() + + internal class ComponentKeyTest : AbstractTest() { @Test @@ -25,9 +34,38 @@ internal class ComponentKeyTest : AbstractTest() { assertEquals(TestComponent::class.qualifiedName!!, TestComponent.componentName(true)) } + @Test + fun `componentName with erased type argument`() { + assertEquals( + "TestComponent & MyInterface", + Key().componentName(false) + ) + assertEquals( + "${TestComponent::class.qualifiedName!!} & ${MyInterface::class.qualifiedName!!}", + Key().componentName(true) + ) + } + + @Test + fun `componentName with actual type argument`() { + assertEquals( + "TestComponentExt", + KeyActual().componentName(false) + ) + assertEquals( + TestComponentExt::class.qualifiedName!!, + KeyActual().componentName(true) + ) + } + @Test fun `test smartToString`() { assertEquals("ComponentKey", TestComponent.smartToString(false)) assertEquals("ComponentKey<${TestComponent::class.qualifiedName!!}>", TestComponent.smartToString(true)) + assertEquals( + "ComponentKey<${TestComponent::class.qualifiedName!!} & ${MyInterface::class.qualifiedName!!}>", + Key().smartToString(true) + ) + assertEquals("ComponentKey<${TestComponentExt::class.qualifiedName!!}>", KeyActual().smartToString(true)) } } \ No newline at end of file