mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-26 16:10:11 +08:00
Code Review
- Add comments. - Use when not if. - Add docs
This commit is contained in:
parent
1901cbec5e
commit
aa032a145f
@ -42,7 +42,7 @@ internal object BuiltInJvmPluginLoaderImpl :
|
|||||||
override val dataStorage: PluginDataStorage
|
override val dataStorage: PluginDataStorage
|
||||||
get() = MiraiConsoleImplementationBridge.dataStorageForJvmPluginLoader
|
get() = MiraiConsoleImplementationBridge.dataStorageForJvmPluginLoader
|
||||||
|
|
||||||
internal val classLoaders: ConcurrentLinkedQueue<JvmPluginClassLoader> = ConcurrentLinkedQueue()
|
internal val classLoaders: MutableList<JvmPluginClassLoader> = mutableListOf()
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // doesn't matter
|
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // doesn't matter
|
||||||
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
||||||
@ -53,7 +53,7 @@ internal object BuiltInJvmPluginLoaderImpl :
|
|||||||
ensureActive()
|
ensureActive()
|
||||||
|
|
||||||
fun Sequence<Map.Entry<File, JvmPluginClassLoader>>.findAllInstances(): Sequence<Map.Entry<File, JvmPlugin>> {
|
fun Sequence<Map.Entry<File, JvmPluginClassLoader>>.findAllInstances(): Sequence<Map.Entry<File, JvmPlugin>> {
|
||||||
return map { (f, pluginClassLoader) ->
|
return onEach { (_, pluginClassLoader) ->
|
||||||
val exportManagers = pluginClassLoader.findServices(
|
val exportManagers = pluginClassLoader.findServices(
|
||||||
ExportManager::class
|
ExportManager::class
|
||||||
).loadAllServices()
|
).loadAllServices()
|
||||||
@ -67,6 +67,7 @@ internal object BuiltInJvmPluginLoaderImpl :
|
|||||||
} else {
|
} else {
|
||||||
pluginClassLoader.declaredFilter = exportManagers[0]
|
pluginClassLoader.declaredFilter = exportManagers[0]
|
||||||
}
|
}
|
||||||
|
}.map { (f, pluginClassLoader) ->
|
||||||
f to (pluginClassLoader.findServices(
|
f to (pluginClassLoader.findServices(
|
||||||
JvmPlugin::class,
|
JvmPlugin::class,
|
||||||
KotlinPlugin::class,
|
KotlinPlugin::class,
|
||||||
|
@ -26,61 +26,46 @@ internal class ExportManagerImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
fun parse(lines: Iterator<String>): ExportManagerImpl {
|
fun parse(lines: Iterator<String>): ExportManagerImpl {
|
||||||
val rules = ArrayList<(String) -> Boolean?>()
|
val rules = ArrayList<(String) -> Boolean?>()
|
||||||
lines.forEach { line ->
|
lines.asSequence().map { it.trim() }.filter { it.isNotBlank() }.filterNot {
|
||||||
val trimed = line.trim()
|
it[0] == '#'
|
||||||
if (trimed.isEmpty()) return@forEach
|
}.forEach { line ->
|
||||||
if (trimed[0] == '#') return@forEach
|
val command = line.substringBefore(' ')
|
||||||
val command: String
|
val argument = line.substringAfter(' ', missingDelimiterValue = "").trim()
|
||||||
val argument: String
|
|
||||||
kotlin.run {
|
|
||||||
val splitter = trimed.indexOf(' ')
|
|
||||||
if (splitter == -1) {
|
|
||||||
command = trimed
|
|
||||||
argument = ""
|
|
||||||
} else {
|
|
||||||
command = trimed.substring(0, splitter)
|
|
||||||
argument = trimed.substring(splitter + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when (command) {
|
when (command) {
|
||||||
"export" -> {
|
"export" -> {
|
||||||
if (argument.isBlank()) {
|
when {
|
||||||
rules.add { true }
|
// export-all
|
||||||
} else {
|
argument.isBlank() -> rules.add { true }
|
||||||
if (argument.endsWith(".")) {
|
// export package
|
||||||
rules.add {
|
argument.endsWith(".") -> rules.add {
|
||||||
if (it.startsWith(argument)) true else null
|
if (it.startsWith(argument)) true else null
|
||||||
}
|
}
|
||||||
} else {
|
// export class
|
||||||
rules.add {
|
else -> rules.add {
|
||||||
if (it == argument) true else null
|
if (it == argument) true else null
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"deny", "internal", "hidden" -> {
|
"deny", "internal", "hidden" -> {
|
||||||
if (argument.isBlank()) {
|
when {
|
||||||
rules.add { false }
|
// deny-all
|
||||||
} else {
|
argument.isBlank() -> rules.add { false }
|
||||||
if (argument.endsWith(".")) {
|
// deny package
|
||||||
rules.add {
|
argument.endsWith(".") -> rules.add {
|
||||||
if (it.startsWith(argument)) false else null
|
if (it.startsWith(argument)) false else null
|
||||||
}
|
}
|
||||||
} else {
|
// deny class
|
||||||
rules.add {
|
else -> rules.add {
|
||||||
if (it == argument) false else null
|
if (it == argument) false else null
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"export-all" -> {
|
"export-all" -> rules.add { true }
|
||||||
rules.add { true }
|
"deny-all", "hidden-all" -> rules.add { false }
|
||||||
}
|
|
||||||
"deny-all", "hidden-all" -> {
|
|
||||||
rules.add { false }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ExportManagerImpl(rules)
|
return ExportManagerImpl(rules)
|
||||||
|
@ -50,6 +50,7 @@ internal class JvmPluginClassLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun findClass(name: String, disableGlobal: Boolean): Class<*>? {
|
internal fun findClass(name: String, disableGlobal: Boolean): Class<*>? {
|
||||||
|
// First. Try direct load in cache.
|
||||||
val cachedClass = cache[name]
|
val cachedClass = cache[name]
|
||||||
if (cachedClass != null) {
|
if (cachedClass != null) {
|
||||||
if (disableGlobal) {
|
if (disableGlobal) {
|
||||||
@ -60,19 +61,30 @@ internal class JvmPluginClassLoader(
|
|||||||
}
|
}
|
||||||
return cachedClass
|
return cachedClass
|
||||||
}
|
}
|
||||||
if (disableGlobal)
|
if (disableGlobal) {
|
||||||
|
// ==== Process Loading Request From JvmPluginClassLoader ====
|
||||||
|
//
|
||||||
|
// If load from other classloader,
|
||||||
|
// means no other loaders are cached.
|
||||||
|
// direct load
|
||||||
return kotlin.runCatching {
|
return kotlin.runCatching {
|
||||||
super.findClass(name).also { cache[name] = it }
|
super.findClass(name).also { cache[name] = it }
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
if (it is ClassNotFoundException) null
|
if (it is ClassNotFoundException) null
|
||||||
else throw it
|
else throw it
|
||||||
}?.also {
|
}?.also {
|
||||||
|
// This request is from other classloader,
|
||||||
|
// so we need to check the class is exported or not.
|
||||||
val filter = declaredFilter
|
val filter = declaredFilter
|
||||||
if (filter != null && !filter.isExported(name)) {
|
if (filter != null && !filter.isExported(name)) {
|
||||||
throw LoadingDeniedException(name)
|
throw LoadingDeniedException(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== Process Loading Request From JDK ClassLoading System ====
|
||||||
|
|
||||||
|
// First. scan other classLoaders's caches
|
||||||
classLoaders.forEach { otherClassloader ->
|
classLoaders.forEach { otherClassloader ->
|
||||||
if (otherClassloader === this) return@forEach
|
if (otherClassloader === this) return@forEach
|
||||||
val filter = otherClassloader.declaredFilter
|
val filter = otherClassloader.declaredFilter
|
||||||
@ -83,20 +95,26 @@ internal class JvmPluginClassLoader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return kotlin.runCatching { super.findClass(name).also { cache[name] = it } }.getOrElse {
|
// If no cache...
|
||||||
if (it is ClassNotFoundException) {
|
return kotlin.runCatching {
|
||||||
|
// Try load this class direct....
|
||||||
|
super.findClass(name).also { cache[name] = it }
|
||||||
|
}.getOrElse { exception ->
|
||||||
|
if (exception is ClassNotFoundException) {
|
||||||
|
// Cannot load the class from this, try others.
|
||||||
classLoaders.forEach { otherClassloader ->
|
classLoaders.forEach { otherClassloader ->
|
||||||
if (otherClassloader === this) return@forEach
|
if (otherClassloader === this) return@forEach
|
||||||
val other = kotlin.runCatching {
|
val other = kotlin.runCatching {
|
||||||
otherClassloader.findClass(name, true)
|
otherClassloader.findClass(name, true)
|
||||||
}.getOrElse { err ->
|
}.onFailure { err ->
|
||||||
if (err is LoadingDeniedException || err !is ClassNotFoundException) throw it
|
if (err is LoadingDeniedException || err !is ClassNotFoundException)
|
||||||
null
|
throw err
|
||||||
}
|
}.getOrNull()
|
||||||
if (other != null) return other
|
if (other != null) return other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw it
|
// Great, nobody known what is the class.
|
||||||
|
throw exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,61 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.plugin.jvm
|
package net.mamoe.mirai.console.plugin.jvm
|
||||||
|
|
||||||
public fun interface ExportManager {
|
import net.mamoe.mirai.console.internal.plugin.ExportManagerImpl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件的类导出管理器
|
||||||
|
*
|
||||||
|
* 我们允许插件将一些内部实现hidden起来, 避免其他插件调用, 要启动这个特性,
|
||||||
|
* 只需要在你的 resources 文件夹创建名为 `export-rules.txt` 的规则文件,便可以控制插件的类的公开规则
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```text
|
||||||
|
*
|
||||||
|
* # #开头的行我们都识别为注释, 你可以在规则文件里面写很多注释
|
||||||
|
*
|
||||||
|
* # export 运行插件访问一个类, 或者一个包
|
||||||
|
*
|
||||||
|
* # 导出了一个internal包的一个类
|
||||||
|
* export org.example.miraiconsole.myplugin.internal.OpenInternal
|
||||||
|
*
|
||||||
|
* # 导出了整个 api 包, 导出包和导出类的区别就是末尾是否存在 . 号
|
||||||
|
* export org.example.miraiconsole.myplugin.api.
|
||||||
|
*
|
||||||
|
* # deny, 不允许其他插件使用这个包, 要隐藏一个包的时候, 注意不要忘记最后的 . 号
|
||||||
|
* #
|
||||||
|
* # 别名: hidden, internal
|
||||||
|
* deny org.example.miraiconsole.myplugin.internal.
|
||||||
|
*
|
||||||
|
* # 这条规则不会生效, 因为在这条规则前已经被上面的 deny 给隐藏了
|
||||||
|
* export org.example.miraiconsole.myplugin.internal.NotOpenInternal
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # export-all, 导出全部内容, 当然在此规则之前的deny依然会生效
|
||||||
|
* # 使用此规则会同时让此规则后的所有规则全部失效
|
||||||
|
* # export-all
|
||||||
|
*
|
||||||
|
* # 拒绝其他插件使用任何类, 除了之前已经explort的
|
||||||
|
* # 此规则会导致后面的所有规则全部失效
|
||||||
|
* deny-all
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 插件也可以通过 Service 来自定义导出控制
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* @AutoService(ExportManager::class)
|
||||||
|
* object MyExportManager: ExportManager {
|
||||||
|
* override fun isExported(className: String): Boolean {
|
||||||
|
* println(" <== $className")
|
||||||
|
* return true
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ExportManager {
|
||||||
public fun isExported(className: String): Boolean
|
public fun isExported(className: String): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,4 +75,9 @@ public object StandardExportManagers {
|
|||||||
public object AllDenied : ExportManager {
|
public object AllDenied : ExportManager {
|
||||||
override fun isExported(className: String): Boolean = false
|
override fun isExported(className: String): Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
public fun parse(lines: Iterator<String>): ExportManager {
|
||||||
|
return ExportManagerImpl.parse(lines)
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,6 +16,8 @@
|
|||||||
[`PluginConfig`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt
|
[`PluginConfig`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt
|
||||||
[`PluginDataStorage`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt
|
[`PluginDataStorage`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt
|
||||||
|
|
||||||
|
[`ExportManager`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/ExportManager.kt
|
||||||
|
|
||||||
[`MiraiConsole`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
|
[`MiraiConsole`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
|
||||||
[`MiraiConsoleImplementation`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt
|
[`MiraiConsoleImplementation`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt
|
||||||
<!--[MiraiConsoleFrontEnd]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt-->
|
<!--[MiraiConsoleFrontEnd]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt-->
|
||||||
@ -160,6 +162,56 @@ public final class JExample extends JavaPlugin {
|
|||||||
多个插件的加载是*顺序的*,意味着若一个插件的 `onLoad()` 等回调处理缓慢,后续插件的加载也会被延后,即使它们可能没有依赖关系。
|
多个插件的加载是*顺序的*,意味着若一个插件的 `onLoad()` 等回调处理缓慢,后续插件的加载也会被延后,即使它们可能没有依赖关系。
|
||||||
因此请尽量让 `onLoad()`,`onEnable()`,`onDisable()`快速返回。
|
因此请尽量让 `onLoad()`,`onEnable()`,`onDisable()`快速返回。
|
||||||
|
|
||||||
|
### API 导出管理
|
||||||
|
|
||||||
|
我们允许插件将一些内部实现hidden起来, 避免其他插件调用, 要启动这个特性,
|
||||||
|
只需要在你的 resources 文件夹创建名为 `export-rules.txt` 的规则文件,便可以控制插件的类的公开规则
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```text
|
||||||
|
|
||||||
|
# #开头的行我们都识别为注释, 你可以在规则文件里面写很多注释
|
||||||
|
|
||||||
|
# export 运行插件访问一个类, 或者一个包
|
||||||
|
|
||||||
|
# 导出了一个internal包的一个类
|
||||||
|
export org.example.miraiconsole.myplugin.internal.OpenInternal
|
||||||
|
|
||||||
|
# 导出了整个 api 包, 导出包和导出类的区别就是末尾是否存在 . 号
|
||||||
|
export org.example.miraiconsole.myplugin.api.
|
||||||
|
|
||||||
|
# deny, 不允许其他插件使用这个包, 要隐藏一个包的时候, 注意不要忘记最后的 . 号
|
||||||
|
#
|
||||||
|
# 别名: hidden, internal
|
||||||
|
deny org.example.miraiconsole.myplugin.internal.
|
||||||
|
|
||||||
|
# 这条规则不会生效, 因为在这条规则前已经被上面的 deny 给隐藏了
|
||||||
|
export org.example.miraiconsole.myplugin.internal.NotOpenInternal
|
||||||
|
|
||||||
|
|
||||||
|
# export-all, 导出全部内容, 当然在此规则之前的deny依然会生效
|
||||||
|
# 使用此规则会同时让此规则后的所有规则全部失效
|
||||||
|
# export-all
|
||||||
|
|
||||||
|
# 拒绝其他插件使用任何类, 除了之前已经explort的
|
||||||
|
# 此规则会导致后面的所有规则全部失效
|
||||||
|
deny-all
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
插件也可以通过 Service 来自定义导出控制
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```kotlin
|
||||||
|
@AutoService(ExportManager::class)
|
||||||
|
object MyExportManager: ExportManager {
|
||||||
|
override fun isExported(className: String): Boolean {
|
||||||
|
println(" <== $className")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 插件生命周期
|
### 插件生命周期
|
||||||
|
|
||||||
Mirai Console 不提供热加载和热卸载功能,所有插件只能在服务器启动前加载,在服务器结束时卸载。([为什么不支持热加载和卸载插件?])
|
Mirai Console 不提供热加载和热卸载功能,所有插件只能在服务器启动前加载,在服务器结束时卸载。([为什么不支持热加载和卸载插件?])
|
||||||
|
Loading…
Reference in New Issue
Block a user