Reformat eval4j

This commit is contained in:
Yan Zhulanow
2019-07-11 22:03:56 +09:00
parent eb173893b0
commit a20014a7e6
6 changed files with 219 additions and 245 deletions
@@ -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)
@@ -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(
when {
l1 > l2 -> 1 l1 > l2 -> 1
l1 == l2 -> 0 l1 == l2 -> 0
else -> -1 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(
when {
l1 > l2 -> 1 l1 > l2 -> 1
l1 == l2 -> 0 l1 == l2 -> 0
l1 < l2 -> -1 l1 < l2 -> -1
// one of them is NaN // one of them is NaN
else -> if (insn.opcode == FCMPG) 1 else -1 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(
when {
l1 > l2 -> 1 l1 > l2 -> 1
l1 == l2 -> 0 l1 == l2 -> 0
l1 < l2 -> -1 l1 < l2 -> -1
// one of them is NaN // one of them is NaN
else -> if (insn.opcode == DCMPG) 1 else -1 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 -> {
@@ -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,7 +69,7 @@ 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"
} }
@@ -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,13 +114,12 @@ 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(
@@ -129,8 +128,7 @@ fun interpreterLoop(
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,8 +162,8 @@ 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()
@@ -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)
@@ -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()
@@ -74,8 +74,7 @@ class JDIEval(
), ),
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,
@@ -92,7 +91,7 @@ class JDIEval(
} }
} }
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(
"Possibly incompatible types: " +
"field declaring type = ${field.declaringType()}, " + "field declaring type = ${field.declaringType()}, " +
"instance type = ${receiver.referenceType()}") "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)
} }
@@ -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) {
@@ -57,15 +57,9 @@ class MethodDescription(
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)
@@ -79,15 +73,9 @@ class FieldDescription(
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,7 +50,7 @@ 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"
} }
@@ -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
} }
} }