Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 13 commits into from
Mar 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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<*>? {
Expand Down Expand Up @@ -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)
}
Expand All @@ -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 }
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public interface JvmPluginClasspath {
*/
public val pluginIndependentLibrariesClassLoader: ClassLoader

/**
* [pluginClassLoader] 是否可以通过 [ClassLoader.getResource] 获取 Mirai Console (包括依赖) 的相关资源
*
* 默认为 `false`
*/
public var shouldResolveConsoleSystemResource: Boolean

/**
* 将 [file] 加入 [classLoader] 的搜索路径内
*
Expand Down
14 changes: 14 additions & 0 deletions mirai-core-utils/src/jvmBaseMain/kotlin/Collections.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}