diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt index fb620fd2b2d..4738c233906 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -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?): 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 diff --git a/idea/testData/debugger/tinyApp/outs/boxParam.out b/idea/testData/debugger/tinyApp/outs/boxParam.out new file mode 100644 index 00000000000..1e3be21eabf --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/boxParam.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/outs/unboxParam.out b/idea/testData/debugger/tinyApp/outs/unboxParam.out new file mode 100644 index 00000000000..afecd718b84 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/unboxParam.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/boxParam.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/boxParam.kt new file mode 100644 index 00000000000..ff8c9d01706 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/boxParam.kt @@ -0,0 +1,14 @@ +package boxParam + +fun main(args: Array) { + 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 +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt new file mode 100644 index 00000000000..cccc30ae8ff --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt @@ -0,0 +1,17 @@ +package unboxParam + +fun main(args: Array) { + 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 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 2e0fbd714c7..cebba7a2719 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -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");