Tooltip reflection for advanced options

This commit is contained in:
huangyuhui
2017-08-23 12:07:08 +08:00
parent 86cef86fc9
commit 2e3d9c22e3
9 changed files with 134 additions and 51 deletions

View File

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

View File

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

View File

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