(ABI change) Rewrite ComponentStorage: order extensions by property priority. Remove builtinImplementations and contribute them at the first initialization phase instead.

Close #1888, fix #1860.

Add `ComponentStorageInternal` for frontend to provide components.

Deprecate:
- SingletonExtension
- SingletonExtensionPoint
- AbstractSingletonExtensionPoint
- SingletonExtensionSelector
- CommandCallInterceptorProviderImpl
- CommandCallInterceptorProviderImplLazy
- CommandCallParserProviderImpl
- CommandCallParserProviderImplLazy
- CommandCallResolverProviderImpl
- CommandCallResolverProviderImplLazy

ABI breaking change:
- `PermissionServiceProvider`: supertype changed
- `CommandCallResolverProvider.ExtensionPoint`: supertype changed
- `PermissionServiceProvider.ExtensionPoint`: supertype changed
This commit is contained in:
Him188 2022-02-17 16:28:25 +00:00
parent 96e943c33f
commit 835059c44c
25 changed files with 554 additions and 312 deletions

View File

@ -162,7 +162,7 @@ public interface MiraiConsole : CoroutineScope {
*
* 调用 [Bot.login] 可登录.
*
* @see Bot.botInstances 获取现有 [Bot] 实例列表
* @see Bot.instances 获取现有 [Bot] 实例列表
* @see BotConfigurationAlterer ExtensionPoint
*/
// don't static
@ -225,11 +225,8 @@ public interface MiraiConsole : CoroutineScope {
configuration()
}
config = GlobalComponentStorage.run {
BotConfigurationAlterer.foldExtensions(config) { acc, extension ->
extension.alterConfiguration(id, acc)
}
config = GlobalComponentStorage.foldExtensions(BotConfigurationAlterer, config) { acc, extension ->
extension.alterConfiguration(id, acc)
}
return when (password) {

View File

@ -25,7 +25,6 @@ import net.mamoe.mirai.console.extension.ComponentStorage
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScopeImpl
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.logging.LoggerControllerImpl
import net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl
import net.mamoe.mirai.console.internal.pluginManagerImpl
@ -317,6 +316,9 @@ public interface MiraiConsoleImplementation : CoroutineScope {
@ConsoleFrontEndImplementation
public interface BackendAccess {
// GlobalComponentStorage
/**
* Mirai Console 第一个 phase 之后会包含内建 storages.
*/
public val globalComponentStorage: ComponentStorage
// PluginManagerImpl.resolvedPlugins
@ -365,7 +367,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
@ConsoleFrontEndImplementation
public companion object {
private val backendAccessInstance = object : BackendAccess {
override val globalComponentStorage: ComponentStorage get() = GlobalComponentStorage
override val globalComponentStorage: ComponentStorage get() = getBridge().globalComponentStorage
override val resolvedPlugins: MutableList<Plugin> get() = MiraiConsole.pluginManagerImpl.resolvedPlugins
}

View File

@ -28,6 +28,7 @@ import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.*
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.MD5
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.PLAIN
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
import net.mamoe.mirai.console.internal.pluginManagerImpl
import net.mamoe.mirai.console.internal.util.autoHexToBytes
@ -484,7 +485,7 @@ public object BuiltInCommands {
lightYellow()
"Built In Permission Service"
} else {
val plugin = PermissionServiceProvider.providerPlugin
val plugin = GlobalComponentStorage.getPreferredExtension(PermissionServiceProvider).plugin
if (plugin == null) {
PermissionService.INSTANCE.toString()
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -44,10 +44,8 @@ public interface CommandCallParser {
*/
@JvmStatic
public fun MessageChain.parseCommandCall(sender: CommandSender): CommandCall? {
GlobalComponentStorage.run {
CommandCallParserProvider.useExtensions { provider ->
provider.instance.parse(sender, this@parseCommandCall)?.let { return it }
}
GlobalComponentStorage.useEachExtensions(CommandCallParserProvider) { provider ->
provider.instance.parse(sender, this@parseCommandCall)?.let { return it }
}
return null
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -12,7 +12,6 @@ package net.mamoe.mirai.console.command.parse
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.extensions.CommandCallParserProvider
import net.mamoe.mirai.console.extensions.CommandCallParserProviderImpl
import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.message.data.MessageChain
@ -22,6 +21,13 @@ import net.mamoe.mirai.message.data.content
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public object SpaceSeparatedCommandCallParser : CommandCallParser {
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public object Provider : CommandCallParserProvider {
override val instance: CommandCallParser get() = SpaceSeparatedCommandCallParser
}
override fun parse(caller: CommandSender, message: MessageChain): CommandCall? {
val flatten = message.flattenCommandComponents().filterIsInstance<MessageContent>()
if (flatten.isEmpty()) return null
@ -31,6 +37,4 @@ public object SpaceSeparatedCommandCallParser : CommandCallParser {
valueArguments = flatten.drop(1).map(::DefaultCommandValueArgument)
)
}
public object Provider : CommandCallParserProvider by CommandCallParserProviderImpl(SpaceSeparatedCommandCallParser)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -15,6 +15,7 @@ import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.i
import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.command.parse.CommandValueArgument
import net.mamoe.mirai.console.command.parse.DefaultCommandValueArgument
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
import net.mamoe.mirai.console.internal.data.classifierAsKClass
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.safeCast
@ -27,6 +28,11 @@ import net.mamoe.mirai.message.data.toMessageChain
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public object BuiltInCommandCallResolver : CommandCallResolver {
internal object Provider : CommandCallResolverProvider {
override val instance: CommandCallResolver = BuiltInCommandCallResolver
}
override fun resolve(call: CommandCall): CommandResolveResult {
val callee = CommandManager.matchCommand(call.calleeName)
?: return CommandResolveResult(CommandExecuteResult.UnresolvedCommand(call))

View File

@ -66,15 +66,13 @@ public interface CommandCallInterceptor {
*/
@JvmStatic
public fun Message.intercepted(caller: CommandSender): InterceptResult<Message> {
GlobalComponentStorage.run {
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptBeforeCall(acc, caller)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
return GlobalComponentStorage.foldExtensions(CommandCallInterceptorProvider, this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptBeforeCall(acc, caller)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
/**
@ -83,15 +81,13 @@ public interface CommandCallInterceptor {
*/
@JvmStatic
public fun CommandCall.intercepted(): InterceptResult<CommandCall> {
GlobalComponentStorage.run {
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
return GlobalComponentStorage.foldExtensions(CommandCallInterceptorProvider, this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
/**
@ -100,15 +96,13 @@ public interface CommandCallInterceptor {
*/
@JvmStatic
public fun ResolvedCommandCall.intercepted(): InterceptResult<ResolvedCommandCall> {
GlobalComponentStorage.run {
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptResolvedCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
return GlobalComponentStorage.foldExtensions(CommandCallInterceptorProvider, this@intercepted) { acc, ext ->
val intercepted = ext.instance.interceptResolvedCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
) ?: acc
}.let { InterceptResult(it) }
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -76,11 +76,9 @@ public interface CommandCallResolver {
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public fun CommandCall.resolve(): CommandResolveResult {
GlobalComponentStorage.run {
val instance =
CommandCallResolverProvider.findSingletonInstance(CommandCallResolverProvider.builtinImplementation)
return instance.resolve(this@resolve)
}
return GlobalComponentStorage.getExtensions(CommandCallResolverProvider).first()
.extension.instance
.resolve(this@resolve)
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -9,27 +9,99 @@
package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
import net.mamoe.mirai.console.MiraiConsoleImplementation
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin.Companion.onLoad
import net.mamoe.mirai.utils.MiraiExperimentalApi
import java.util.stream.Stream
/**
* 组件容器, 容纳 [Plugin] 注册的 [Extension].
*
* 插件可在 [JvmPlugin.onLoad] 时提供扩展. 前端可在 [MiraiConsoleImplementation.BackendAccess.globalComponentStorage] 获取全局组件容器.
* 目前未允许获取全局组件容器. 如有需求请 [提交 issues](https://github.com/mamoe/mirai/issues/new/choose).
*
* 实现细节: 线程安全.
*
* @see Extension
* @see JvmPlugin.onLoad
*/
public interface ComponentStorage {
public fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
plugin: Plugin,
extensionInstance: T,
)
public fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
/**
* 注册一个扩展
*/
public fun <E : Extension> contribute(
extensionPoint: ExtensionPoint<E>,
plugin: Plugin,
lazyInstance: () -> T,
)
extensionInstance: E,
) // 2.11: E changed to T (only naming)
/**
* 注册一个扩展. [lazyInstance] 将会在 [getExtensions] 时才会计算.
*/
public fun <E : Extension> contribute(
extensionPoint: ExtensionPoint<E>,
plugin: Plugin,
lazyInstance: () -> E,
) // 2.11: E changed to T (only naming)
/**
* 获取优先级最高的 [ExtensionPoint] 扩展实例. 在未找到任何注册的实例时抛出 [NoSuchElementException].
*
* @since 2.11
*/
@MiraiExperimentalApi
public fun <E : Extension> getPreferredExtension(
extensionPoint: ExtensionPoint<E>,
): ExtensionRegistry<E> {
return getExtensions(extensionPoint).firstOrNull()
?: throw NoSuchElementException("No extension registered for $extensionPoint")
}
/**
* 获取注册的 [ExtensionPoint] 扩展实例列表. 返回的 [Sequence] [Extension.priority] 倒序排序.
*
* @since 2.11
*/
public fun <E : Extension> getExtensions(
extensionPoint: ExtensionPoint<E>,
): Sequence<ExtensionRegistry<E>>
/**
* 获取注册的 [ExtensionPoint] 扩展实例列表. 返回的 [Stream] [Extension.priority] 倒序排序.
*
* @since 2.11
*/
public fun <E : Extension> getExtensionsStream(
extensionPoint: ExtensionPoint<E>,
): Stream<ExtensionRegistry<E>>
}
/**
* 仅前端实现可用
*/
@ConsoleFrontEndImplementation
public interface ComponentStorageInternal : ComponentStorage {
/**
* 注册一个由 Mirai Console 实现的扩展 (因此没有相关 [Plugin]). [lazyInstance] 将会在 [getExtensions] 时才会计算.
*/
public fun <E : Extension> contributeConsole(
extensionPoint: ExtensionPoint<E>,
lazyInstance: () -> E,
)
/**
* 注册一个由 Mirai Console 实现的扩展 (因此没有相关 [Plugin]).
*/
public fun <E : Extension> contributeConsole(
extensionPoint: ExtensionPoint<E>,
instance: E,
) {
@Suppress("USELESS_CAST") // bug
contributeConsole(extensionPoint, { instance } as () -> E)
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -9,12 +9,15 @@
package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.command.parse.SpaceSeparatedCommandCallParser
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.extensions.PluginLoaderProvider
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector.ExtensionPoint.selectSingleton
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin.Companion.onLoad
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.utils.DeprecatedSinceMirai
/**
* 表示一个扩展.
@ -37,7 +40,18 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin.Companion.onLoad
*
* @see ComponentStorage
*/
public interface Extension
public interface Extension {
/**
* 优先级. 越高越先使用. 内嵌的 [SpaceSeparatedCommandCallParser] 拥有优先级 0.
*
* 若两个 [InstanceExtension] 有相同的优先级, 将会优先使用内嵌的实现, 再按 [ComponentStorage.contribute] 顺序依次使用.
*
* @since 2.11
*/ // https://github.com/mamoe/mirai/issues/1860
@ConsoleExperimentalApi
public val priority: Int
get() = 0
}
/**
* 增加一些函数 (方法)的扩展
@ -51,6 +65,8 @@ public interface FunctionExtension : Extension
*
* @see PermissionServiceProvider
*/
@Deprecated("Please use InstanceExtension instead.", replaceWith = ReplaceWith("InstanceExtension"))
@DeprecatedSinceMirai(warningSince = "2.11")
public interface SingletonExtension<T> : Extension {
public val instance: T
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -12,13 +12,15 @@
package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.utils.DeprecatedSinceMirai
import kotlin.reflect.KClass
/**
* [Extension] 的伴生对象实现.
* 表示一个扩展接入点(扩展类型). Kotlin [Extension] 的伴生对象实现, Java 可通过静态字段提供.
*
* [注册扩展][ComponentStorage.contribute]时需要提供其 [ExtensionPoint], [ExtensionPoint] 也可以用于获取所有注册的扩展 ([ComponentStorage.getExtensions]).
*
* @see AbstractExtensionPoint
*/
@ -37,6 +39,8 @@ public abstract class AbstractExtensionPoint<T : Extension>(
/**
* 表示一个 [SingletonExtension] [ExtensionPoint]
*/
@Deprecated("Please use InstanceExtensionPoint instead.", replaceWith = ReplaceWith("InstanceExtensionPoint"))
@DeprecatedSinceMirai(warningSince = "2.11")
public interface SingletonExtensionPoint<T : SingletonExtension<*>> : ExtensionPoint<T>
/**
@ -55,34 +59,56 @@ public abstract class AbstractInstanceExtensionPoint<E : InstanceExtension<T>, T
* @since 2.10
*/
@ConsoleExperimentalApi
constructor(
extensionType: KClass<E>,
/**
* 内建的实现列表.
*/
@ConsoleExperimentalApi
public vararg val builtinImplementations: () -> E,
public constructor(
extensionType: KClass<E>
) : AbstractExtensionPoint<E>(extensionType) {
/**
* @since 2.10
*/
@Deprecated(
"Default(builtin) implementations are not allowed any more. " +
"For plugin authors, provide them with lower priority when plugin being loaded(through the ComponentScope). " +
"For frontend implementers, provide them by `BackendAccess.globalComponentScope.contribute`. ",
replaceWith = ReplaceWith("AbstractInstanceExtensionPoint(extensionType)"),
level = DeprecationLevel.ERROR,
)
@DeprecatedSinceMirai(errorSince = "2.11") // for removal
@ConsoleExperimentalApi
public constructor(extensionType: KClass<E>) : this(
extensionType,
builtinImplementations = arrayOf<() -> E>() as Array<out () -> E>
) // to avoid resolution ambiguity
public constructor(
extensionType: KClass<E>,
/**
* 内建的实现列表.
*/
builtinImplementations: () -> E,
) : this(extensionType)
/**
* @since 2.0
*/
@ConsoleExperimentalApi
@Deprecated(
"Default(builtin) implementations are not allowed any more. " +
"For plugin authors, provide them with lower priority when plugin being loaded(through the ComponentScope). " +
"For frontend implementers, provide them by `BackendAccess.globalComponentScope.contribute`. ",
replaceWith = ReplaceWith("AbstractInstanceExtensionPoint(extensionType)"),
level = DeprecationLevel.ERROR
)
@DeprecatedSinceMirai(errorSince = "2.11") // for removal
@ConsoleExperimentalApi // was experimental since 2.0
public constructor(extensionType: KClass<E>, vararg builtinImplementations: E) : this(
extensionType,
builtinImplementations = builtinImplementations.map { { it } }.toTypedArray()
)
}
@Deprecated(
"Please use AbstractInstanceExtensionPoint instead.",
replaceWith = ReplaceWith(
"AbstractInstanceExtension",
"net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint"
)
)
@DeprecatedSinceMirai(warningSince = "2.11")
@Suppress("DEPRECATION")
public abstract class AbstractSingletonExtensionPoint<E : SingletonExtension<T>, T>
/**
* @since 2.10
@ -111,12 +137,6 @@ constructor(
* [SingletonExtensionSelector] 选择后的实例.
*/
@ConsoleExperimentalApi
public open val selectedInstance: T by lazy {
GlobalComponentStorage.run {
this@AbstractSingletonExtensionPoint.findSingletonInstance(
extensionType,
builtinImplementation
)
}
}
public open val selectedInstance: T
get() = throw UnsupportedOperationException("SingletonExtension has been deprecated.")
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.utils.NotStableForInheritance
/**
* 一个已经注册的 [Extension]. 可通过 [ComponentStorage.getExtensions] 获得.
*
* @since 2.11
*/
@NotStableForInheritance
public interface ExtensionRegistry<E : Extension> {
/**
* 提供该 [ExtensionRegistry] 的插件. 若为 `null` 则表示由 Mirai Console 内置或者由前端实现.
*/
public val plugin: Plugin?
/**
* [Extension] 实例.
*/
public val extension: E
}
internal inline val <T> ExtensionRegistry<out InstanceExtension<T>>.instance get() = extension.instance
internal class ExtensionRegistryImpl<E : Extension> internal constructor(
/**
* 提供该 [ExtensionRegistry] 的插件. 若为 `null` 则表示由 Mirai Console 内置或者由前端实现.
*/
override val plugin: Plugin?,
/**
* [Extension] 实例.
*/
extensionInitializer: () -> E,
) : ExtensionRegistry<E> {
override val extension: E by lazy(extensionInitializer)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -13,19 +13,25 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.CommandCallInterceptor
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.utils.DeprecatedSinceMirai
@ExperimentalCommandDescriptors
public interface CommandCallInterceptorProvider : InstanceExtension<CommandCallInterceptor> {
@ExperimentalCommandDescriptors
public companion object ExtensionPoint :
AbstractInstanceExtensionPoint<CommandCallInterceptorProvider, CommandCallInterceptor>(
CommandCallInterceptorProvider::class
)
}
@Deprecated("Deprecated for removal. Please implement your own CommandCallInterceptorProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallInterceptorProviderImpl(override val instance: CommandCallInterceptor) :
CommandCallInterceptorProvider
@Deprecated("Deprecated for removal. Please implement your own CommandCallInterceptorProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallInterceptorProviderImplLazy(initializer: () -> CommandCallInterceptor) :
CommandCallInterceptorProvider {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -11,26 +11,27 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCallParser
import net.mamoe.mirai.console.command.parse.SpaceSeparatedCommandCallParser
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.utils.DeprecatedSinceMirai
/**
* The provider of [CommandCallParser]
*/
@ExperimentalCommandDescriptors
public interface CommandCallParserProvider : InstanceExtension<CommandCallParser> {
@ExperimentalCommandDescriptors
public companion object ExtensionPoint :
AbstractInstanceExtensionPoint<CommandCallParserProvider, CommandCallParser>(
CommandCallParserProvider::class,
SpaceSeparatedCommandCallParser.Provider
)
AbstractInstanceExtensionPoint<CommandCallParserProvider, CommandCallParser>(CommandCallParserProvider::class)
}
@Deprecated("Deprecated for removal. Please implement your own CommandCallParserProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallParserProviderImpl(override val instance: CommandCallParser) : CommandCallParserProvider
@Deprecated("Deprecated for removal. Please implement your own CommandCallParserProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallParserProviderImplLazy(initializer: () -> CommandCallParser) : CommandCallParserProvider {
override val instance: CommandCallParser by lazy(initializer)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -10,23 +10,26 @@
package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.BuiltInCommandCallResolver
import net.mamoe.mirai.console.command.resolve.CommandCallResolver
import net.mamoe.mirai.console.extension.AbstractSingletonExtensionPoint
import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.utils.DeprecatedSinceMirai
@ExperimentalCommandDescriptors
public interface CommandCallResolverProvider : SingletonExtension<CommandCallResolver> {
public interface CommandCallResolverProvider : InstanceExtension<CommandCallResolver> {
@ExperimentalCommandDescriptors
public companion object ExtensionPoint :
AbstractSingletonExtensionPoint<CommandCallResolverProvider, CommandCallResolver>(
CommandCallResolverProvider::class,
{ BuiltInCommandCallResolver }
)
AbstractInstanceExtensionPoint<CommandCallResolverProvider, CommandCallResolver>(CommandCallResolverProvider::class)
}
@Deprecated("Deprecated for removal. Please implement your own CommandCallResolverProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallResolverProviderImpl(override val instance: CommandCallResolver) : CommandCallResolverProvider
@Deprecated("Deprecated for removal. Please implement your own CommandCallResolverProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for removal.
@ExperimentalCommandDescriptors
public class CommandCallResolverProviderImplLazy(initializer: () -> CommandCallResolver) : CommandCallResolverProvider {
override val instance: CommandCallResolver by lazy(initializer)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -9,55 +9,35 @@
package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.extension.AbstractSingletonExtensionPoint
import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.utils.DeprecatedSinceMirai
/**
* [权限服务][PermissionService] 提供器.
*
* 当插件注册 [PermissionService] , 默认会使用插件的 [PermissionService].
*/
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
public interface PermissionServiceProvider : InstanceExtension<PermissionService<*>> {
public companion object ExtensionPoint :
AbstractSingletonExtensionPoint<PermissionServiceProvider, PermissionService<*>>(
PermissionServiceProvider::class,
{ BuiltInPermissionService() }
) {
internal var permissionServiceOk = false
@ConsoleExperimentalApi
public val providerPlugin: Plugin? by lazy {
GlobalComponentStorage.run {
val instance = PermissionService.INSTANCE
if (instance is BuiltInPermissionService) return@lazy null
PermissionServiceProvider.getExtensions().find { it.extension.instance === instance }?.plugin
}
}
@ConsoleExperimentalApi
override val selectedInstance: PermissionService<*>
get() {
if (!permissionServiceOk) {
error("PermissionService not yet loaded")
}
return super.selectedInstance
}
}
AbstractInstanceExtensionPoint<PermissionServiceProvider, PermissionService<*>>(PermissionServiceProvider::class)
// ! BREAKING CHANGE MADE IN 2.11: supertype changed from AbstractSingletonExtensionPoint to AbstractInstanceExtensionPoint
}
/**
* @see PermissionServiceProvider
*/
@Deprecated("Please implement your own PermissionServiceProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for hidden.
public class PermissionServiceProviderImpl(override val instance: PermissionService<*>) : PermissionServiceProvider
/**
* @see PermissionServiceProvider
*/
@Deprecated("Please implement your own PermissionServiceProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for hidden.
public class PermissionServiceProviderImplLazy(initializer: () -> PermissionService<*>) : PermissionServiceProvider {
override val instance: PermissionService<*> by lazy(initializer)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,6 +14,7 @@ import net.mamoe.mirai.console.extension.Extension
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.extension.PluginComponentStorage
import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.utils.DeprecatedSinceMirai
/**
* 提供扩展 [PluginLoader]
@ -30,8 +31,12 @@ public interface PluginLoaderProvider : InstanceExtension<PluginLoader<*, *>> {
public companion object ExtensionPoint : AbstractExtensionPoint<PluginLoaderProvider>(PluginLoaderProvider::class)
}
@Deprecated("Please implement your own PluginLoaderProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for hidden.
public class PluginLoaderProviderImpl(override val instance: PluginLoader<*, *>) : PluginLoaderProvider
@Deprecated("Please implement your own PluginLoaderProvider.")
@DeprecatedSinceMirai(warningSince = "2.11") // for hidden.
public class PluginLoaderProviderImplLazy(initializer: () -> PluginLoader<*, *>) : PluginLoaderProvider {
override val instance: PluginLoader<*, *> by lazy(initializer)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -7,18 +7,17 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("DEPRECATION")
package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.Extension
import net.mamoe.mirai.console.extension.FunctionExtension
import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.internal.extension.SingletonExtensionSelectorImpl
import net.mamoe.mirai.console.internal.extension.ExtensionRegistry
import net.mamoe.mirai.console.extension.*
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.extension.SingletonExtensionSelectorImpl
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.name
import net.mamoe.mirai.utils.DeprecatedSinceMirai
import net.mamoe.mirai.utils.info
import kotlin.reflect.KClass
@ -27,10 +26,20 @@ import kotlin.reflect.KClass
*
* 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器.
*/
@Deprecated(
"Order of extensions is now determined by its priority property since 2.11. SingletonExtensionSelector is not needed anymore. ",
level = DeprecationLevel.WARNING
)
@DeprecatedSinceMirai(warningSince = "2.11")
public interface SingletonExtensionSelector : FunctionExtension {
/**
* 表示一个插件注册的 [Extension]
*/
@Deprecated(
"Order of extensions is now determined by its priority property since 2.11. SingletonExtensionSelector is not needed anymore. ",
level = DeprecationLevel.WARNING
)
@DeprecatedSinceMirai(warningSince = "2.11")
public data class Registry<T : Extension>(
val plugin: Plugin?,
val extension: T,
@ -44,6 +53,11 @@ public interface SingletonExtensionSelector : FunctionExtension {
candidates: Collection<Registry<T>>,
): T?
@Deprecated(
"Order of extensions is now determined by its priority property since 2.11. SingletonExtensionSelector is not needed anymore. ",
level = DeprecationLevel.WARNING
)
@DeprecatedSinceMirai(warningSince = "2.11")
public companion object ExtensionPoint :
AbstractExtensionPoint<SingletonExtensionSelector>(SingletonExtensionSelector::class) {
@ -53,16 +67,22 @@ public interface SingletonExtensionSelector : FunctionExtension {
internal fun init() {
check(instanceField == null) { "Internal error: reinitialize SingletonExtensionSelector" }
val instances = GlobalComponentStorage.run { SingletonExtensionSelector.getExtensions() }
val instances = GlobalComponentStorage.getExtensions(ExtensionPoint).toList()
instanceField = when {
instances.isEmpty() -> SingletonExtensionSelectorImpl
instances.size == 1 -> {
instances.single().also { (plugin, ext) ->
MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: $ext from ${plugin?.name ?: "<builtin>"}" }
instances.single().also { registry ->
MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: ${registry.extension} from ${registry.plugin?.name ?: "<builtin>"}" }
}.extension
}
else -> {
error("Found too many SingletonExtensionSelectors: ${instances.joinToString { (p, i) -> "'$i' from '${p?.name ?: "<builtin>"}'" }}. Check your plugins and ensure there is only one external SingletonExtensionSelectors")
val hint = instances.joinToString { reg ->
"'${reg.extension}' from '${reg.plugin?.name ?: "<builtin>"}'"
}
error(
"Found too many SingletonExtensionSelectors: $hint. " +
"Check your plugins and ensure there is only one external SingletonExtensionSelectors"
)
}
}
}

View File

@ -22,9 +22,13 @@ import net.mamoe.mirai.console.MiraiConsoleImplementation
import net.mamoe.mirai.console.command.BuiltInCommands
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.SpaceSeparatedCommandCallParser
import net.mamoe.mirai.console.command.resolve.BuiltInCommandCallResolver
import net.mamoe.mirai.console.extensions.CommandCallParserProvider
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.extensions.PostStartupExtension
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.internal.command.CommandConfig
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.ConfigurationKey
@ -32,7 +36,7 @@ import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.Pa
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.PLAIN
import net.mamoe.mirai.console.internal.data.builtins.LoggerConfig
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.extension.SingletonExtensionSelectorImpl
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorageImpl
import net.mamoe.mirai.console.internal.logging.LoggerControllerImpl
import net.mamoe.mirai.console.internal.logging.MiraiConsoleLogger
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
@ -77,6 +81,9 @@ internal class MiraiConsoleImplementationBridge(
override val version: SemVersion by MiraiConsoleBuildConstants::version
override val pluginManager: PluginManagerImpl by lazy { PluginManagerImpl(coroutineContext) }
// used internally
val globalComponentStorage: GlobalComponentStorageImpl by lazy { GlobalComponentStorageImpl() }
override val mainLogger: MiraiLogger by lazy { createLogger("main") }
init {
@ -99,6 +106,15 @@ internal class MiraiConsoleImplementationBridge(
internal fun doStart() {
externalImplementation.preStart()
@OptIn(ExperimentalCommandDescriptors::class)
phase("register builtin componenets") {
GlobalComponentStorage.run {
contributeConsole(CommandCallParserProvider, SpaceSeparatedCommandCallParser.Provider)
contributeConsole(CommandCallResolverProvider, BuiltInCommandCallResolver.Provider)
contributeConsole(PermissionServiceProvider, BuiltInPermissionService.Provider())
}
}
phase("setup logger controller") {
if (loggerController === LoggerControllerImpl) {
// Reload LoggerConfig.
@ -159,25 +175,30 @@ internal class MiraiConsoleImplementationBridge(
mainLogger.verbose { "${PluginManager.plugins.size} plugin(s) loaded." }
}
phase("load SingletonExtensionSelector") {
SingletonExtensionSelector.init()
val instance = SingletonExtensionSelector.instance
if (instance is SingletonExtensionSelectorImpl) {
consoleDataScope.addAndReloadConfig(instance.config)
}
}
// phase("load SingletonExtensionSelector") {
// SingletonExtensionSelector.init()
// val instance = SingletonExtensionSelector.instance
// if (instance is SingletonExtensionSelectorImpl) {
// consoleDataScope.addAndReloadConfig(instance.config)
// }
// }
phase("load PermissionService") {
mainLogger.verbose { "Loading PermissionService..." }
PermissionServiceProvider.permissionServiceOk = true
PermissionService.INSTANCE.let { ps ->
if (ps is BuiltInPermissionService) {
consoleDataScope.addAndReloadConfig(ps.config)
mainLogger.verbose { "Reloaded PermissionService settings." }
} else {
mainLogger.info { "Loaded PermissionService from plugin ${PermissionServiceProvider.providerPlugin?.name}" }
mainLogger.info {
"Loaded PermissionService from plugin ${
GlobalComponentStorage.getPreferredExtension(
PermissionServiceProvider
).plugin?.name
}"
}
}
}
@ -258,9 +279,7 @@ internal class MiraiConsoleImplementationBridge(
}
phase("finally post") {
GlobalComponentStorage.run {
PostStartupExtension.useExtensions { it() } // exceptions thrown will be caught by caller of `doStart`.
}
globalComponentStorage.useEachExtensions(PostStartupExtension) { it.invoke() }
}
externalImplementation.postStart()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -10,140 +10,116 @@
package net.mamoe.mirai.console.internal.extension
import net.mamoe.mirai.console.extension.*
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector.ExtensionPoint.selectSingleton
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.name
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
import java.util.stream.Stream
import kotlin.contracts.contract
import kotlin.reflect.KClass
internal class GlobalComponentStorageImpl : AbstractConcurrentComponentStorage()
// source compatibility for <2.11
internal val GlobalComponentStorage get() = MiraiConsoleImplementationBridge.globalComponentStorage
/**
* The [ComponentStorage] containing all components provided by Mirai Console internals and installed plugins.
* thread-safe.
*/
internal object GlobalComponentStorage : AbstractConcurrentComponentStorage()
internal interface ExtensionRegistry<out E : Extension> {
val plugin: Plugin?
val extension: E
internal abstract class AbstractConcurrentComponentStorage : ComponentStorage, ComponentStorageInternal {
///////////////////////////////////////////////////////////////////////////
// registry implementation
///////////////////////////////////////////////////////////////////////////
operator fun component1(): Plugin? {
return this.plugin
}
/**
* For each [ExtensionPoint]. thread-safe.
*/
internal class Registries<T : Extension> {
@Volatile
private var data: MutableCollection<ExtensionRegistry<T>> = ArrayList()
private val lock = Any()
operator fun component2(): E {
return this.extension
}
}
internal class LazyExtensionRegistry<out E : Extension>(
override val plugin: Plugin?,
initializer: () -> E,
) : ExtensionRegistry<E> {
override val extension: E by lazy { initializer() }
}
internal data class DataExtensionRegistry<out E : Extension>(
override val plugin: Plugin?,
override val extension: E,
) : ExtensionRegistry<E>
internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
private val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap()
@Suppress("UNCHECKED_CAST")
internal fun <T : Extension> ExtensionPoint<out T>.getExtensions(): Set<ExtensionRegistry<T>> {
val userDefined = instances.getOrPut(this, ::CopyOnWriteArraySet) as Set<ExtensionRegistry<T>>
val builtins = if (this is AbstractInstanceExtensionPoint<*, *>) {
this.builtinImplementations.mapTo(HashSet()) { instance ->
DataExtensionRegistry(
null,
instance()
fun register(registry: ExtensionRegistry<T>) {
synchronized(lock) {
val list = PriorityQueue(
data.size + 1,
Comparator.comparing<ExtensionRegistry<T>, Int> { it.extension.priority }.reversed()
)
} as Set<ExtensionRegistry<T>>
} else null
return builtins?.plus(userDefined) ?: userDefined
}
// unused for now
internal fun removeExtensionsRegisteredByPlugin(plugin: Plugin) {
instances.forEach { (_, u) ->
u.removeAll { it.plugin == plugin }
list.addAll(data)
list.add(registry)
data = list
}
}
/**
* @return thread-safe Sequence
*/
fun asSequence(): Sequence<ExtensionRegistry<T>> = data.asSequence()
/**
* @return thread-safe Sequence
*/
fun asStream(): Stream<ExtensionRegistry<T>> = data.stream()
}
private val registries: MutableMap<ExtensionPoint<*>, Registries<*>> = ConcurrentHashMap()
private fun <T : Extension> getRegistries(ep: ExtensionPoint<T>): Registries<T> {
@Suppress("UNCHECKED_CAST")
return registries.getOrPut(ep) { Registries<T>() } as Registries<T>
}
private fun <T : Extension> registerExtension(ep: ExtensionPoint<T>, registry: ExtensionRegistry<T>) {
getRegistries(ep).register(registry)
}
///////////////////////////////////////////////////////////////////////////
// public API implementation
///////////////////////////////////////////////////////////////////////////
internal fun mergeWith(another: AbstractConcurrentComponentStorage) {
for ((ep, list) in another.instances) {
for (extensionRegistry in list) {
another.registries.forEach { (ep, registries) ->
for (extensionRegistry in registries.asSequence()) {
@Suppress("UNCHECKED_CAST")
ep as ExtensionPoint<Extension>
this.contribute(ep, extensionRegistry.plugin, lazyInstance = { extensionRegistry.extension })
registerExtension(ep, ExtensionRegistryImpl(extensionRegistry.plugin) { extensionRegistry.extension })
}
}
}
internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.() -> Unit) {
return withExtensions { _ -> block() }
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution
internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.(plugin: Plugin?) -> Unit) {
contract {
callsInPlace(block)
}
for ((plugin, extension) in this.getExtensions()) {
internal inline fun <T : Extension> useEachExtensions(
extensionPoint: ExtensionPoint<T>,
block: ExtensionRegistry<T>.(instance: T) -> Unit
) {
contract { callsInPlace(block) }
getExtensions(extensionPoint).forEach { registry ->
val plugin = registry.plugin
val extension = registry.extension
kotlin.runCatching {
block.invoke(extension, plugin)
block.invoke(registry, registry.extension)
}.getOrElse { throwable ->
throwExtensionException(extension, plugin, throwable)
extensionPoint.throwExtensionException(extension, plugin, throwable)
}
}
}
internal inline fun <reified E : SingletonExtension<*>> ExtensionPoint<out E>.findSingleton(builtin: E): E =
findSingleton(E::class, builtin)
internal fun <E : SingletonExtension<*>> ExtensionPoint<out E>.findSingleton(type: KClass<E>, builtin: E): E {
val candidates = this.getExtensions()
return when (candidates.size) {
0 -> builtin
1 -> candidates.single().extension
else -> SingletonExtensionSelector.instance.selectSingleton(type, candidates) ?: builtin
}
}
internal inline fun <reified E : SingletonExtension<T>, T> ExtensionPoint<out E>.findSingletonInstance(noinline default: () -> T): T =
findSingletonInstance(E::class, default)
internal fun <E : SingletonExtension<T>, T> ExtensionPoint<out E>.findSingletonInstance(
type: KClass<E>,
default: () -> T,
): T {
val candidates = this.getExtensions()
return when (candidates.size) {
0 -> default()
1 -> candidates.single().extension.instance
else -> SingletonExtensionSelector.instance.selectSingleton(type, candidates)?.instance ?: default()
}
}
internal inline fun <T : Extension, E> ExtensionPoint<out T>.foldExtensions(
internal inline fun <T : Extension, E> foldExtensions(
extensionPoint: ExtensionPoint<T>,
initial: E,
block: (acc: E, extension: T) -> E,
): E {
contract {
callsInPlace(block)
}
contract { callsInPlace(block) }
var e: E = initial
for ((plugin, extension) in this.getExtensions()) {
getExtensions(extensionPoint).forEach { registry ->
val plugin = registry.plugin
val extension = registry.extension
kotlin.runCatching {
e = block.invoke(e, extension)
}.getOrElse { throwable ->
throwExtensionException(extension, plugin, throwable)
extensionPoint.throwExtensionException(extension, plugin, throwable)
}
}
return e
@ -153,52 +129,38 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
extension: T,
plugin: Plugin?,
throwable: Throwable,
) {
): Nothing {
throw ExtensionException(
"Exception while executing extension '${extension.kClassQualifiedNameOrTip}' provided by plugin '${plugin?.name ?: "<builtin>"}', registered for '${this.extensionType.qualifiedName}'",
throwable
)
}
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T) -> Unit): Unit =
withExtensions(block)
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin?) -> Unit): Unit =
withExtensions(block)
override fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
override fun <E : Extension> contribute(
extensionPoint: ExtensionPoint<E>,
plugin: Plugin,
extensionInstance: T,
extensionInstance: E,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance))
registerExtension(extensionPoint, ExtensionRegistryImpl(plugin) { extensionInstance })
}
@JvmName("contribute1")
fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
plugin: Plugin?,
extensionInstance: T,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance))
override fun <E : Extension> contributeConsole(extensionPoint: ExtensionPoint<E>, lazyInstance: () -> E) {
registerExtension(extensionPoint, ExtensionRegistryImpl(null, lazyInstance))
}
override fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
override fun <E : Extension> contribute(
extensionPoint: ExtensionPoint<E>,
plugin: Plugin,
lazyInstance: () -> T,
lazyInstance: () -> E,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance))
registerExtension(extensionPoint, ExtensionRegistryImpl(plugin, lazyInstance))
}
@JvmName("contribute1")
fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
plugin: Plugin?,
lazyInstance: () -> T,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance))
override fun <E : Extension> getExtensions(extensionPoint: ExtensionPoint<E>): Sequence<ExtensionRegistry<E>> {
return getRegistries(extensionPoint).asSequence()
}
override fun <E : Extension> getExtensionsStream(extensionPoint: ExtensionPoint<E>): Stream<ExtensionRegistry<E>> {
return getRegistries(extensionPoint).asStream()
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -18,9 +18,16 @@ import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
import net.mamoe.mirai.console.plugin.name
import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.utils.DeprecatedSinceMirai
import net.mamoe.mirai.utils.info
import kotlin.reflect.KClass
@Suppress("DEPRECATION")
@Deprecated(
"Order of extensions is not determined by its priority property since 2.11. SingletonExtensionSelector is not needed anymore. ",
level = DeprecationLevel.WARNING
)
@DeprecatedSinceMirai(warningSince = "2.11")
internal object SingletonExtensionSelectorImpl : SingletonExtensionSelector {
internal val config: SaveData = SaveData()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,6 +14,7 @@ import net.mamoe.mirai.console.data.AutoSavePluginConfig
import net.mamoe.mirai.console.data.PluginDataExtensions
import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault
import net.mamoe.mirai.console.data.value
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.permission.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
@ -100,6 +101,12 @@ internal class AllDenyPermissionService : PermissionService<PermissionImpl> {
internal class BuiltInPermissionService : AbstractConcurrentPermissionService<PermissionImpl>(),
PermissionService<PermissionImpl> {
class Provider : PermissionServiceProvider {
override val instance: PermissionService<*> by lazy {
BuiltInPermissionService()
}
}
override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class
override val permissions: ConcurrentHashMap<PermissionId, PermissionImpl> = ConcurrentHashMap()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -160,12 +160,10 @@ internal class PluginManagerImpl(
internal fun initExternalPluginLoaders(): Int {
var count = 0
GlobalComponentStorage.run {
PluginLoaderProvider.useExtensions { ext, plugin ->
logger.info { "Loaded PluginLoader ${ext.instance} from ${plugin?.name ?: "<builtin>"}" }
_pluginLoaders.add(ext.instance)
count++
}
GlobalComponentStorage.useEachExtensions(PluginLoaderProvider) {
logger.info { "Loaded PluginLoader ${extension.instance} from ${plugin?.name ?: "<builtin>"}" }
_pluginLoaders.add(extension.instance)
count++
}
return count
}

View File

@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "unused", "MemberVisibilityCanBePrivate")
@ -13,7 +13,9 @@ package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.extension.instance
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.permission.checkType
import net.mamoe.mirai.console.permission.Permission.Companion.parentsWithSelf
import net.mamoe.mirai.console.plugin.Plugin
@ -140,14 +142,12 @@ public interface PermissionService<P : Permission> {
public companion object {
/**
* [PermissionService] 实例
*
* @see PermissionServiceProvider.selectedInstance
* 选用的 [PermissionService] 实例.
*/
@get:JvmName("getInstance")
@JvmStatic
public val INSTANCE: PermissionService<out Permission>
get() = PermissionServiceProvider.selectedInstance
get() = GlobalComponentStorage.getPreferredExtension(PermissionServiceProvider).instance
/**
* 获取一个权限, 失败时抛出 [NoSuchElementException]

View File

@ -0,0 +1,80 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorageImpl
import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
internal class GlobalComponentStorageTest : AbstractConsoleInstanceTest() {
class MyInstance
class MyExtension(override val instance: MyInstance, override val priority: Int) : InstanceExtension<MyInstance> {
companion object EP : AbstractInstanceExtensionPoint<MyExtension, MyInstance>(MyExtension::class)
}
@Test
fun `can register`() {
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 1))
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 2))
GlobalComponentStorage.getExtensionsList(MyExtension).run {
assertEquals(2, size)
}
}
@Test
fun `can contribute`() {
GlobalComponentStorage.contribute(MyExtension, mockPlugin, MyExtension(MyInstance(), 1))
GlobalComponentStorage.contribute(MyExtension, mockPlugin, MyExtension(MyInstance(), 2))
GlobalComponentStorage.getExtensionsList(MyExtension).run {
assertEquals(2, size)
}
}
@Test
fun `can sort by priority`() {
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 1))
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 2))
GlobalComponentStorage.getExtensionsList(MyExtension).run {
assertEquals(2, size)
assertEquals(2, first().extension.priority)
assertEquals(1, get(1).extension.priority)
}
}
@Test
fun `can sort by priority 2`() {
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 2))
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 1))
GlobalComponentStorage.getExtensionsList(MyExtension).run {
assertEquals(2, size)
assertEquals(2, first().extension.priority)
assertEquals(1, get(1).extension.priority)
}
}
@Test
fun `can fold`() {
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 2))
GlobalComponentStorage.contributeConsole(MyExtension, MyExtension(MyInstance(), 1))
val list = GlobalComponentStorage.foldExtensions(MyExtension, listOf<MyExtension>()) { acc, extension ->
acc + extension
}
assertEquals(2, list.size)
assertEquals(2, list.first().priority)
assertEquals(1, list[1].priority)
}
}
private fun <T : Extension> GlobalComponentStorageImpl.getExtensionsList(ep: ExtensionPoint<T>): List<ExtensionRegistry<T>> {
return getExtensions(ep).toList()
}