Evaluate Expression: box and unbox arguments before running JDIEval

This commit is contained in:
Natalia Ukhorskaya
2015-04-27 15:46:32 +03:00
parent 7e245e97ad
commit 328013a351
6 changed files with 93 additions and 6 deletions
@@ -212,19 +212,23 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
ClassReader(compiledData.bytecodes).accept(object : ClassVisitor(ASM5) {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
if (name == compiledData.funName) {
val args = context.getArgumentsForEval4j(compiledData.parameters.getParameterNames(), Type.getArgumentTypes(desc))
val argumentTypes = Type.getArgumentTypes(desc)
val args = context.getArgumentsForEval4j(compiledData.parameters.getParameterNames(), argumentTypes)
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
override fun visitEnd() {
val breakpoints = virtualMachine.eventRequestManager().breakpointRequests()
breakpoints?.forEach { it.disable() }
val eval = JDIEval(virtualMachine,
context.getClassLoader(),
context.getSuspendContext().getThread()?.getThreadReference()!!,
context.getSuspendContext().getInvokePolicy())
resultValue = interpreterLoop(
this,
makeInitialFrame(this, args),
JDIEval(virtualMachine,
context.getClassLoader(),
context.getSuspendContext().getThread()?.getThreadReference()!!,
context.getSuspendContext().getInvokePolicy())
makeInitialFrame(this, args.zip(argumentTypes).map { boxOrUnboxArgumentIfNeeded(eval, it.first, it.second) }),
eval
)
breakpoints?.forEach { it.enable() }
@@ -239,6 +243,29 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
return resultValue ?: throw IllegalStateException("resultValue is null: cannot find method ${compiledData.funName}")
}
private fun boxOrUnboxArgumentIfNeeded(eval: JDIEval, argumentValue: Value, parameterType: Type): Value {
val argumentType = argumentValue.asmType
if (AsmUtil.isPrimitive(parameterType) && !AsmUtil.isPrimitive(argumentType)) {
try {
val unboxedType = AsmUtil.unboxType(argumentType)
if (parameterType == unboxedType) {
return eval.unboxType(argumentValue, parameterType)
}
}
catch(ignored: UnsupportedOperationException) {
}
}
if (!AsmUtil.isPrimitive(parameterType) && AsmUtil.isPrimitive(argumentType)) {
if (parameterType == AsmUtil.boxType(argumentType)) {
return eval.boxType(argumentValue)
}
}
return argumentValue
}
private fun InterpreterResult.toJdiValue(vm: VirtualMachine): com.sun.jdi.Value? {
val jdiValue = when (this) {
is ValueReturned -> result
@@ -0,0 +1,9 @@
LineBreakpoint created at boxParam.kt:13
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! boxParam.BoxParamPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
boxParam.kt:13
Compile bytecode for nullableInt?.plus(1)
Compile bytecode for nullableByte?.plus(1)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at unboxParam.kt:12
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! unboxParam.UnboxParamPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
unboxParam.kt:12
Compile bytecode for fooInt(nullableInt)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,14 @@
package boxParam
fun main(args: Array<String>) {
val nullableInt: Int? = 1
val nullableByte: Byte? = 1
// EXPRESSION: nullableInt?.plus(1)
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
// EXPRESSION: nullableByte?.plus(1)
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
//Breakpoint!
val i = 1
}
@@ -0,0 +1,17 @@
package unboxParam
fun main(args: Array<String>) {
val nullableInt = fooNullableInt()
if (nullableInt == null) {
return
}
// EXPRESSION: fooInt(nullableInt)
// RESULT: 1: I
//Breakpoint!
val a = fooInt(nullableInt)
}
fun fooNullableInt(): Int? = 1
fun fooInt(param: Int) = param
@@ -55,6 +55,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
doSingleBreakpointTest(fileName);
}
@TestMetadata("boxParam.kt")
public void testBoxParam() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/boxParam.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("callableBug.kt")
public void testCallableBug() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/callableBug.kt");
@@ -217,6 +223,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
doSingleBreakpointTest(fileName);
}
@TestMetadata("unboxParam.kt")
public void testUnboxParam() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("unsafeCall.kt")
public void testUnsafeCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unsafeCall.kt");