Using PlatformClassLoader to resolve java.*; fix #2009

This commit is contained in:
Karlatemp 2022-05-04 16:31:03 +08:00
parent edf7622b0b
commit 8250c3da65
No known key found for this signature in database
GPG Key ID: C6B606FF23D8FED7
3 changed files with 77 additions and 4 deletions

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,7 +10,12 @@
package net.mamoe.console.integrationtest
import org.junit.jupiter.api.fail
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import java.io.File
import java.util.UUID
internal fun readStringListFromEnv(key: String): MutableList<String> {
val size = System.getenv(key)!!.toInt()
@ -35,3 +40,51 @@ public fun File.assertNotExists() {
}
}
// endregion
// region JVM Utils
public val vmClassfileVersion: Int = runCatching {
val obj = ClassReader("java.lang.Object")
val classobj = ClassNode().also { obj.accept(it, ClassReader.SKIP_CODE) }
classobj.version
}.recoverCatching {
val ccl = object : ClassLoader(null) {
fun canLoad(ver: Int) : Boolean{
val klass = ClassWriter(ClassWriter.COMPUTE_MAXS)
val cname =
"net/mamoe/console/integrationtest/vtest/C${ver}_${System.currentTimeMillis()}_${UUID.randomUUID()}"
.replace('-', '_')
klass.visit(
ver,
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
cname,
null, "java/lang/Object", null
)
klass.visitMethod(Opcodes.ACC_PRIVATE, "<init>", "()V", null, null)!!.also { cinit ->
cinit.visitVarInsn(Opcodes.ALOAD, 0)
cinit.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
cinit.visitInsn(Opcodes.RETURN)
cinit.visitMaxs(0, 0)
}
val code = klass.toByteArray()
return kotlin.runCatching {
val k = defineClass(null, code, 0, code.size)
Class.forName(k.name, true, this)
}.isSuccess
}
}
if (ccl.canLoad(Opcodes.V17)) return@recoverCatching Opcodes.V17
if (ccl.canLoad(Opcodes.V16)) return@recoverCatching Opcodes.V16
if (ccl.canLoad(Opcodes.V15)) return@recoverCatching Opcodes.V15
if (ccl.canLoad(Opcodes.V14)) return@recoverCatching Opcodes.V14
if (ccl.canLoad(Opcodes.V13)) return@recoverCatching Opcodes.V13
if (ccl.canLoad(Opcodes.V12)) return@recoverCatching Opcodes.V12
if (ccl.canLoad(Opcodes.V11)) return@recoverCatching Opcodes.V11
if (ccl.canLoad(Opcodes.V10)) return@recoverCatching Opcodes.V10
if (ccl.canLoad(Opcodes.V9)) return@recoverCatching Opcodes.V9
Opcodes.V1_8
}.getOrElse { Opcodes.V1_8 } // Fallback
public fun canVmLoad(opversion: Int): Boolean = opversion <= vmClassfileVersion
// endregion

View File

@ -9,9 +9,12 @@
package net.mamoe.console.integrationtest.ep.pddd
import net.mamoe.console.integrationtest.canVmLoad
import net.mamoe.mirai.console.extension.PluginComponentStorage
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
import net.mamoe.mirai.utils.info
import org.objectweb.asm.Opcodes
import kotlin.test.assertEquals
/*
@ -35,9 +38,16 @@ internal object P : KotlinPlugin(
// console-non-hard-link dependency
// mirai-core used 1.64 current
val bc = Class.forName("org.bouncycastle.LICENSE")
assertEquals(
"1.63.0",
Class.forName("org.bouncycastle.LICENSE").`package`.implementationVersion
bc.`package`.implementationVersion,
message = "$bc <- ${bc.classLoader}"
)
// #2009
if (canVmLoad(Opcodes.V11)) {
logger.info { "V11" }
Class.forName("java.net.http.HttpClient")
}
}
}

View File

@ -120,7 +120,7 @@ internal class DynLibClassLoader(
}
override fun loadClass(name: String, resolve: Boolean): Class<*> {
if (name.startsWith("java.")) return Class.forName(name, false, null)
if (name.startsWith("java.")) return Class.forName(name, false, JavaSystemPlatformClassLoader)
val pt = this.parent
val topPt: ClassLoader? = if (pt is DynLibClassLoader) {
pt.findButNoSystem(name)?.let { return it }
@ -303,7 +303,7 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
override fun loadClass(name: String, resolve: Boolean): Class<*> = loadClass(name)
override fun loadClass(name: String): Class<*> {
if (name.startsWith("java.")) return Class.forName(name, false, null)
if (name.startsWith("java.")) return Class.forName(name, false, JavaSystemPlatformClassLoader)
if (name.startsWith("io.netty") || name in AllDependenciesClassesHolder.allclasses) {
return AllDependenciesClassesHolder.appClassLoader.loadClass(name)
}
@ -411,6 +411,16 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
}
private val JavaSystemPlatformClassLoader: ClassLoader by lazy {
kotlin.runCatching {
ClassLoader::class.java.methods.asSequence().filter {
it.name == "getPlatformClassLoader"
}.filter {
java.lang.reflect.Modifier.isStatic(it.modifiers)
}.firstOrNull()?.invoke(null) as ClassLoader?
}.getOrNull() ?: ClassLoader.getSystemClassLoader().parent
}
private fun String.pkgName(): String = substringBeforeLast('.', "")
internal fun Artifact.depId(): String = "$groupId:$artifactId"