From ef278865b6a7d5949f831f9571ce9253b1d2571c Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Thu, 16 Feb 2023 15:27:12 -0500 Subject: [PATCH 01/13] add fallback solution for Services --- .../src/commonMain/kotlin/Services.kt | 40 +++++++++++++++++ .../src/jvmBaseMain/kotlin/Services.kt | 30 ++++++++++--- .../src/nativeMain/kotlin/Service.kt | 45 +------------------ 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 9b4a1d06525..63b830c87a9 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -11,9 +11,49 @@ package net.mamoe.mirai.utils +import kotlinx.atomicfu.locks.reentrantLock +import kotlinx.atomicfu.locks.withLock import kotlin.jvm.JvmName import kotlin.reflect.KClass +public object Services { + private val lock = reentrantLock() + public fun qualifiedNameOrFail(clazz: KClass): String = + clazz.qualifiedName ?: error("Could not find qualifiedName for $clazz") + + private class Implementation( + val implementationClass: String, + val instance: Lazy + ) + + private val registered: MutableMap> = mutableMapOf() + + public fun register(baseClass: String, implementationClass: String, implementation: () -> Any) { + lock.withLock { + registered.getOrPut(baseClass, ::mutableListOf) + .add(Implementation(implementationClass, lazy(implementation))) + } + } + + public fun firstImplementationOrNull(baseClass: String): Any? { + lock.withLock { + return registered[baseClass]?.firstOrNull()?.instance?.value + } + } + + public fun implementations(baseClass: String): List>? { + lock.withLock { + return registered[baseClass]?.map { it.instance } + } + } + + public fun print(): String { + lock.withLock { + return registered.entries.joinToString { "${it.key}:${it.value}" } + } + } +} + public expect fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String? = null): T? public expect fun loadService(clazz: KClass, fallbackImplementation: String? = null): T public expect fun loadServices(clazz: KClass): Sequence diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 979c380e850..a851dd31e4a 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -13,14 +13,21 @@ import java.util.* import kotlin.reflect.KClass import kotlin.reflect.full.createInstance +@Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { var suppressed: Throwable? = null - return ServiceLoader.load(clazz.java).firstOrNull() - ?: (if (fallbackImplementation == null) null - else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) - ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { - if (suppressed != null) addSuppressed(suppressed) - } + return if (systemProp("mirai.service.loader", "jdk") == "jdk") { + ServiceLoader.load(clazz.java).firstOrNull() + ?: (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) + } else { + // mirai.service.loader=fallback + (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) + ?: ServiceLoader.load(clazz.java).firstOrNull() + } ?: (if (fallbackImplementation == null) null + else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) + ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { + if (suppressed != null) addSuppressed(suppressed) + } } private fun findCreateInstance(fallbackImplementation: String): T { @@ -33,6 +40,15 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } +@Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { - return ServiceLoader.load(clazz.java).asSequence() + val seq: Sequence = + Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }?.asSequence() + ?: emptySequence() + return if (systemProp("mirai.service.loader", "jdk") == "jdk") { + ServiceLoader.load(clazz.java).asSequence().plus(seq) + } else { + // mirai.service.loader=fallback + seq + } } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Service.kt b/mirai-core-utils/src/nativeMain/kotlin/Service.kt index 4bbeb98e895..f2b6dc8b1ec 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/Service.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/Service.kt @@ -11,47 +11,9 @@ package net.mamoe.mirai.utils -import kotlinx.atomicfu.locks.reentrantLock -import kotlinx.atomicfu.locks.withLock +import net.mamoe.mirai.utils.Services.qualifiedNameOrFail import kotlin.reflect.KClass -public object Services { - private val lock = reentrantLock() - - private class Implementation( - val implementationClass: String, - val instance: Lazy - ) - - private val registered: MutableMap> = mutableMapOf() - - public fun register(baseClass: String, implementationClass: String, implementation: () -> Any) { - lock.withLock { - registered.getOrPut(baseClass, ::mutableListOf) - .add(Implementation(implementationClass, lazy(implementation))) - } - } - - public fun firstImplementationOrNull(baseClass: String): Any? { - lock.withLock { - return registered[baseClass]?.firstOrNull()?.instance?.value - } - } - - public fun implementations(baseClass: String): List>? { - lock.withLock { - return registered[baseClass]?.map { it.instance } - } - - } - - public fun print(): String { - lock.withLock { - return registered.entries.joinToString { "${it.key}:${it.value}" } - } - } -} - @Suppress("UNCHECKED_CAST") public actual fun loadServiceOrNull( clazz: KClass, @@ -66,7 +28,4 @@ public actual fun loadService( ?: error("Could not load service '${clazz.qualifiedName ?: clazz}'. Current services: ${Services.print()}") public actual fun loadServices(clazz: KClass): Sequence = - Services.implementations(qualifiedNameOrFail(clazz))?.asSequence()?.map { it.value }.orEmpty().castUp() - -private fun qualifiedNameOrFail(clazz: KClass) = - clazz.qualifiedName ?: error("Could not find qualifiedName for $clazz") \ No newline at end of file + Services.implementations(qualifiedNameOrFail(clazz))?.asSequence()?.map { it.value }.orEmpty().castUp() \ No newline at end of file From d56debbc0499a445df18211792ba71806755c28f Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Thu, 16 Feb 2023 15:31:08 -0500 Subject: [PATCH 02/13] use castUp --- mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index a851dd31e4a..d876be25b66 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -40,11 +40,9 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } -@Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { val seq: Sequence = - Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }?.asSequence() - ?: emptySequence() + Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value }.orEmpty().castUp() return if (systemProp("mirai.service.loader", "jdk") == "jdk") { ServiceLoader.load(clazz.java).asSequence().plus(seq) } else { From a7a48fd2dc559a30e9857c852d39f24c350683b0 Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Thu, 16 Feb 2023 17:39:29 -0500 Subject: [PATCH 03/13] throw exception when prop doesn't match --- .../src/jvmBaseMain/kotlin/Services.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index d876be25b66..91016ffbb0d 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -13,17 +13,24 @@ import java.util.* import kotlin.reflect.KClass import kotlin.reflect.full.createInstance +private inline fun loadDependOnProp(jdkBlock: () -> T?, fallBackBlock: () -> T?): T? { + return when (systemProp("mirai.service.loader", "jdk")) { + "jdk" -> jdkBlock() + "fallback" -> fallBackBlock() + else -> throw IllegalArgumentException("mirai.service.loader must be jdk or fallback, cannot find a service loader") + } +} + @Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { var suppressed: Throwable? = null - return if (systemProp("mirai.service.loader", "jdk") == "jdk") { + return loadDependOnProp({ ServiceLoader.load(clazz.java).firstOrNull() ?: (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) - } else { - // mirai.service.loader=fallback + }, { (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) ?: ServiceLoader.load(clazz.java).firstOrNull() - } ?: (if (fallbackImplementation == null) null + }) ?: (if (fallbackImplementation == null) null else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { if (suppressed != null) addSuppressed(suppressed) @@ -43,10 +50,5 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl public actual fun loadServices(clazz: KClass): Sequence { val seq: Sequence = Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value }.orEmpty().castUp() - return if (systemProp("mirai.service.loader", "jdk") == "jdk") { - ServiceLoader.load(clazz.java).asSequence().plus(seq) - } else { - // mirai.service.loader=fallback - seq - } + return loadDependOnProp({ ServiceLoader.load(clazz.java).asSequence().plus(seq) }, { seq })!! } \ No newline at end of file From 857b0b23181154471a2efb8b4bd7d7488f0d0218 Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Thu, 16 Feb 2023 19:59:31 -0500 Subject: [PATCH 04/13] cannot use castUp --- mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 91016ffbb0d..69e81d66331 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -47,8 +47,9 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } +@Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { val seq: Sequence = - Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value }.orEmpty().castUp() + Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() return loadDependOnProp({ ServiceLoader.load(clazz.java).asSequence().plus(seq) }, { seq })!! } \ No newline at end of file From 5b929cdedb30bff8575f3301fc5b1af8d1f90121 Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Sun, 19 Feb 2023 11:12:19 -0500 Subject: [PATCH 05/13] improve codes --- .../src/jvmBaseMain/kotlin/Services.kt | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 69e81d66331..121cfea2931 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -13,28 +13,37 @@ import java.util.* import kotlin.reflect.KClass import kotlin.reflect.full.createInstance -private inline fun loadDependOnProp(jdkBlock: () -> T?, fallBackBlock: () -> T?): T? { - return when (systemProp("mirai.service.loader", "jdk")) { - "jdk" -> jdkBlock() - "fallback" -> fallBackBlock() - else -> throw IllegalArgumentException("mirai.service.loader must be jdk or fallback, cannot find a service loader") - } +private enum class LoaderType { + JDK, + FALLBACK, +} + +private val loaderType = when (systemProp("mirai.service.loader", "jdk")) { + "jdk" -> LoaderType.JDK + "fallback" -> LoaderType.FALLBACK + else -> throw IllegalStateException("mirai.service.loader must be jdk or fallback, cannot find a service loader") } +private fun getJDKServices(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) + @Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { - var suppressed: Throwable? = null - return loadDependOnProp({ - ServiceLoader.load(clazz.java).firstOrNull() - ?: (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) - }, { + fun getFALLBACKService(clazz: KClass) = (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) - ?: ServiceLoader.load(clazz.java).firstOrNull() - }) ?: (if (fallbackImplementation == null) null - else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) - ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { - if (suppressed != null) addSuppressed(suppressed) - } + + var suppressed: Throwable? = null + + val services = when (loaderType) { + LoaderType.JDK -> getJDKServices(clazz).firstOrNull() ?: getFALLBACKService(clazz) + LoaderType.FALLBACK -> getFALLBACKService(clazz) ?: getJDKServices(clazz).firstOrNull() + } ?: if (fallbackImplementation != null) { + runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() + } else null + + return services + ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { + if (suppressed != null) addSuppressed(suppressed) + } } private fun findCreateInstance(fallbackImplementation: String): T { @@ -51,5 +60,9 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl public actual fun loadServices(clazz: KClass): Sequence { val seq: Sequence = Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() - return loadDependOnProp({ ServiceLoader.load(clazz.java).asSequence().plus(seq) }, { seq })!! + + return when (loaderType) { + LoaderType.JDK -> getJDKServices(clazz).asSequence().plus(seq) + LoaderType.FALLBACK -> seq + } } \ No newline at end of file From 0f615ca151b4cf82cf51cfdfbcd88a4e12c9a4ff Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Mon, 20 Feb 2023 20:08:21 -0500 Subject: [PATCH 06/13] improve name of functions --- .../src/jvmBaseMain/kotlin/Services.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 121cfea2931..18bcaa18562 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -24,18 +24,18 @@ private val loaderType = when (systemProp("mirai.service.loader", "jdk")) { else -> throw IllegalStateException("mirai.service.loader must be jdk or fallback, cannot find a service loader") } -private fun getJDKServices(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) +private fun getJdkServices(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) @Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { - fun getFALLBACKService(clazz: KClass) = + fun getFallbackService(clazz: KClass) = (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) var suppressed: Throwable? = null val services = when (loaderType) { - LoaderType.JDK -> getJDKServices(clazz).firstOrNull() ?: getFALLBACKService(clazz) - LoaderType.FALLBACK -> getFALLBACKService(clazz) ?: getJDKServices(clazz).firstOrNull() + LoaderType.JDK -> getJdkServices(clazz).firstOrNull() ?: getFallbackService(clazz) + LoaderType.FALLBACK -> getFallbackService(clazz) ?: getJdkServices(clazz).firstOrNull() } ?: if (fallbackImplementation != null) { runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() } else null @@ -58,11 +58,11 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl @Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { - val seq: Sequence = + val fallBackServicesSeq: Sequence = Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() return when (loaderType) { - LoaderType.JDK -> getJDKServices(clazz).asSequence().plus(seq) - LoaderType.FALLBACK -> seq + LoaderType.JDK -> getJdkServices(clazz).asSequence().plus(fallBackServicesSeq) + LoaderType.FALLBACK -> fallBackServicesSeq } } \ No newline at end of file From f808477cce4123ecc0a0dfd25e22a85c8d6a1bad Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Tue, 21 Feb 2023 23:42:41 -0500 Subject: [PATCH 07/13] add both --- .../src/jvmBaseMain/kotlin/Services.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 18bcaa18562..53750ac66ff 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -15,27 +15,31 @@ import kotlin.reflect.full.createInstance private enum class LoaderType { JDK, + BOTH, FALLBACK, } -private val loaderType = when (systemProp("mirai.service.loader", "jdk")) { +private val loaderType = when (systemProp("mirai.service.loader", "both")) { "jdk" -> LoaderType.JDK + "both" -> LoaderType.BOTH "fallback" -> LoaderType.FALLBACK - else -> throw IllegalStateException("mirai.service.loader must be jdk or fallback, cannot find a service loader") + else -> throw IllegalStateException("cannot find a service loader, mirai.service.loader must be both, jdk or fallback (default by both)") } -private fun getJdkServices(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) +private fun loadServiceByJdk(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) @Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { - fun getFallbackService(clazz: KClass) = - (Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T?) + val fallbackService by lazy { + Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T? + } var suppressed: Throwable? = null val services = when (loaderType) { - LoaderType.JDK -> getJdkServices(clazz).firstOrNull() ?: getFallbackService(clazz) - LoaderType.FALLBACK -> getFallbackService(clazz) ?: getJdkServices(clazz).firstOrNull() + LoaderType.JDK -> loadServiceByJdk(clazz).firstOrNull() + LoaderType.BOTH -> loadServiceByJdk(clazz).firstOrNull() ?: fallbackService + LoaderType.FALLBACK -> fallbackService } ?: if (fallbackImplementation != null) { runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() } else null @@ -58,11 +62,13 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl @Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { - val fallBackServicesSeq: Sequence = + val fallBackServicesSeq: Sequence by lazy { Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() + } return when (loaderType) { - LoaderType.JDK -> getJdkServices(clazz).asSequence().plus(fallBackServicesSeq) + LoaderType.JDK -> loadServiceByJdk(clazz).asSequence() + LoaderType.BOTH -> loadServiceByJdk(clazz).asSequence().plus(fallBackServicesSeq) LoaderType.FALLBACK -> fallBackServicesSeq } } \ No newline at end of file From a4c967e6291770ff910a668a1c0907c6c69b1891 Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Sat, 25 Feb 2023 13:48:24 -0500 Subject: [PATCH 08/13] add override --- .../src/commonMain/kotlin/Services.kt | 14 +++++++++++ .../src/jvmBaseMain/kotlin/Services.kt | 24 ++++++++++--------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 63b830c87a9..9cd757ede45 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -27,6 +27,20 @@ public object Services { ) private val registered: MutableMap> = mutableMapOf() + private val overrided: MutableMap = mutableMapOf() + + @Suppress("UNCHECKED_CAST") + public fun getOverrideOrNull(clazz: KClass): T? { + lock.withLock { + return overrided[qualifiedNameOrFail(clazz)]?.instance?.value as T? + } + } + + internal fun registerAsOverride(baseClass: String, implementationClass: String, implementation: () -> Any) { + lock.withLock { + overrided[baseClass] = Implementation(implementationClass, lazy(implementation)) + } + } public fun register(baseClass: String, implementationClass: String, implementation: () -> Any) { lock.withLock { diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 53750ac66ff..853b4bbccd4 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -36,18 +36,20 @@ public actual fun loadService(clazz: KClass, fallbackImplementa var suppressed: Throwable? = null - val services = when (loaderType) { - LoaderType.JDK -> loadServiceByJdk(clazz).firstOrNull() - LoaderType.BOTH -> loadServiceByJdk(clazz).firstOrNull() ?: fallbackService - LoaderType.FALLBACK -> fallbackService - } ?: if (fallbackImplementation != null) { - runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() - } else null + val services by lazy { + when (loaderType) { + LoaderType.JDK -> loadServiceByJdk(clazz).firstOrNull() + LoaderType.BOTH -> loadServiceByJdk(clazz).firstOrNull() ?: fallbackService + LoaderType.FALLBACK -> fallbackService + } ?: if (fallbackImplementation != null) { + runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() + } else null + } - return services - ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { - if (suppressed != null) addSuppressed(suppressed) - } + return Services.getOverrideOrNull(clazz) ?: services + ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { + if (suppressed != null) addSuppressed(suppressed) + } } private fun findCreateInstance(fallbackImplementation: String): T { From 3e9afbd56a5039a84e46a25e0fc40bed4bf7d116 Mon Sep 17 00:00:00 2001 From: Eritque arcus Date: Thu, 2 Mar 2023 10:28:39 -0500 Subject: [PATCH 09/13] solve conflicts --- .../src/jvmBaseMain/kotlin/Services.kt | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 853b4bbccd4..5f2374284c4 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -26,20 +26,23 @@ private val loaderType = when (systemProp("mirai.service.loader", "both")) { else -> throw IllegalStateException("cannot find a service loader, mirai.service.loader must be both, jdk or fallback (default by both)") } -private fun loadServiceByJdk(clazz: KClass): ServiceLoader = ServiceLoader.load(clazz.java) - @Suppress("UNCHECKED_CAST") public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { val fallbackService by lazy { Services.firstImplementationOrNull(Services.qualifiedNameOrFail(clazz)) as T? } + val jdkService by lazy { + ServiceLoader.load(clazz.java).firstOrNull() ?: ServiceLoader.load(clazz.java, clazz.java.classLoader) + .firstOrNull() + } + var suppressed: Throwable? = null val services by lazy { when (loaderType) { - LoaderType.JDK -> loadServiceByJdk(clazz).firstOrNull() - LoaderType.BOTH -> loadServiceByJdk(clazz).firstOrNull() ?: fallbackService + LoaderType.JDK -> jdkService + LoaderType.BOTH -> jdkService ?: fallbackService LoaderType.FALLBACK -> fallbackService } ?: if (fallbackImplementation != null) { runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() @@ -57,9 +60,7 @@ private fun findCreateInstance(fallbackImplementation: String): T { } public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { - return ServiceLoader.load(clazz.java).firstOrNull() - ?: if (fallbackImplementation == null) return null - else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() + return runCatching { loadService(clazz, fallbackImplementation) }.getOrNull() } @Suppress("UNCHECKED_CAST") @@ -68,9 +69,20 @@ public actual fun loadServices(clazz: KClass): Sequence { Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() } + val jdkServices: Sequence by lazy { + sequence { + val current = ServiceLoader.load(clazz.java).iterator() + if (current.hasNext()) { + yieldAll(current) + } else { + yieldAll(ServiceLoader.load(clazz.java, clazz.java.classLoader)) + } + } + } + return when (loaderType) { - LoaderType.JDK -> loadServiceByJdk(clazz).asSequence() - LoaderType.BOTH -> loadServiceByJdk(clazz).asSequence().plus(fallBackServicesSeq) + LoaderType.JDK -> jdkServices + LoaderType.BOTH -> jdkServices.plus(fallBackServicesSeq) LoaderType.FALLBACK -> fallBackServicesSeq } } \ No newline at end of file From 2973895f44eab33b4d0d4f06b7404232ca83fb3a Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Wed, 8 Mar 2023 23:39:57 +0800 Subject: [PATCH 10/13] [core] Move MiraiCoreServices to common --- .../kotlin/utils/MiraiCoreServices.kt | 0 mirai-core/src/jvmBaseMain/kotlin/MiraiImpl.kt | 2 ++ 2 files changed, 2 insertions(+) rename mirai-core/src/{nativeMain => commonMain}/kotlin/utils/MiraiCoreServices.kt (100%) diff --git a/mirai-core/src/nativeMain/kotlin/utils/MiraiCoreServices.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt similarity index 100% rename from mirai-core/src/nativeMain/kotlin/utils/MiraiCoreServices.kt rename to mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt diff --git a/mirai-core/src/jvmBaseMain/kotlin/MiraiImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/MiraiImpl.kt index 2679e62d61a..7db2c86cf3d 100644 --- a/mirai-core/src/jvmBaseMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/jvmBaseMain/kotlin/MiraiImpl.kt @@ -16,12 +16,14 @@ import io.ktor.client.engine.okhttp.* import io.ktor.client.plugins.* import kotlinx.atomicfu.atomic import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade +import net.mamoe.mirai.internal.utils.MiraiCoreServices private val initialized = atomic(false) @Suppress("FunctionName") internal actual fun _MiraiImpl_static_init() { if (!initialized.compareAndSet(expect = false, update = true)) return + MiraiCoreServices.registerAll() MessageProtocolFacade.INSTANCE // register serializers } From aa70d6f5aaa9151c1bfe100a6a0f98bb1fd5e341 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Wed, 8 Mar 2023 23:40:46 +0800 Subject: [PATCH 11/13] [core] Improvement --- .../src/commonMain/kotlin/Services.kt | 19 +++++- .../src/jvmBaseMain/kotlin/Services.kt | 58 +++++++++++++------ .../src/nativeMain/kotlin/Service.kt | 2 +- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 9cd757ede45..0b18985f6fb 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -21,7 +21,7 @@ public object Services { public fun qualifiedNameOrFail(clazz: KClass): String = clazz.qualifiedName ?: error("Could not find qualifiedName for $clazz") - private class Implementation( + internal class Implementation( val implementationClass: String, val instance: Lazy ) @@ -51,16 +51,29 @@ public object Services { public fun firstImplementationOrNull(baseClass: String): Any? { lock.withLock { + overrided[baseClass]?.let { return it.instance.value } return registered[baseClass]?.firstOrNull()?.instance?.value } } - public fun implementations(baseClass: String): List>? { + public fun implementations(baseClass: String): Sequence>? { lock.withLock { - return registered[baseClass]?.map { it.instance } + val reg = registered[baseClass] + val ove = overrided[baseClass] + if (ove == null && reg == null) return null + + val regSnapshot = reg?.toList().orEmpty() + + return sequence { + if (ove != null) yield(ove.instance) + + regSnapshot.forEach { yield(it.instance) } + } } } + internal fun implementations1(baseClass: String) = lock.withLock { registered[baseClass]?.toList().orEmpty() } + public fun print(): String { lock.withLock { return registered.entries.joinToString { "${it.key}:${it.value}" } diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index 5f2374284c4..c933f5c5c11 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -33,8 +33,9 @@ public actual fun loadService(clazz: KClass, fallbackImplementa } val jdkService by lazy { - ServiceLoader.load(clazz.java).firstOrNull() ?: ServiceLoader.load(clazz.java, clazz.java.classLoader) - .firstOrNull() + ServiceLoader.load(clazz.java).firstOrNull()?.let { return@lazy it } + + ServiceLoader.load(clazz.java, clazz.java.classLoader).firstOrNull() } var suppressed: Throwable? = null @@ -44,8 +45,12 @@ public actual fun loadService(clazz: KClass, fallbackImplementa LoaderType.JDK -> jdkService LoaderType.BOTH -> jdkService ?: fallbackService LoaderType.FALLBACK -> fallbackService - } ?: if (fallbackImplementation != null) { - runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() + }?.let { return@lazy it } + + if (fallbackImplementation != null) { + runCatching { + findCreateInstance(fallbackImplementation) + }.onFailure { suppressed = it }.getOrNull() } else null } @@ -65,24 +70,43 @@ public actual fun loadServiceOrNull(clazz: KClass, fallbackImpl @Suppress("UNCHECKED_CAST") public actual fun loadServices(clazz: KClass): Sequence { - val fallBackServicesSeq: Sequence by lazy { - Services.implementations(Services.qualifiedNameOrFail(clazz))?.map { it.value as T }.orEmpty().asSequence() + fun fallBackServicesSeq(): Sequence { + return Services.implementations(Services.qualifiedNameOrFail(clazz)).orEmpty() + .map { it.value as T } } - val jdkServices: Sequence by lazy { - sequence { - val current = ServiceLoader.load(clazz.java).iterator() - if (current.hasNext()) { - yieldAll(current) - } else { - yieldAll(ServiceLoader.load(clazz.java, clazz.java.classLoader)) - } + fun jdkServices(): Sequence = sequence { + val current = ServiceLoader.load(clazz.java).iterator() + if (current.hasNext()) { + yieldAll(current) + } else { + yieldAll(ServiceLoader.load(clazz.java, clazz.java.classLoader)) + } + } + + fun bothServices(): Sequence = sequence { + Services.getOverrideOrNull(clazz)?.let { yield(it) } + + var jdkServices = ServiceLoader.load(clazz.java).toList() + if (jdkServices.isEmpty()) { + jdkServices = ServiceLoader.load(clazz.java, clazz.java.classLoader).toList() } + yieldAll(jdkServices) + + Services.implementations1(Services.qualifiedNameOrFail(clazz)).asSequence() + .filter { impl -> + // Drop duplicated + jdkServices.none { it.javaClass.name == impl.implementationClass } + } + .forEach { yield(it.instance.value as T) } } + + + return when (loaderType) { - LoaderType.JDK -> jdkServices - LoaderType.BOTH -> jdkServices.plus(fallBackServicesSeq) - LoaderType.FALLBACK -> fallBackServicesSeq + LoaderType.JDK -> jdkServices() + LoaderType.BOTH -> bothServices() + LoaderType.FALLBACK -> fallBackServicesSeq() } } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Service.kt b/mirai-core-utils/src/nativeMain/kotlin/Service.kt index f2b6dc8b1ec..c74f7e4959e 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/Service.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/Service.kt @@ -28,4 +28,4 @@ public actual fun loadService( ?: error("Could not load service '${clazz.qualifiedName ?: clazz}'. Current services: ${Services.print()}") public actual fun loadServices(clazz: KClass): Sequence = - Services.implementations(qualifiedNameOrFail(clazz))?.asSequence()?.map { it.value }.orEmpty().castUp() \ No newline at end of file + Services.implementations(qualifiedNameOrFail(clazz)).orEmpty().map { it.value }.castUp() From eb0d0f2885893f1ea282b988290e66e79df88a8d Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Fri, 10 Mar 2023 17:59:05 +0800 Subject: [PATCH 12/13] update var names --- mirai-core-utils/src/commonMain/kotlin/Services.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 0b18985f6fb..9204ab7c63d 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -58,16 +58,16 @@ public object Services { public fun implementations(baseClass: String): Sequence>? { lock.withLock { - val reg = registered[baseClass] - val ove = overrided[baseClass] - if (ove == null && reg == null) return null + val implementations = registered[baseClass] + val forced = overrided[baseClass] + if (forced == null && implementations == null) return null - val regSnapshot = reg?.toList().orEmpty() + val implementationsSnapshot = implementations?.toList().orEmpty() return sequence { - if (ove != null) yield(ove.instance) + if (forced != null) yield(forced.instance) - regSnapshot.forEach { yield(it.instance) } + implementationsSnapshot.forEach { yield(it.instance) } } } } From 1ec549de90de4235465943b662f060adbab1f5c3 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Fri, 10 Mar 2023 22:31:28 +0800 Subject: [PATCH 13/13] update func names --- mirai-core-utils/src/commonMain/kotlin/Services.kt | 2 +- mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 9204ab7c63d..507ee3cda32 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -72,7 +72,7 @@ public object Services { } } - internal fun implementations1(baseClass: String) = lock.withLock { registered[baseClass]?.toList().orEmpty() } + internal fun implementationsDirectly(baseClass: String) = lock.withLock { registered[baseClass]?.toList().orEmpty() } public fun print(): String { lock.withLock { diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt index c933f5c5c11..e21e7d81e1f 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -93,7 +93,7 @@ public actual fun loadServices(clazz: KClass): Sequence { } yieldAll(jdkServices) - Services.implementations1(Services.qualifiedNameOrFail(clazz)).asSequence() + Services.implementationsDirectly(Services.qualifiedNameOrFail(clazz)).asSequence() .filter { impl -> // Drop duplicated jdkServices.none { it.javaClass.name == impl.implementationClass }