Tooltip reflection for advanced options
This commit is contained in:
@@ -53,8 +53,6 @@ class CurseForgeModpackManifest @JvmOverloads constructor(
|
||||
check(manifestType == MINECRAFT_MODPACK, { "Only support Minecraft modpack" })
|
||||
}
|
||||
|
||||
fun toModpack() = Modpack(name = name, author = author, version = version, description = "No description")
|
||||
|
||||
companion object {
|
||||
val MINECRAFT_MODPACK = "minecraftModpack"
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ enum class OS {
|
||||
* The total memory/MB this computer have.
|
||||
*/
|
||||
val TOTAL_MEMORY: Int by lazy {
|
||||
val bytes = ReflectionHelper.invoke<Long>(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize")
|
||||
val bytes = ManagementFactory.getOperatingSystemMXBean().call("getTotalPhysicalMemorySize") as? Long?
|
||||
if (bytes == null) 1024
|
||||
else (bytes / 1024 / 1024).toInt()
|
||||
}
|
||||
|
||||
@@ -19,61 +19,125 @@ package org.jackhuang.hmcl.util
|
||||
|
||||
import sun.misc.Unsafe
|
||||
import java.lang.reflect.AccessibleObject
|
||||
import java.lang.reflect.Executable
|
||||
import java.lang.reflect.Method
|
||||
import java.security.AccessController
|
||||
import java.security.PrivilegedExceptionAction
|
||||
|
||||
object ReflectionHelper {
|
||||
private val unsafe: Unsafe = AccessController.doPrivileged(PrivilegedExceptionAction {
|
||||
val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe")
|
||||
theUnsafe.isAccessible = true
|
||||
theUnsafe.get(null) as Unsafe
|
||||
})
|
||||
private val objectFieldOffset = unsafe.objectFieldOffset(
|
||||
AccessibleObject::class.java.getDeclaredField("override"))
|
||||
|
||||
private lateinit var unsafe: Unsafe
|
||||
private var objectFieldOffset = 0L
|
||||
private fun AccessibleObject.setAccessibleForcibly() =
|
||||
unsafe.putBoolean(this, objectFieldOffset, true)
|
||||
|
||||
init {
|
||||
fun getMethod(obj: Any, methodName: String): Method? =
|
||||
getMethod(obj.javaClass, methodName)
|
||||
|
||||
fun getMethod(cls: Class<*>, methodName: String): Method? =
|
||||
try {
|
||||
unsafe = AccessController.doPrivileged(PrivilegedExceptionAction {
|
||||
val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe")
|
||||
theUnsafe.isAccessible = true
|
||||
theUnsafe.get(null) as Unsafe
|
||||
})
|
||||
val overrideField = AccessibleObject::class.java.getDeclaredField("override")
|
||||
objectFieldOffset = unsafe.objectFieldOffset(overrideField)
|
||||
cls.getDeclaredMethod(methodName).apply { setAccessibleForcibly() }
|
||||
} catch (ex: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a field, method or constructor by reflection.
|
||||
*
|
||||
* @param name the field or method name of [clazz], "new" if you are looking for a constructor.
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Any.call(name: String, vararg args: Any?): Any? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return javaClass.call(name, this, *args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a constructor by reflection.
|
||||
*
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Class<*>.construct(vararg args: Any?) = call("new", null, *args)
|
||||
|
||||
/**
|
||||
* Call a field, method or constructor by reflection.
|
||||
*
|
||||
* @param name the field or method name of [clazz], "new" if you are looking for a constructor.
|
||||
* @param obj null for constructors or static/object methods/fields.
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Class<*>.call(name: String, obj: Any? = null, vararg args: Any?): Any? {
|
||||
try {
|
||||
if (args.isEmpty())
|
||||
try {
|
||||
return getDeclaredField(name).get(obj)
|
||||
} catch(ignored: NoSuchFieldException) {
|
||||
}
|
||||
if (name == "new")
|
||||
declaredConstructors.forEach {
|
||||
if (checkParameter(it, *args)) return it.newInstance(*args)
|
||||
}
|
||||
else
|
||||
return forMethod(name, *args)!!.invoke(obj, *args)
|
||||
throw RuntimeException()
|
||||
} catch(e: Exception) {
|
||||
throw IllegalArgumentException("Cannot find `$name` in Class `${this.name}`, please check your code.", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAccessible(obj: AccessibleObject) =
|
||||
unsafe.putBoolean(obj, objectFieldOffset, true)
|
||||
fun Class<*>.forMethod(name: String, vararg args: Any?): Method? =
|
||||
declaredMethods.filter { it.name == name }.filter { checkParameter(it, *args) }.firstOrNull()
|
||||
|
||||
fun <T> get(obj: Any, fieldName: String): T? =
|
||||
get(obj.javaClass, obj, fieldName)
|
||||
fun checkParameter(exec: Executable, vararg args: Any?): Boolean {
|
||||
val cArgs = exec.parameterTypes
|
||||
if (args.size == cArgs.size) {
|
||||
for (i in 0 until args.size) {
|
||||
val arg = args[i]
|
||||
// primitive variable cannot be null
|
||||
if (if (arg != null) !isInstance(cArgs[i], arg) else cArgs[i].isPrimitive)
|
||||
return false
|
||||
}
|
||||
exec.setAccessibleForcibly()
|
||||
return true
|
||||
} else
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T, S> get(cls: Class<out S>, obj: S, fieldName: String): T? =
|
||||
try {
|
||||
val method = cls.getDeclaredField(fieldName)
|
||||
setAccessible(method)
|
||||
method.get(obj) as T
|
||||
} catch (ex: Throwable) {
|
||||
null
|
||||
}
|
||||
fun isInstance(superClass: Class<*>, obj: Any): Boolean {
|
||||
if (superClass.isInstance(obj)) return true
|
||||
else if (PRIMITIVES[superClass.name] == obj.javaClass) return true
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> invoke(obj: Any, methodName: String): T? =
|
||||
try {
|
||||
val method = obj.javaClass.getDeclaredMethod(methodName)
|
||||
setAccessible(method)
|
||||
method.invoke(obj) as T?
|
||||
} catch (ex: Throwable) {
|
||||
null
|
||||
}
|
||||
fun isInstance(superClass: Class<*>, clazz: Class<*>): Boolean {
|
||||
for (i in clazz.interfaces)
|
||||
if (isInstance(superClass, i) || PRIMITIVES[superClass.name] == clazz)
|
||||
return true
|
||||
return isSubClass(superClass, clazz)
|
||||
}
|
||||
|
||||
fun getMethod(obj: Any, methodName: String): Method? =
|
||||
getMethod(obj.javaClass, methodName)
|
||||
fun isSubClass(superClass: Class<*>, clazz: Class<*>): Boolean {
|
||||
var clz: Class<*>? = clazz
|
||||
do {
|
||||
if (superClass == clz) return true
|
||||
clz = clz?.superclass
|
||||
} while (clz != null)
|
||||
return false
|
||||
}
|
||||
|
||||
fun getMethod(cls: Class<*>, methodName: String): Method? =
|
||||
try {
|
||||
cls.getDeclaredMethod(methodName).apply { ReflectionHelper.setAccessible(this) }
|
||||
} catch (ex: Throwable) {
|
||||
null
|
||||
}
|
||||
}
|
||||
fun <T> Class<T>.objectInstance() = call("INSTANCE")
|
||||
|
||||
val PRIMITIVES = mapOf(
|
||||
"byte" to java.lang.Byte::class.java,
|
||||
"short" to java.lang.Short::class.java,
|
||||
"int" to java.lang.Integer::class.java,
|
||||
"long" to java.lang.Long::class.java,
|
||||
"char" to java.lang.Character::class.java,
|
||||
"float" to java.lang.Float::class.java,
|
||||
"double" to java.lang.Double::class.java,
|
||||
"boolean" to java.lang.Boolean::class.java
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user