Code Review

- Add comments.
- Use when not if.
- Add docs
This commit is contained in:
Karlatemp 2020-09-13 12:39:46 +08:00
parent 1901cbec5e
commit aa032a145f
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
5 changed files with 170 additions and 55 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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
} }
} }
} }

View File

@ -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)
}
} }

View File

@ -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 不提供热加载和热卸载功能,所有插件只能在服务器启动前加载,在服务器结束时卸载。([为什么不支持热加载和卸载插件?]