Reformat eval4j
This commit is contained in:
@@ -82,23 +82,21 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
BIPUSH, SIPUSH -> int((insn as IntInsnNode).operand)
|
BIPUSH, SIPUSH -> int((insn as IntInsnNode).operand)
|
||||||
|
|
||||||
LDC -> {
|
LDC -> {
|
||||||
val cst = ((insn as LdcInsnNode)).cst
|
when (val cst = ((insn as LdcInsnNode)).cst) {
|
||||||
when (cst) {
|
|
||||||
is Int -> int(cst)
|
is Int -> int(cst)
|
||||||
is Float -> float(cst)
|
is Float -> float(cst)
|
||||||
is Long -> long(cst)
|
is Long -> long(cst)
|
||||||
is Double -> double(cst)
|
is Double -> double(cst)
|
||||||
is String -> eval.loadString(cst)
|
is String -> eval.loadString(cst)
|
||||||
is Type -> {
|
is Type -> {
|
||||||
val sort = cst.sort
|
when (cst.sort) {
|
||||||
when (sort) {
|
|
||||||
Type.OBJECT, Type.ARRAY -> eval.loadClass(cst)
|
Type.OBJECT, Type.ARRAY -> eval.loadClass(cst)
|
||||||
Type.METHOD -> throw UnsupportedByteCodeException("Method handles are not supported")
|
Type.METHOD -> throw UnsupportedByteCodeException("Method handles are not supported")
|
||||||
else -> throw UnsupportedByteCodeException("Illegal LDC constant " + cst)
|
else -> throw UnsupportedByteCodeException("Illegal LDC constant $cst")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Handle -> throw UnsupportedByteCodeException("Method handles are not supported")
|
is Handle -> throw UnsupportedByteCodeException("Method handles are not supported")
|
||||||
else -> throw UnsupportedByteCodeException("Illegal LDC constant " + cst)
|
else -> throw UnsupportedByteCodeException("Illegal LDC constant $cst")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSR -> LabelValue((insn as JumpInsnNode).label)
|
JSR -> LabelValue((insn as JumpInsnNode).label)
|
||||||
@@ -157,13 +155,13 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
NEWARRAY -> {
|
NEWARRAY -> {
|
||||||
val typeStr = when ((insn as IntInsnNode).operand) {
|
val typeStr = when ((insn as IntInsnNode).operand) {
|
||||||
T_BOOLEAN -> "[Z"
|
T_BOOLEAN -> "[Z"
|
||||||
T_CHAR -> "[C"
|
T_CHAR -> "[C"
|
||||||
T_BYTE -> "[B"
|
T_BYTE -> "[B"
|
||||||
T_SHORT -> "[S"
|
T_SHORT -> "[S"
|
||||||
T_INT -> "[I"
|
T_INT -> "[I"
|
||||||
T_FLOAT -> "[F"
|
T_FLOAT -> "[F"
|
||||||
T_DOUBLE -> "[D"
|
T_DOUBLE -> "[D"
|
||||||
T_LONG -> "[J"
|
T_LONG -> "[J"
|
||||||
else -> throw AnalyzerException(insn, "Invalid array type")
|
else -> throw AnalyzerException(insn, "Invalid array type")
|
||||||
}
|
}
|
||||||
eval.newArray(Type.getType(typeStr), value.int)
|
eval.newArray(Type.getType(typeStr), value.int)
|
||||||
@@ -310,11 +308,13 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
val l1 = value1.long
|
val l1 = value1.long
|
||||||
val l2 = value2.long
|
val l2 = value2.long
|
||||||
|
|
||||||
int(when {
|
int(
|
||||||
l1 > l2 -> 1
|
when {
|
||||||
l1 == l2 -> 0
|
l1 > l2 -> 1
|
||||||
else -> -1
|
l1 == l2 -> 0
|
||||||
})
|
else -> -1
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
FCMPL,
|
FCMPL,
|
||||||
@@ -322,13 +322,15 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
val l1 = value1.float
|
val l1 = value1.float
|
||||||
val l2 = value2.float
|
val l2 = value2.float
|
||||||
|
|
||||||
int(when {
|
int(
|
||||||
l1 > l2 -> 1
|
when {
|
||||||
l1 == l2 -> 0
|
l1 > l2 -> 1
|
||||||
l1 < l2 -> -1
|
l1 == l2 -> 0
|
||||||
// one of them is NaN
|
l1 < l2 -> -1
|
||||||
else -> if (insn.opcode == FCMPG) 1 else -1
|
// one of them is NaN
|
||||||
})
|
else -> if (insn.opcode == FCMPG) 1 else -1
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DCMPL,
|
DCMPL,
|
||||||
@@ -336,13 +338,15 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
val l1 = value1.double
|
val l1 = value1.double
|
||||||
val l2 = value2.double
|
val l2 = value2.double
|
||||||
|
|
||||||
int(when {
|
int(
|
||||||
l1 > l2 -> 1
|
when {
|
||||||
l1 == l2 -> 0
|
l1 > l2 -> 1
|
||||||
l1 < l2 -> -1
|
l1 == l2 -> 0
|
||||||
// one of them is NaN
|
l1 < l2 -> -1
|
||||||
else -> if (insn.opcode == DCMPG) 1 else -1
|
// one of them is NaN
|
||||||
})
|
else -> if (insn.opcode == DCMPG) 1 else -1
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
|
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
|
||||||
@@ -393,10 +397,10 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
|
|
||||||
INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE -> {
|
INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE -> {
|
||||||
eval.invokeMethod(
|
eval.invokeMethod(
|
||||||
values[0],
|
values[0],
|
||||||
MethodDescription(insn as MethodInsnNode),
|
MethodDescription(insn as MethodInsnNode),
|
||||||
values.subList(1, values.size),
|
values.subList(1, values.size),
|
||||||
insn.opcode == INVOKESPECIAL
|
insn.opcode == INVOKESPECIAL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ interface InterpreterResult {
|
|||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExceptionThrown(val exception: ObjectValue, val kind: ExceptionKind): InterpreterResult {
|
class ExceptionThrown(val exception: ObjectValue, val kind: ExceptionKind) : InterpreterResult {
|
||||||
override fun toString(): String = "Thrown $exception: $kind"
|
override fun toString(): String = "Thrown $exception: $kind"
|
||||||
|
|
||||||
enum class ExceptionKind {
|
enum class ExceptionKind {
|
||||||
@@ -38,11 +38,11 @@ class ExceptionThrown(val exception: ObjectValue, val kind: ExceptionKind): Inte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ValueReturned(val result: Value): InterpreterResult {
|
data class ValueReturned(val result: Value) : InterpreterResult {
|
||||||
override fun toString(): String = "Returned $result"
|
override fun toString(): String = "Returned $result"
|
||||||
}
|
}
|
||||||
|
|
||||||
class AbnormalTermination(val message: String): InterpreterResult {
|
class AbnormalTermination(val message: String) : InterpreterResult {
|
||||||
override fun toString(): String = "Terminated abnormally: $message"
|
override fun toString(): String = "Terminated abnormally: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ interface InterpretationEventHandler {
|
|||||||
fun exceptionCaught(currentState: Frame<Value>, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
|
fun exceptionCaught(currentState: Frame<Value>, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ThrownFromEvalExceptionBase(cause: Throwable): RuntimeException(cause) {
|
abstract class ThrownFromEvalExceptionBase(cause: Throwable) : RuntimeException(cause) {
|
||||||
override fun toString(): String = "Thrown by evaluator: ${cause}"
|
override fun toString(): String = "Thrown by evaluator: ${cause}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,15 +69,15 @@ class BrokenCode(cause: Throwable) : ThrownFromEvalExceptionBase(cause)
|
|||||||
// Interpreting exceptions should not be sent to EA
|
// Interpreting exceptions should not be sent to EA
|
||||||
class Eval4JInterpretingException(override val cause: Throwable) : RuntimeException(cause)
|
class Eval4JInterpretingException(override val cause: Throwable) : RuntimeException(cause)
|
||||||
|
|
||||||
class ThrownFromEvaluatedCodeException(val exception: ObjectValue): RuntimeException() {
|
class ThrownFromEvaluatedCodeException(val exception: ObjectValue) : RuntimeException() {
|
||||||
override fun toString(): String = "Thrown from evaluated code: $exception"
|
override fun toString(): String = "Thrown from evaluated code: $exception"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interpreterLoop(
|
fun interpreterLoop(
|
||||||
m: MethodNode,
|
m: MethodNode,
|
||||||
initialState: Frame<Value>,
|
initialState: Frame<Value>,
|
||||||
eval: Eval,
|
eval: Eval,
|
||||||
handler: InterpretationEventHandler = InterpretationEventHandler.NONE
|
handler: InterpretationEventHandler = InterpretationEventHandler.NONE
|
||||||
): InterpreterResult {
|
): InterpreterResult {
|
||||||
val firstInsn = m.instructions.first
|
val firstInsn = m.instructions.first
|
||||||
if (firstInsn == null) throw IllegalArgumentException("Empty method")
|
if (firstInsn == null) throw IllegalArgumentException("Empty method")
|
||||||
@@ -93,7 +93,7 @@ fun interpreterLoop(
|
|||||||
val frame = Frame(initialState)
|
val frame = Frame(initialState)
|
||||||
val handlers = computeHandlers(m)
|
val handlers = computeHandlers(m)
|
||||||
|
|
||||||
class ResultException(val result: InterpreterResult): RuntimeException()
|
class ResultException(val result: InterpreterResult) : RuntimeException()
|
||||||
|
|
||||||
fun exceptionCaught(exceptionValue: Value, instanceOf: (Type) -> Boolean): Boolean {
|
fun exceptionCaught(exceptionValue: Value, instanceOf: (Type) -> Boolean): Boolean {
|
||||||
val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf()
|
val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf()
|
||||||
@@ -114,23 +114,21 @@ fun interpreterLoop(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exceptionCaught(exceptionValue: Value): Boolean = exceptionCaught(exceptionValue) {
|
fun exceptionCaught(exceptionValue: Value): Boolean = exceptionCaught(exceptionValue) { exceptionType ->
|
||||||
exceptionType -> eval.isInstanceOf(exceptionValue, exceptionType)
|
eval.isInstanceOf(exceptionValue, exceptionType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exceptionFromEvalCaught(exception: Throwable, exceptionValue: Value): Boolean {
|
fun exceptionFromEvalCaught(exception: Throwable, exceptionValue: Value): Boolean {
|
||||||
return exceptionCaught(exceptionValue) {
|
return exceptionCaught(exceptionValue) { exceptionType ->
|
||||||
exceptionType ->
|
|
||||||
try {
|
try {
|
||||||
val exceptionClass = exception::class.java
|
val exceptionClass = exception::class.java
|
||||||
val _class = Class.forName(
|
val _class = Class.forName(
|
||||||
exceptionType.internalName.replace('/', '.'),
|
exceptionType.internalName.replace('/', '.'),
|
||||||
true,
|
true,
|
||||||
exceptionClass.classLoader
|
exceptionClass.classLoader
|
||||||
)
|
)
|
||||||
_class.isAssignableFrom(exceptionClass)
|
_class.isAssignableFrom(exceptionClass)
|
||||||
}
|
} catch (e: ClassNotFoundException) {
|
||||||
catch (e: ClassNotFoundException) {
|
|
||||||
// If the class is not available in this VM, it can not be a superclass of an exception trown in it
|
// If the class is not available in this VM, it can not be a superclass of an exception trown in it
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -164,16 +162,16 @@ fun interpreterLoop(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: switch
|
// TODO: switch
|
||||||
LOOKUPSWITCH -> UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
|
LOOKUPSWITCH -> throw UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
|
||||||
TABLESWITCH -> UnsupportedByteCodeException("TABLESWITCH is not supported yet")
|
TABLESWITCH -> throw UnsupportedByteCodeException("TABLESWITCH is not supported yet")
|
||||||
|
|
||||||
IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
|
IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
|
||||||
val value = frame.getStackTop()
|
val value = frame.getStackTop()
|
||||||
val expectedType = Type.getReturnType(m.desc)
|
val expectedType = Type.getReturnType(m.desc)
|
||||||
if (expectedType.sort == Type.OBJECT || expectedType.sort == Type.ARRAY) {
|
if (expectedType.sort == Type.OBJECT || expectedType.sort == Type.ARRAY) {
|
||||||
val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
|
val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
|
||||||
ObjectValue(value.obj(), expectedType)
|
ObjectValue(value.obj(), expectedType)
|
||||||
else value
|
else value
|
||||||
return ValueReturned(coerced)
|
return ValueReturned(coerced)
|
||||||
}
|
}
|
||||||
if (value.asmType != expectedType) {
|
if (value.asmType != expectedType) {
|
||||||
@@ -216,24 +214,25 @@ fun interpreterLoop(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
|
// Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frame.execute(currentInsn, interpreter)
|
frame.execute(currentInsn, interpreter)
|
||||||
}
|
} catch (e: ThrownFromEvalExceptionBase) {
|
||||||
catch (e: ThrownFromEvalExceptionBase) {
|
|
||||||
val exception = e.cause!!
|
val exception = e.cause!!
|
||||||
val exceptionValue = ObjectValue(exception, Type.getType(exception::class.java))
|
val exceptionValue = ObjectValue(exception, Type.getType(exception::class.java))
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn,
|
val handled = handler.exceptionThrown(
|
||||||
exceptionValue)
|
frame, currentInsn,
|
||||||
|
exceptionValue
|
||||||
|
)
|
||||||
if (handled != null) return handled
|
if (handled != null) return handled
|
||||||
if (exceptionFromEvalCaught(exception, exceptionValue)) continue@loop
|
if (exceptionFromEvalCaught(exception, exceptionValue)) continue@loop
|
||||||
|
|
||||||
val exceptionType = if (e is BrokenCode) ExceptionKind.BROKEN_CODE else ExceptionKind.FROM_EVALUATOR
|
val exceptionType = if (e is BrokenCode) ExceptionKind.BROKEN_CODE else ExceptionKind.FROM_EVALUATOR
|
||||||
return ExceptionThrown(exceptionValue, exceptionType)
|
return ExceptionThrown(exceptionValue, exceptionType)
|
||||||
}
|
} catch (e: ThrownFromEvaluatedCodeException) {
|
||||||
catch (e: ThrownFromEvaluatedCodeException) {
|
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
||||||
if (handled != null) return handled
|
if (handled != null) return handled
|
||||||
if (exceptionCaught(e.exception)) continue@loop
|
if (exceptionCaught(e.exception)) continue@loop
|
||||||
@@ -247,23 +246,24 @@ fun interpreterLoop(
|
|||||||
|
|
||||||
goto(currentInsn.next)
|
goto(currentInsn.next)
|
||||||
}
|
}
|
||||||
}
|
} catch (e: ResultException) {
|
||||||
catch(e: ResultException) {
|
|
||||||
return e.result
|
return e.result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T: Value> Frame<T>.getStackTop(i: Int = 0) = this.getStack(this.stackSize - 1 - i) ?: throwBrokenCodeException(IllegalArgumentException("Couldn't get value with index = $i from top of stack"))
|
private fun <T : Value> Frame<T>.getStackTop(i: Int = 0) = this.getStack(this.stackSize - 1 - i) ?: throwBrokenCodeException(
|
||||||
|
IllegalArgumentException("Couldn't get value with index = $i from top of stack")
|
||||||
|
)
|
||||||
|
|
||||||
// Copied from org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze()
|
// Copied from org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze()
|
||||||
fun computeHandlers(m: MethodNode): Array<out List<TryCatchBlockNode>?> {
|
fun computeHandlers(m: MethodNode): Array<out List<TryCatchBlockNode>?> {
|
||||||
val insns = m.instructions
|
val insns = m.instructions
|
||||||
val handlers = Array<MutableList<TryCatchBlockNode>?>(insns.size()) {null}
|
val handlers = Array<MutableList<TryCatchBlockNode>?>(insns.size()) { null }
|
||||||
for (tcb in m.tryCatchBlocks) {
|
for (tcb in m.tryCatchBlocks) {
|
||||||
val begin = insns.indexOf(tcb.start)
|
val begin = insns.indexOf(tcb.start)
|
||||||
val end = insns.indexOf(tcb.end)
|
val end = insns.indexOf(tcb.end)
|
||||||
for (i in begin..end - 1) {
|
for (i in begin until end) {
|
||||||
val insnHandlers = handlers[i] ?: ArrayList<TryCatchBlockNode>()
|
val insnHandlers = handlers[i] ?: ArrayList()
|
||||||
handlers[i] = insnHandlers
|
handlers[i] = insnHandlers
|
||||||
|
|
||||||
insnHandlers.add(tcb)
|
insnHandlers.add(tcb)
|
||||||
|
|||||||
@@ -32,21 +32,21 @@ private val OBJECT = Type.getType(Any::class.java)
|
|||||||
private val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
private val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
||||||
|
|
||||||
class JDIEval(
|
class JDIEval(
|
||||||
private val vm: VirtualMachine,
|
private val vm: VirtualMachine,
|
||||||
private val defaultClassLoader: ClassLoaderReference?,
|
private val defaultClassLoader: ClassLoaderReference?,
|
||||||
private val thread: ThreadReference,
|
private val thread: ThreadReference,
|
||||||
private val invokePolicy: Int
|
private val invokePolicy: Int
|
||||||
) : Eval {
|
) : Eval {
|
||||||
|
|
||||||
private val primitiveTypes = mapOf(
|
private val primitiveTypes = mapOf(
|
||||||
Type.BOOLEAN_TYPE.className to vm.mirrorOf(true).type(),
|
Type.BOOLEAN_TYPE.className to vm.mirrorOf(true).type(),
|
||||||
Type.BYTE_TYPE.className to vm.mirrorOf(1.toByte()).type(),
|
Type.BYTE_TYPE.className to vm.mirrorOf(1.toByte()).type(),
|
||||||
Type.SHORT_TYPE.className to vm.mirrorOf(1.toShort()).type(),
|
Type.SHORT_TYPE.className to vm.mirrorOf(1.toShort()).type(),
|
||||||
Type.INT_TYPE.className to vm.mirrorOf(1).type(),
|
Type.INT_TYPE.className to vm.mirrorOf(1).type(),
|
||||||
Type.CHAR_TYPE.className to vm.mirrorOf('1').type(),
|
Type.CHAR_TYPE.className to vm.mirrorOf('1').type(),
|
||||||
Type.LONG_TYPE.className to vm.mirrorOf(1L).type(),
|
Type.LONG_TYPE.className to vm.mirrorOf(1L).type(),
|
||||||
Type.FLOAT_TYPE.className to vm.mirrorOf(1.0f).type(),
|
Type.FLOAT_TYPE.className to vm.mirrorOf(1.0f).type(),
|
||||||
Type.DOUBLE_TYPE.className to vm.mirrorOf(1.0).type()
|
Type.DOUBLE_TYPE.className to vm.mirrorOf(1.0).type()
|
||||||
)
|
)
|
||||||
|
|
||||||
private val isJava8OrLater = StringUtil.compareVersionNumbers(vm.version(), "1.8") >= 0
|
private val isJava8OrLater = StringUtil.compareVersionNumbers(vm.version(), "1.8") >= 0
|
||||||
@@ -55,9 +55,9 @@ class JDIEval(
|
|||||||
return loadClass(classType, defaultClassLoader)
|
return loadClass(classType, defaultClassLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadClass(classType: Type, classLoader: ClassLoaderReference?): Value {
|
private fun loadClass(classType: Type, classLoader: ClassLoaderReference?): Value {
|
||||||
val loadedClasses = vm.classesByName(classType.internalName)
|
val loadedClasses = vm.classesByName(classType.internalName)
|
||||||
if (!loadedClasses.isEmpty()) {
|
if (loadedClasses.isNotEmpty()) {
|
||||||
for (loadedClass in loadedClasses) {
|
for (loadedClass in loadedClasses) {
|
||||||
if (loadedClass.isPrepared && (classType.descriptor in BOOTSTRAP_CLASS_DESCRIPTORS || loadedClass.classLoader() == classLoader)) {
|
if (loadedClass.isPrepared && (classType.descriptor in BOOTSTRAP_CLASS_DESCRIPTORS || loadedClass.classLoader() == classLoader)) {
|
||||||
return loadedClass.classObject().asValue()
|
return loadedClass.classObject().asValue()
|
||||||
@@ -66,33 +66,32 @@ class JDIEval(
|
|||||||
}
|
}
|
||||||
if (classLoader == null) {
|
if (classLoader == null) {
|
||||||
return invokeStaticMethod(
|
return invokeStaticMethod(
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
CLASS.internalName,
|
CLASS.internalName,
|
||||||
"forName",
|
"forName",
|
||||||
"(Ljava/lang/String;)Ljava/lang/Class;",
|
"(Ljava/lang/String;)Ljava/lang/Class;",
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
listOf(vm.mirrorOf(classType.internalName.replace('/', '.')).asValue())
|
listOf(vm.mirrorOf(classType.internalName.replace('/', '.')).asValue())
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return invokeStaticMethod(
|
return invokeStaticMethod(
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
CLASS.internalName,
|
CLASS.internalName,
|
||||||
"forName",
|
"forName",
|
||||||
"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
|
"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
listOf(
|
listOf(
|
||||||
vm.mirrorOf(classType.internalName.replace('/', '.')).asValue(),
|
vm.mirrorOf(classType.internalName.replace('/', '.')).asValue(),
|
||||||
boolean(true),
|
boolean(true),
|
||||||
classLoader.asValue()
|
classLoader.asValue()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadClassByName(name: String, classLoader: ClassLoaderReference): jdi_Type {
|
private fun loadClassByName(name: String, classLoader: ClassLoaderReference): jdi_Type {
|
||||||
val dimensions = name.count { it == '[' }
|
val dimensions = name.count { it == '[' }
|
||||||
val baseTypeName = if (dimensions > 0) name.substring(0, name.indexOf('[')) else name
|
val baseTypeName = if (dimensions > 0) name.substring(0, name.indexOf('[')) else name
|
||||||
|
|
||||||
@@ -115,20 +114,24 @@ class JDIEval(
|
|||||||
"Can't check isInstanceOf() for non-object type $targetType"
|
"Can't check isInstanceOf() for non-object type $targetType"
|
||||||
}
|
}
|
||||||
|
|
||||||
val _class = loadClass(targetType)
|
val clazz = loadClass(targetType)
|
||||||
return invokeMethod(
|
return invokeMethod(
|
||||||
_class,
|
clazz,
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
CLASS.internalName,
|
CLASS.internalName,
|
||||||
"isInstance",
|
"isInstance",
|
||||||
"(Ljava/lang/Object;)Z",
|
"(Ljava/lang/Object;)Z",
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
listOf(value)).boolean
|
listOf(value)
|
||||||
|
).boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Type.asReferenceType(classLoader: ClassLoaderReference? = this@JDIEval.defaultClassLoader): ReferenceType = loadClass(this, classLoader).jdiClass!!.reflectedType()
|
private fun Type.asReferenceType(classLoader: ClassLoaderReference? = this@JDIEval.defaultClassLoader): ReferenceType =
|
||||||
fun Type.asArrayType(classLoader: ClassLoaderReference? = this@JDIEval.defaultClassLoader): ArrayType = asReferenceType(classLoader) as ArrayType
|
loadClass(this, classLoader).jdiClass!!.reflectedType()
|
||||||
|
|
||||||
|
private fun Type.asArrayType(classLoader: ClassLoaderReference? = this@JDIEval.defaultClassLoader): ArrayType =
|
||||||
|
asReferenceType(classLoader) as ArrayType
|
||||||
|
|
||||||
override fun newArray(arrayType: Type, size: Int): Value {
|
override fun newArray(arrayType: Type, size: Int): Value {
|
||||||
val jdiArrayType = arrayType.asArrayType()
|
val jdiArrayType = arrayType.asArrayType()
|
||||||
@@ -143,11 +146,11 @@ class JDIEval(
|
|||||||
|
|
||||||
private fun fillArray(elementType: Type, size: Int, nestedSizes: List<Int>): Value {
|
private fun fillArray(elementType: Type, size: Int, nestedSizes: List<Int>): Value {
|
||||||
val arr = newArray(Type.getType("[" + elementType.descriptor), size)
|
val arr = newArray(Type.getType("[" + elementType.descriptor), size)
|
||||||
if (!nestedSizes.isEmpty()) {
|
if (nestedSizes.isNotEmpty()) {
|
||||||
val nestedElementType = elementType.arrayElementType
|
val nestedElementType = elementType.arrayElementType
|
||||||
val nestedSize = nestedSizes[0]
|
val nestedSize = nestedSizes[0]
|
||||||
val tail = nestedSizes.drop(1)
|
val tail = nestedSizes.drop(1)
|
||||||
for (i in 0..size - 1) {
|
for (i in 0 until size) {
|
||||||
setArrayElement(arr, int(i), fillArray(nestedElementType, nestedSize, tail))
|
setArrayElement(arr, int(i), fillArray(nestedElementType, nestedSize, tail))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,8 +170,7 @@ class JDIEval(
|
|||||||
override fun getArrayElement(array: Value, index: Value): Value {
|
override fun getArrayElement(array: Value, index: Value): Value {
|
||||||
try {
|
try {
|
||||||
return array.array().getValue(index.int).asValue()
|
return array.array().getValue(index.int).asValue()
|
||||||
}
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
catch (e: IndexOutOfBoundsException) {
|
|
||||||
throwInterpretingException(ArrayIndexOutOfBoundsException(e.message))
|
throwInterpretingException(ArrayIndexOutOfBoundsException(e.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,8 +178,7 @@ class JDIEval(
|
|||||||
override fun setArrayElement(array: Value, index: Value, newValue: Value) {
|
override fun setArrayElement(array: Value, index: Value, newValue: Value) {
|
||||||
try {
|
try {
|
||||||
return array.array().setValue(index.int, newValue.asJdiValue(vm, array.asmType.arrayElementType))
|
return array.array().setValue(index.int, newValue.asJdiValue(vm, array.asmType.arrayElementType))
|
||||||
}
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
catch (e: IndexOutOfBoundsException) {
|
|
||||||
throwInterpretingException(ArrayIndexOutOfBoundsException(e.message))
|
throwInterpretingException(ArrayIndexOutOfBoundsException(e.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,13 +211,11 @@ class JDIEval(
|
|||||||
throwBrokenCodeException(NoSuchFieldError("Can't modify a final field: $field"))
|
throwBrokenCodeException(NoSuchFieldError("Can't modify a final field: $field"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val _class = field.declaringType()
|
val clazz = field.declaringType() as? ClassType
|
||||||
if (_class !is ClassType) {
|
?: throwBrokenCodeException(NoSuchFieldError("Can't a field in a non-class: $field"))
|
||||||
throwBrokenCodeException(NoSuchFieldError("Can't a field in a non-class: $field"))
|
|
||||||
}
|
|
||||||
|
|
||||||
val jdiValue = newValue.asJdiValue(vm, field.type().asType())
|
val jdiValue = newValue.asJdiValue(vm, field.type().asType())
|
||||||
mayThrow { _class.setValue(field, jdiValue) }.ifFail(field)
|
mayThrow { clazz.setValue(field, jdiValue) }.ifFail(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findMethod(methodDesc: MethodDescription, clazz: ReferenceType = methodDesc.ownerType.asReferenceType()): Method {
|
private fun findMethod(methodDesc: MethodDescription, clazz: ReferenceType = methodDesc.ownerType.asReferenceType()): Method {
|
||||||
@@ -260,9 +259,9 @@ class JDIEval(
|
|||||||
name.startsWith(internalNameWithoutSuffix) && canBeMangledInternalName(name) && it.signature() == methodDesc.desc
|
name.startsWith(internalNameWithoutSuffix) && canBeMangledInternalName(name) && it.signature() == methodDesc.desc
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!internalMethods.isEmpty()) {
|
if (internalMethods.isNotEmpty()) {
|
||||||
return internalMethods.singleOrNull() ?:
|
return internalMethods.singleOrNull()
|
||||||
throwBrokenCodeException(IllegalArgumentException("Several internal methods found for $methodDesc"))
|
?: throwBrokenCodeException(IllegalArgumentException("Several internal methods found for $methodDesc"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,9 +312,11 @@ class JDIEval(
|
|||||||
try {
|
try {
|
||||||
receiver.getValue(field)
|
receiver.getValue(field)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
throw IllegalArgumentException("Possibly incompatible types: " +
|
throw IllegalArgumentException(
|
||||||
"field declaring type = ${field.declaringType()}, " +
|
"Possibly incompatible types: " +
|
||||||
"instance type = ${receiver.referenceType()}")
|
"field declaring type = ${field.declaringType()}, " +
|
||||||
|
"instance type = ${receiver.referenceType()}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.ifFail(field, receiver).asValue()
|
}.ifFail(field, receiver).asValue()
|
||||||
}
|
}
|
||||||
@@ -328,7 +329,7 @@ class JDIEval(
|
|||||||
mayThrow { receiver.setValue(field, jdiValue) }
|
mayThrow { receiver.setValue(field, jdiValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unboxType(boxedValue: Value, type: Type): Value {
|
private fun unboxType(boxedValue: Value, type: Type): Value {
|
||||||
val method = when (type) {
|
val method = when (type) {
|
||||||
Type.INT_TYPE -> MethodDescription("java/lang/Integer", "intValue", "()I", false)
|
Type.INT_TYPE -> MethodDescription("java/lang/Integer", "intValue", "()I", false)
|
||||||
Type.BOOLEAN_TYPE -> MethodDescription("java/lang/Boolean", "booleanValue", "()Z", false)
|
Type.BOOLEAN_TYPE -> MethodDescription("java/lang/Boolean", "booleanValue", "()Z", false)
|
||||||
@@ -343,29 +344,14 @@ class JDIEval(
|
|||||||
return invokeMethod(boxedValue, method, listOf(), true)
|
return invokeMethod(boxedValue, method, listOf(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun boxType(value: Value): Value {
|
|
||||||
val method = when (value.asmType) {
|
|
||||||
Type.INT_TYPE -> MethodDescription("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
|
|
||||||
Type.BYTE_TYPE -> MethodDescription("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false)
|
|
||||||
Type.SHORT_TYPE -> MethodDescription("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false)
|
|
||||||
Type.LONG_TYPE -> MethodDescription("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false)
|
|
||||||
Type.BOOLEAN_TYPE -> MethodDescription("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)
|
|
||||||
Type.CHAR_TYPE -> MethodDescription("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false)
|
|
||||||
Type.FLOAT_TYPE -> MethodDescription("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false)
|
|
||||||
Type.DOUBLE_TYPE -> MethodDescription("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false)
|
|
||||||
else -> throw UnsupportedOperationException("Couldn't box non-primitive type ${value.asmType.internalName}")
|
|
||||||
}
|
|
||||||
return invokeStaticMethod(method, listOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value {
|
override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value {
|
||||||
if (invokespecial && methodDesc.name == "<init>") {
|
if (invokespecial && methodDesc.name == "<init>") {
|
||||||
// Constructor call
|
// Constructor call
|
||||||
val ctor = findMethod(methodDesc)
|
val ctor = findMethod(methodDesc)
|
||||||
val _class = (instance as NewObjectValue).asmType.asReferenceType() as ClassType
|
val clazz = (instance as NewObjectValue).asmType.asReferenceType() as ClassType
|
||||||
val args = mapArguments(arguments, ctor.safeArgumentTypes())
|
val args = mapArguments(arguments, ctor.safeArgumentTypes())
|
||||||
args.disableCollection()
|
args.disableCollection()
|
||||||
val result = mayThrow { _class.newInstance(thread, ctor, args, invokePolicy) }.ifFail(ctor)
|
val result = mayThrow { clazz.newInstance(thread, ctor, args, invokePolicy) }.ifFail(ctor)
|
||||||
args.enableCollection()
|
args.enableCollection()
|
||||||
instance.value = result
|
instance.value = result
|
||||||
return result.asValue()
|
return result.asValue()
|
||||||
@@ -388,8 +374,7 @@ class JDIEval(
|
|||||||
return if (invokespecial) {
|
return if (invokespecial) {
|
||||||
val method = findMethod(methodDesc)
|
val method = findMethod(methodDesc)
|
||||||
doInvokeMethod(obj, method, invokePolicy or ObjectReference.INVOKE_NONVIRTUAL)
|
doInvokeMethod(obj, method, invokePolicy or ObjectReference.INVOKE_NONVIRTUAL)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val method = findMethod(methodDesc, obj.referenceType() ?: methodDesc.ownerType.asReferenceType())
|
val method = findMethod(methodDesc, obj.referenceType() ?: methodDesc.ownerType.asReferenceType())
|
||||||
doInvokeMethod(obj, method, invokePolicy)
|
doInvokeMethod(obj, method, invokePolicy)
|
||||||
}
|
}
|
||||||
@@ -415,36 +400,36 @@ class JDIEval(
|
|||||||
|
|
||||||
private fun invokeMethodWithReflection(ownerType: Type, instance: Value, args: List<jdi_Value?>, methodDesc: MethodDescription): Value {
|
private fun invokeMethodWithReflection(ownerType: Type, instance: Value, args: List<jdi_Value?>, methodDesc: MethodDescription): Value {
|
||||||
val methodToInvoke = invokeMethod(
|
val methodToInvoke = invokeMethod(
|
||||||
loadClass(ownerType),
|
loadClass(ownerType),
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
CLASS.internalName,
|
CLASS.internalName,
|
||||||
"getDeclaredMethod",
|
"getDeclaredMethod",
|
||||||
"(Ljava/lang/String;[L${CLASS.internalName};)Ljava/lang/reflect/Method;",
|
"(Ljava/lang/String;[L${CLASS.internalName};)Ljava/lang/reflect/Method;",
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
listOf(vm.mirrorOf(methodDesc.name).asValue(), *methodDesc.parameterTypes.map { loadClass(it) }.toTypedArray())
|
listOf(vm.mirrorOf(methodDesc.name).asValue(), *methodDesc.parameterTypes.map { loadClass(it) }.toTypedArray())
|
||||||
)
|
)
|
||||||
|
|
||||||
invokeMethod(
|
invokeMethod(
|
||||||
methodToInvoke,
|
methodToInvoke,
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
Type.getType(AccessibleObject::class.java).internalName,
|
Type.getType(AccessibleObject::class.java).internalName,
|
||||||
"setAccessible",
|
"setAccessible",
|
||||||
"(Z)V",
|
"(Z)V",
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
listOf(vm.mirrorOf(true).asValue())
|
listOf(vm.mirrorOf(true).asValue())
|
||||||
)
|
)
|
||||||
|
|
||||||
val invocationResult = invokeMethod(
|
val invocationResult = invokeMethod(
|
||||||
methodToInvoke,
|
methodToInvoke,
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
methodToInvoke.asmType.internalName,
|
methodToInvoke.asmType.internalName,
|
||||||
"invoke",
|
"invoke",
|
||||||
"(L${OBJECT.internalName};[L${OBJECT.internalName};)L${OBJECT.internalName};",
|
"(L${OBJECT.internalName};[L${OBJECT.internalName};)L${OBJECT.internalName};",
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
listOf(instance, mirrorOfArgs(args))
|
listOf(instance, mirrorOfArgs(args))
|
||||||
)
|
)
|
||||||
|
|
||||||
if (methodDesc.returnType.sort != Type.OBJECT && methodDesc.returnType.sort != Type.ARRAY && methodDesc.returnType.sort != Type.VOID) {
|
if (methodDesc.returnType.sort != Type.OBJECT && methodDesc.returnType.sort != Type.ARRAY && methodDesc.returnType.sort != Type.VOID) {
|
||||||
@@ -501,7 +486,7 @@ class JDIEval(
|
|||||||
"float" -> primitiveTypes.getValue(Type.FLOAT_TYPE.className)
|
"float" -> primitiveTypes.getValue(Type.FLOAT_TYPE.className)
|
||||||
"double" -> primitiveTypes.getValue(Type.DOUBLE_TYPE.className)
|
"double" -> primitiveTypes.getValue(Type.DOUBLE_TYPE.className)
|
||||||
else -> virtualMachine().classesByName(name).firstOrNull()
|
else -> virtualMachine().classesByName(name).firstOrNull()
|
||||||
?: throw IllegalStateException("Unknown class $name")
|
?: throw IllegalStateException("Unknown class $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,18 +495,16 @@ class JDIEval(
|
|||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private sealed class JdiOperationResult<T> {
|
private sealed class JdiOperationResult<T> {
|
||||||
class Fail<T>(val cause: Exception): JdiOperationResult<T>()
|
class Fail<T>(val cause: Exception) : JdiOperationResult<T>()
|
||||||
class OK<T>(val value: T): JdiOperationResult<T>()
|
class OK<T>(val value: T) : JdiOperationResult<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> mayThrow(f: () -> T): JdiOperationResult<T> {
|
private fun <T> mayThrow(f: () -> T): JdiOperationResult<T> {
|
||||||
try {
|
return try {
|
||||||
return JdiOperationResult.OK(f())
|
JdiOperationResult.OK(f())
|
||||||
}
|
} catch (e: IllegalArgumentException) {
|
||||||
catch (e: IllegalArgumentException) {
|
JdiOperationResult.Fail(e)
|
||||||
return JdiOperationResult.Fail<T>(e)
|
} catch (e: InvocationException) {
|
||||||
}
|
|
||||||
catch (e: InvocationException) {
|
|
||||||
throw ThrownFromEvaluatedCodeException(e.exception().asValue())
|
throw ThrownFromEvaluatedCodeException(e.exception().asValue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,13 +518,12 @@ private fun <T> JdiOperationResult<T>.ifFail(member: TypeComponent, thisObj: Obj
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> JdiOperationResult<T>.ifFail(lazyMessage: () -> String): T {
|
private fun <T> JdiOperationResult<T>.ifFail(lazyMessage: () -> String): T {
|
||||||
return when(this) {
|
return when (this) {
|
||||||
is JdiOperationResult.OK -> this.value
|
is JdiOperationResult.OK -> this.value
|
||||||
is JdiOperationResult.Fail -> {
|
is JdiOperationResult.Fail -> {
|
||||||
if (cause is IllegalArgumentException) {
|
if (cause is IllegalArgumentException) {
|
||||||
throwBrokenCodeException(IllegalArgumentException(lazyMessage(), this.cause))
|
throwBrokenCodeException(IllegalArgumentException(lazyMessage(), this.cause))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throwBrokenCodeException(IllegalStateException(lazyMessage(), this.cause))
|
throwBrokenCodeException(IllegalStateException(lazyMessage(), this.cause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ fun makeInitialFrame(methodNode: MethodNode, arguments: List<Value>): Frame<Valu
|
|||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
|
||||||
class JDIFailureException(message: String?, cause: Throwable? = null): RuntimeException(message, cause)
|
class JDIFailureException(message: String?, cause: Throwable? = null) : RuntimeException(message, cause)
|
||||||
|
|
||||||
fun jdi_ObjectReference?.asValue(): ObjectValue {
|
fun jdi_ObjectReference?.asValue(): ObjectValue {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
|
|
||||||
open class MemberDescription protected constructor(
|
open class MemberDescription protected constructor(
|
||||||
val ownerInternalName: String,
|
val ownerInternalName: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val desc: String,
|
val desc: String,
|
||||||
val isStatic: Boolean
|
val isStatic: Boolean
|
||||||
) {
|
) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@@ -34,7 +34,7 @@ open class MemberDescription protected constructor(
|
|||||||
&& name == other.name
|
&& name == other.name
|
||||||
&& desc == other.desc
|
&& desc == other.desc
|
||||||
&& isStatic == other.isStatic
|
&& isStatic == other.isStatic
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -53,19 +53,13 @@ val MemberDescription.ownerType: Type
|
|||||||
get() = Type.getObjectType(ownerInternalName)
|
get() = Type.getObjectType(ownerInternalName)
|
||||||
|
|
||||||
class MethodDescription(
|
class MethodDescription(
|
||||||
ownerInternalName: String,
|
ownerInternalName: String,
|
||||||
name: String,
|
name: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
isStatic: Boolean
|
isStatic: Boolean
|
||||||
) : MemberDescription(ownerInternalName, name, desc, isStatic)
|
) : MemberDescription(ownerInternalName, name, desc, isStatic) {
|
||||||
|
constructor(insn: MethodInsnNode) : this(insn.owner, insn.name, insn.desc, insn.opcode == INVOKESTATIC)
|
||||||
fun MethodDescription(insn: MethodInsnNode): MethodDescription =
|
}
|
||||||
MethodDescription(
|
|
||||||
insn.owner,
|
|
||||||
insn.name,
|
|
||||||
insn.desc,
|
|
||||||
insn.opcode == INVOKESTATIC
|
|
||||||
)
|
|
||||||
|
|
||||||
val MethodDescription.returnType: Type
|
val MethodDescription.returnType: Type
|
||||||
get() = Type.getReturnType(desc)
|
get() = Type.getReturnType(desc)
|
||||||
@@ -75,19 +69,13 @@ val MethodDescription.parameterTypes: List<Type>
|
|||||||
|
|
||||||
|
|
||||||
class FieldDescription(
|
class FieldDescription(
|
||||||
ownerInternalName: String,
|
ownerInternalName: String,
|
||||||
name: String,
|
name: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
isStatic: Boolean
|
isStatic: Boolean
|
||||||
) : MemberDescription(ownerInternalName, name, desc, isStatic)
|
) : MemberDescription(ownerInternalName, name, desc, isStatic) {
|
||||||
|
constructor(insn: FieldInsnNode) : this(insn.owner, insn.name, insn.desc, insn.opcode in setOf(GETSTATIC, PUTSTATIC))
|
||||||
fun FieldDescription(insn: FieldInsnNode): FieldDescription =
|
}
|
||||||
FieldDescription(
|
|
||||||
insn.owner,
|
|
||||||
insn.name,
|
|
||||||
insn.desc,
|
|
||||||
insn.opcode in setOf(GETSTATIC, PUTSTATIC)
|
|
||||||
)
|
|
||||||
|
|
||||||
val FieldDescription.fieldType: Type
|
val FieldDescription.fieldType: Type
|
||||||
get() = Type.getType(desc)
|
get() = Type.getType(desc)
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ interface Value : org.jetbrains.org.objectweb.asm.tree.analysis.Value {
|
|||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
object NOT_A_VALUE: Value {
|
@Suppress("ClassName")
|
||||||
|
object NOT_A_VALUE : Value {
|
||||||
override val asmType = Type.getObjectType("<invalid>")
|
override val asmType = Type.getObjectType("<invalid>")
|
||||||
override val valid = false
|
override val valid = false
|
||||||
override fun getSize(): Int = 1
|
override fun getSize(): Int = 1
|
||||||
@@ -35,7 +36,8 @@ object NOT_A_VALUE: Value {
|
|||||||
override fun toString() = "NOT_A_VALUE"
|
override fun toString() = "NOT_A_VALUE"
|
||||||
}
|
}
|
||||||
|
|
||||||
object VOID_VALUE: Value {
|
@Suppress("ClassName")
|
||||||
|
object VOID_VALUE : Value {
|
||||||
override val asmType: Type = Type.VOID_TYPE
|
override val asmType: Type = Type.VOID_TYPE
|
||||||
override val valid: Boolean = false
|
override val valid: Boolean = false
|
||||||
override fun toString() = "VOID_VALUE"
|
override fun toString() = "VOID_VALUE"
|
||||||
@@ -48,13 +50,13 @@ fun makeNotInitializedValue(t: Type): Value? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotInitialized(override val asmType: Type): Value {
|
class NotInitialized(override val asmType: Type) : Value {
|
||||||
override val valid = false
|
override val valid = false
|
||||||
override fun toString() = "NotInitialized: $asmType"
|
override fun toString() = "NotInitialized: $asmType"
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractValueBase<out V>(
|
abstract class AbstractValueBase<out V>(
|
||||||
override val asmType: Type
|
override val asmType: Type
|
||||||
) : Value {
|
) : Value {
|
||||||
override val valid = true
|
override val valid = true
|
||||||
abstract val value: V
|
abstract val value: V
|
||||||
@@ -72,24 +74,22 @@ abstract class AbstractValueBase<out V>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractValue<V>(
|
abstract class AbstractValue<V>(override val value: V, asmType: Type) : AbstractValueBase<V>(asmType)
|
||||||
override val value: V,
|
|
||||||
asmType: Type
|
|
||||||
) : AbstractValueBase<V>(asmType)
|
|
||||||
|
|
||||||
class IntValue(value: Int, asmType: Type): AbstractValue<Int>(value, asmType)
|
class IntValue(value: Int, asmType: Type) : AbstractValue<Int>(value, asmType)
|
||||||
class LongValue(value: Long): AbstractValue<Long>(value, Type.LONG_TYPE)
|
class LongValue(value: Long) : AbstractValue<Long>(value, Type.LONG_TYPE)
|
||||||
class FloatValue(value: Float): AbstractValue<Float>(value, Type.FLOAT_TYPE)
|
class FloatValue(value: Float) : AbstractValue<Float>(value, Type.FLOAT_TYPE)
|
||||||
class DoubleValue(value: Double): AbstractValue<Double>(value, Type.DOUBLE_TYPE)
|
class DoubleValue(value: Double) : AbstractValue<Double>(value, Type.DOUBLE_TYPE)
|
||||||
open class ObjectValue(value: Any?, asmType: Type): AbstractValue<Any?>(value, asmType)
|
open class ObjectValue(value: Any?, asmType: Type) : AbstractValue<Any?>(value, asmType)
|
||||||
class NewObjectValue(asmType: Type): ObjectValue(null, asmType) {
|
|
||||||
|
class NewObjectValue(asmType: Type) : ObjectValue(null, asmType) {
|
||||||
override var value: Any? = null
|
override var value: Any? = null
|
||||||
get(): Any? {
|
get(): Any? {
|
||||||
return field ?: throw IllegalStateException("Trying to access an unitialized object: $this")
|
return field ?: throw IllegalStateException("Trying to access an unitialized object: $this")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LabelValue(value: LabelNode): AbstractValue<LabelNode>(value, Type.VOID_TYPE)
|
class LabelValue(value: LabelNode) : AbstractValue<LabelNode>(value, Type.VOID_TYPE)
|
||||||
|
|
||||||
fun boolean(v: Boolean) = IntValue(if (v) 1 else 0, Type.BOOLEAN_TYPE)
|
fun boolean(v: Boolean) = IntValue(if (v) 1 else 0, Type.BOOLEAN_TYPE)
|
||||||
fun byte(v: Byte) = IntValue(v.toInt(), Type.BYTE_TYPE)
|
fun byte(v: Byte) = IntValue(v.toInt(), Type.BYTE_TYPE)
|
||||||
@@ -99,7 +99,6 @@ fun int(v: Int) = IntValue(v, Type.INT_TYPE)
|
|||||||
fun long(v: Long) = LongValue(v)
|
fun long(v: Long) = LongValue(v)
|
||||||
fun float(v: Float) = FloatValue(v)
|
fun float(v: Float) = FloatValue(v)
|
||||||
fun double(v: Double) = DoubleValue(v)
|
fun double(v: Double) = DoubleValue(v)
|
||||||
//fun <T> obj(v: T, t: Type = if (v != null) Type.getType(v.javaClass) else Type.getType(Any::class.java)) = ObjectValue(v, t)
|
|
||||||
|
|
||||||
val NULL_VALUE = ObjectValue(null, Type.getObjectType("null"))
|
val NULL_VALUE = ObjectValue(null, Type.getObjectType("null"))
|
||||||
|
|
||||||
@@ -108,12 +107,13 @@ val Value.int: Int get() = (this as IntValue).value
|
|||||||
val Value.long: Long get() = (this as LongValue).value
|
val Value.long: Long get() = (this as LongValue).value
|
||||||
val Value.float: Float get() = (this as FloatValue).value
|
val Value.float: Float get() = (this as FloatValue).value
|
||||||
val Value.double: Double get() = (this as DoubleValue).value
|
val Value.double: Double get() = (this as DoubleValue).value
|
||||||
|
|
||||||
fun Value.obj(expectedType: Type = asmType): Any? {
|
fun Value.obj(expectedType: Type = asmType): Any? {
|
||||||
return when {
|
return when (expectedType) {
|
||||||
expectedType == Type.BOOLEAN_TYPE -> this.boolean
|
Type.BOOLEAN_TYPE -> this.boolean
|
||||||
expectedType == Type.SHORT_TYPE -> (this as IntValue).int.toShort()
|
Type.SHORT_TYPE -> (this as IntValue).int.toShort()
|
||||||
expectedType == Type.BYTE_TYPE -> (this as IntValue).int.toByte()
|
Type.BYTE_TYPE -> (this as IntValue).int.toByte()
|
||||||
expectedType == Type.CHAR_TYPE -> (this as IntValue).int.toChar()
|
Type.CHAR_TYPE -> (this as IntValue).int.toChar()
|
||||||
else -> (this as AbstractValue<*>).value
|
else -> (this as AbstractValue<*>).value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user