修复JvmPlugin无法通过自己的classLoader以getResource的方法获取全局依赖的class文件 (#2536)

* [console] Fixed classLoaders of JvmPlugin cannot getResource(classes of global dependencies), fix #2535

* [core/utils/jvm] new fun: addAll(Enumeration)

* [console] modify DynLibClassLoader.tryFastOrStrictResolveResource to tryFastOrStrictResolveResources

* [console] remove allClassesAsResources field

* [console] add var to control whether class files of mirai dependencies could be load as resources

* [console/integration-test] modify loader test

* [console] dump new apis

* [console] rename JvmPluginClasspath.openMiraiDependenciesClassResource and standardize its KDoc

* [console] modify the KDoc of JvmPluginClasspath.shouldResolveConsoleSystemResource

* [console/integration-test] improve resources loading test cases

* [console/integration-test] modify assert to assertTrue
This commit is contained in:
ArgonarioD 2023-03-12 22:37:47 +08:00 committed by GitHub
parent 3ff2737b3c
commit 4f0f733f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 8 deletions

View File

@ -17,6 +17,7 @@ import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
internal class PS : ServiceTypedef
@ -64,7 +65,45 @@ internal object PMain : KotlinPlugin(JvmPluginDescription("net.mamoe.console.ite
println(it)
}.size
)
assertNull(javaClass.getResource("/net/mamoe/mirai/console/MiraiConsole.class"))
assertNull(javaClass.getResource("/net/mamoe/mirai/Bot.class"))
// ************************* resources loading tests *************************
val miraiConsoleClassPath = "net/mamoe/mirai/console/MiraiConsole.class"
val miraiBotClassPath = "net/mamoe/mirai/Bot.class"
val allClassesPath = "META-INF/mirai-console/allclasses.txt"
fun ClassLoader.getSizeOfResources(path: String): Int {
return this.getResources(path).toList().size
}
assertNull(javaClass.getResource("/$miraiConsoleClassPath"))
assertNull(javaClass.classLoader.getResource(miraiConsoleClassPath))
val miraiConsoleClassResourceCount = javaClass.classLoader.getSizeOfResources(miraiConsoleClassPath)
.also { assertEquals(0, it) }
assertNull(javaClass.getResource("/$miraiBotClassPath"))
assertNull(javaClass.classLoader.getResource(miraiBotClassPath))
val allClassesResourceCount = javaClass.classLoader.getSizeOfResources(allClassesPath)
.also { assertEquals(0, it) }
jvmPluginClasspath.shouldResolveConsoleSystemResource = true
assertNotNull(javaClass.classLoader.getResource(miraiConsoleClassPath))
assertNotNull(javaClass.classLoader.getResource(miraiBotClassPath))
assertTrue(javaClass.classLoader.getSizeOfResources(miraiConsoleClassPath) > miraiConsoleClassResourceCount)
assertTrue(javaClass.classLoader.getSizeOfResources(allClassesPath) > allClassesResourceCount)
jvmPluginClasspath.shouldResolveConsoleSystemResource = false
assertNull(javaClass.getResource("/$miraiConsoleClassPath"))
assertNull(javaClass.classLoader.getResource(miraiConsoleClassPath))
assertEquals(0, javaClass.classLoader.getSizeOfResources(miraiConsoleClassPath))
assertNull(javaClass.getResource("/$miraiBotClassPath"))
assertNull(javaClass.classLoader.getResource(miraiBotClassPath))
assertEquals(0, javaClass.classLoader.getSizeOfResources(allClassesPath))
}
}

View File

@ -2023,6 +2023,8 @@ public abstract interface class net/mamoe/mirai/console/plugin/jvm/JvmPluginClas
public abstract fun getPluginFile ()Ljava/io/File;
public abstract fun getPluginIndependentLibrariesClassLoader ()Ljava/lang/ClassLoader;
public abstract fun getPluginSharedLibrariesClassLoader ()Ljava/lang/ClassLoader;
public abstract fun getShouldResolveConsoleSystemResource ()Z
public abstract fun setShouldResolveConsoleSystemResource (Z)V
}
public abstract interface class net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription : net/mamoe/mirai/console/plugin/description/PluginDescription {

View File

@ -22,6 +22,7 @@ import java.net.URLClassLoader
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.ZipFile
import kotlin.collections.LinkedHashSet
/*
Class resolving:
@ -146,6 +147,29 @@ internal class DynLibClassLoader : DynamicClasspathClassLoader {
}
return null
}
fun tryFastOrStrictResolveResources(name: String): Enumeration<URL> {
if (name.startsWith("java/")) return JavaSystemPlatformClassLoader.getResources(name)
// All mirai-core hard-linked should use same version to avoid errors (ClassCastException).
val fromDependencies = AllDependenciesClassesHolder.appClassLoader.getResources(name)
return if (
name.startsWith("net/mamoe/mirai/")
|| name.startsWith("kotlin/")
|| name.startsWith("kotlinx/")
|| name.startsWith("org/slf4j/")
) { // Avoid plugin classing cheating
fromDependencies
} else {
LinkedHashSet<URL>().apply {
addAll(fromDependencies)
addAll(JavaSystemPlatformClassLoader.getResources(name))
}.let {
Collections.enumeration(it)
}
}
}
}
internal fun loadClassInThisClassLoader(name: String): Class<*>? {
@ -458,13 +482,12 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
}
src.add(pluginIndependentCL.getResources(name))
val resolved = mutableListOf<URL>()
src.forEach { nested ->
nested.iterator().forEach { url ->
if (url !in resolved)
resolved.add(url)
}
val resolved = LinkedHashSet<URL>()
if (openaccess.shouldResolveConsoleSystemResource) {
DynLibClassLoader.tryFastOrStrictResolveResources(name).let { resolved.addAll(it) }
}
src.forEach { nested -> resolved.addAll(nested) }
return Collections.enumeration(resolved)
}
@ -489,6 +512,12 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
if (name.startsWith("META-INF/services/net.mamoe.mirai.console.plugin."))
return findResource(name)
if (openaccess.shouldResolveConsoleSystemResource) {
DynLibClassLoader.tryFastOrStrictResolveResources(name)
.takeIf { it.hasMoreElements() }
?.let { return it.nextElement() }
}
findResource(name)?.let { return it }
// parent: ctx.sharedLibrariesLoader
sharedLibrariesLogger.getResource(name)?.let { return it }
@ -514,6 +543,8 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
override val pluginIndependentLibrariesClassLoader: ClassLoader
get() = pluginIndependentCL
override var shouldResolveConsoleSystemResource: Boolean = false
private val permitted by lazy {
arrayOf(
this@JvmPluginClassLoaderN,

View File

@ -41,6 +41,13 @@ public interface JvmPluginClasspath {
*/
public val pluginIndependentLibrariesClassLoader: ClassLoader
/**
* [pluginClassLoader] 是否可以通过 [ClassLoader.getResource] 获取 Mirai Console (包括依赖) 的相关资源
*
* 默认为 `false`
*/
public var shouldResolveConsoleSystemResource: Boolean
/**
* [file] 加入 [classLoader] 的搜索路径内
*

View File

@ -54,4 +54,18 @@ public actual fun <K : Enum<K>, V> EnumMap(clazz: KClass<K>): MutableMap<K, V> {
@Suppress("FunctionName")
public actual fun <E> ConcurrentSet(): MutableSet<E> {
return CopyOnWriteArraySet()
}
/**
* Same as [MutableCollection.addAll].
*
* Adds all the elements of the specified enumeration to this collection.
* @return true if any of the specified elements was added to the collection, false if the collection was not modified.
*/
public fun <T> MutableCollection<T>.addAll(enumeration: Enumeration<T>): Boolean {
var addResult = false
while (enumeration.hasMoreElements()) {
addResult = this.add(enumeration.nextElement())
}
return addResult
}