JVM IR: fix "step over" for inline function calls in conditions
Reimplement the same hacky approach used in the old backend (see
cc2fe6b0c6).
Previously, the debugger incorrectly stepped into Collections.kt on
"step over" inline function calls from stdlib like 'any'.
Since `if` and `when` expressions are represented the same way in IR,
the behavior is fixed for both of them. It's not the case in the old JVM
backend, where stepping over `when` conditions still suffers from the
same problem, which the newly added test checks.
This commit is contained in:
+17
-4
@@ -131,6 +131,8 @@ class ExpressionCodegen(
|
||||
override var lastLineNumber: Int = -1
|
||||
var noLineNumberScope: Boolean = false
|
||||
|
||||
private var isInsideCondition = false
|
||||
|
||||
private val closureReifiedMarkers = hashMapOf<IrClass, ReifiedTypeParametersUsages>()
|
||||
|
||||
private val IrType.asmType: Type
|
||||
@@ -429,7 +431,7 @@ class ExpressionCodegen(
|
||||
val generatorForActualCall =
|
||||
// Do not inline callee to continuation, instead, call it
|
||||
if (irFunction.isInvokeSuspendOfContinuation()) IrCallGenerator.DefaultCallGenerator else callGenerator
|
||||
generatorForActualCall.genCall(callable, this, expression)
|
||||
generatorForActualCall.genCall(callable, this, expression, isInsideCondition)
|
||||
|
||||
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||
addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
|
||||
@@ -829,7 +831,10 @@ class ExpressionCodegen(
|
||||
if (branch.condition.isFalseConst())
|
||||
continue // The branch body is dead code.
|
||||
} else {
|
||||
val oldIsInsideCondition = isInsideCondition
|
||||
isInsideCondition = true
|
||||
branch.condition.accept(this, data).coerceToBoolean().jumpIfFalse(elseLabel)
|
||||
isInsideCondition = oldIsInsideCondition
|
||||
}
|
||||
val result = branch.result.accept(this, data)
|
||||
if (!exhaustive) {
|
||||
@@ -1250,9 +1255,17 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
override fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean) {
|
||||
// Inline function has its own line number which is in a separate instance of codegen,
|
||||
// therefore we need to reset lastLineNumber to force a line number generation after visiting inline function.
|
||||
lastLineNumber = -1
|
||||
if (noLineNumberScope || registerLineNumberAfterwards) {
|
||||
if (lastLineNumber > -1) {
|
||||
val label = Label()
|
||||
v.visitLabel(label)
|
||||
v.visitLineNumber(lastLineNumber, label)
|
||||
}
|
||||
} else {
|
||||
// Inline function has its own line number which is in a separate instance of codegen,
|
||||
// therefore we need to reset lastLineNumber to force a line number generation after visiting inline function.
|
||||
lastLineNumber = -1
|
||||
}
|
||||
}
|
||||
|
||||
fun isFinallyMarkerRequired(): Boolean {
|
||||
|
||||
+6
-1
@@ -22,7 +22,12 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
interface IrCallGenerator {
|
||||
fun genCall(callableMethod: IrCallableMethod, codegen: ExpressionCodegen, expression: IrFunctionAccessExpression) {
|
||||
fun genCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
) {
|
||||
with(callableMethod) {
|
||||
codegen.mv.visitMethodInsn(invokeOpcode, owner.internalName, asmMethod.name, asmMethod.descriptor, isInterfaceMethod)
|
||||
}
|
||||
|
||||
+7
-4
@@ -5,10 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
@@ -143,7 +145,8 @@ class IrInlineCodegen(
|
||||
override fun genCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
) {
|
||||
val element = codegen.context.psiSourceManager.findPsiElement(expression, codegen.irFunction)
|
||||
?: codegen.context.psiSourceManager.findPsiElement(codegen.irFunction)
|
||||
@@ -161,7 +164,7 @@ class IrInlineCodegen(
|
||||
true,
|
||||
false,
|
||||
codegen.typeMapper.typeSystem,
|
||||
registerLineNumberAfterwards = false,
|
||||
registerLineNumberAfterwards = isInsideIfCondition,
|
||||
isCallOfFunctionInCorrespondingDefaultDispatch = codegen.irFunction == codegen.context.mapping.defaultArgumentsDispatchFunction[function]
|
||||
)
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//FILE: test.kt
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
|
||||
fun box() {
|
||||
if (listOf(1, 2, 3).myAny { it > 2 }) {
|
||||
@@ -14,4 +14,4 @@ public inline fun <T> Iterable<T>.myAny(predicate: (T) -> Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3 LINENUMBER 5
|
||||
// 3 LINENUMBER 5
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// FILE: test.kt
|
||||
fun box() {
|
||||
if (inlineFun()) {
|
||||
nop()
|
||||
}
|
||||
|
||||
if (
|
||||
inlineFun().rid() &&
|
||||
inlineFun()
|
||||
) {
|
||||
nop()
|
||||
}
|
||||
|
||||
if (
|
||||
id(
|
||||
inlineFun()
|
||||
)
|
||||
) {
|
||||
nop()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun inlineFun(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun id(x: Boolean): Boolean = x
|
||||
inline fun Boolean.rid(): Boolean = this
|
||||
|
||||
fun nop() {}
|
||||
|
||||
// LINENUMBERS
|
||||
// test.kt:3 box
|
||||
// test.kt:24 box
|
||||
// test.kt:3 box
|
||||
// test.kt:4 box
|
||||
// test.kt:30 nop
|
||||
// test.kt:8 box
|
||||
// test.kt:24 box
|
||||
// test.kt:8 box
|
||||
// test.kt:28 box
|
||||
// test.kt:8 box
|
||||
// test.kt:9 box
|
||||
// test.kt:24 box
|
||||
// test.kt:9 box
|
||||
// test.kt:11 box
|
||||
// test.kt:30 nop
|
||||
// test.kt:16 box
|
||||
// test.kt:24 box
|
||||
// test.kt:16 box
|
||||
// test.kt:15 box
|
||||
// test.kt:27 box
|
||||
// test.kt:15 box
|
||||
// test.kt:19 box
|
||||
// test.kt:30 nop
|
||||
// test.kt:21 box
|
||||
@@ -0,0 +1,71 @@
|
||||
// FILE: test.kt
|
||||
fun box() {
|
||||
val x = value()
|
||||
when (x) {
|
||||
x0() -> nop()
|
||||
x1() -> nop()
|
||||
x2() -> nop()
|
||||
x3() -> nop()
|
||||
else -> nop()
|
||||
}
|
||||
|
||||
when (x0() + x1()) {
|
||||
x0().rid() -> nop()
|
||||
id(x1()) -> nop()
|
||||
else -> nop()
|
||||
}
|
||||
}
|
||||
|
||||
fun value(): Int = 2
|
||||
inline fun x0(): Int = 0
|
||||
inline fun x1(): Int = 1
|
||||
inline fun x2(): Int = 2
|
||||
inline fun x3(): Int = 3
|
||||
|
||||
inline fun id(x: Int): Int = x
|
||||
inline fun Int.rid(): Int = this
|
||||
|
||||
fun nop() {}
|
||||
|
||||
// JVM_IR generates an additional line number for the end of the condition, which is necessary for the correct "step over" behavior.
|
||||
|
||||
// LINENUMBERS
|
||||
// test.kt:3 box
|
||||
// test.kt:19 value
|
||||
// test.kt:3 box
|
||||
// test.kt:4 box
|
||||
// test.kt:5 box
|
||||
// test.kt:20 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:5 box
|
||||
// LINENUMBERS
|
||||
// test.kt:6 box
|
||||
// test.kt:21 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:6 box
|
||||
// LINENUMBERS
|
||||
// test.kt:7 box
|
||||
// test.kt:22 box
|
||||
// test.kt:7 box
|
||||
// test.kt:28 nop
|
||||
// test.kt:7 box
|
||||
// test.kt:12 box
|
||||
// test.kt:20 box
|
||||
// test.kt:12 box
|
||||
// test.kt:21 box
|
||||
// test.kt:12 box
|
||||
// test.kt:13 box
|
||||
// test.kt:20 box
|
||||
// test.kt:13 box
|
||||
// test.kt:26 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:13 box
|
||||
// LINENUMBERS
|
||||
// test.kt:14 box
|
||||
// test.kt:21 box
|
||||
// test.kt:14 box
|
||||
// test.kt:25 box
|
||||
// test.kt:14 box
|
||||
// test.kt:28 nop
|
||||
// test.kt:14 box
|
||||
// test.kt:17 box
|
||||
+12
@@ -187,6 +187,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest {
|
||||
runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ifWithInlineInCondition.kt")
|
||||
public void testIfWithInlineInCondition() throws Exception {
|
||||
runTest("compiler/testData/debug/stepping/ifWithInlineInCondition.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("iincStepping.kt")
|
||||
public void testIincStepping() throws Exception {
|
||||
@@ -475,6 +481,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest {
|
||||
runTest("compiler/testData/debug/stepping/whenSubject2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("whenWithInlineInCondition.kt")
|
||||
public void testWhenWithInlineInCondition() throws Exception {
|
||||
runTest("compiler/testData/debug/stepping/whenWithInlineInCondition.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("while.kt")
|
||||
public void testWhile() throws Exception {
|
||||
|
||||
+12
@@ -187,6 +187,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
|
||||
runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ifWithInlineInCondition.kt")
|
||||
public void testIfWithInlineInCondition() throws Exception {
|
||||
runTest("compiler/testData/debug/stepping/ifWithInlineInCondition.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("iincStepping.kt")
|
||||
public void testIincStepping() throws Exception {
|
||||
@@ -475,6 +481,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
|
||||
runTest("compiler/testData/debug/stepping/whenSubject2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("whenWithInlineInCondition.kt")
|
||||
public void testWhenWithInlineInCondition() throws Exception {
|
||||
runTest("compiler/testData/debug/stepping/whenWithInlineInCondition.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("while.kt")
|
||||
public void testWhile() throws Exception {
|
||||
|
||||
+5
@@ -607,6 +607,11 @@ public class IrKotlinSteppingTestGenerated extends AbstractIrKotlinSteppingTest
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineLibFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("soInlineLibFunInWhen.kt")
|
||||
public void testSoInlineLibFunInWhen() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineLibFunInWhen.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("soInlineOperatorIterator.kt")
|
||||
public void testSoInlineOperatorIterator() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineOperatorIterator.kt");
|
||||
|
||||
+5
@@ -607,6 +607,11 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineLibFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("soInlineLibFunInWhen.kt")
|
||||
public void testSoInlineLibFunInWhen() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineLibFunInWhen.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("soInlineOperatorIterator.kt")
|
||||
public void testSoInlineOperatorIterator() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/soInlineOperatorIterator.kt");
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
LineBreakpoint created at soInlineLibFun.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
LineBreakpoint created at soInlineLibFunInWhen.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
soInlineLibFunInWhen.kt:6
|
||||
soInlineLibFunInWhen.kt:8
|
||||
soInlineLibFunInWhen.kt:9
|
||||
soInlineLibFunInWhen.kt:10
|
||||
soInlineLibFunInWhen.kt:12
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// FILE: soInlineLibFunInWhen.kt
|
||||
package soInlineLibFunInWhen
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint!
|
||||
val l = listOf(1, 2, 3)
|
||||
|
||||
when {
|
||||
l.any { it > 4 } -> nop()
|
||||
l.any { it > 2 } -> nop()
|
||||
}
|
||||
}
|
||||
|
||||
fun nop() {}
|
||||
|
||||
// STEP_OVER: 7
|
||||
// Note: on the old JVM backend, debugger incorrectly steps into Collections.kt even though we're stepping over it. This is fixed in JVM_IR.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
LineBreakpoint created at soInlineLibFunInWhen.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
soInlineLibFunInWhen.kt:6
|
||||
soInlineLibFunInWhen.kt:8
|
||||
soInlineLibFunInWhen.kt:9
|
||||
soInlineLibFunInWhen.kt:10
|
||||
_Collections.!EXT!
|
||||
soInlineLibFunInWhen.kt:10
|
||||
soInlineLibFunInWhen.kt:12
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
Vendored
+2
@@ -16,3 +16,5 @@ inline fun foo(f: () -> Boolean): Boolean = f()
|
||||
fun test(i: Int): Boolean = true
|
||||
|
||||
fun bar() {}
|
||||
|
||||
// STEP_OVER: 5
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@ Run Java
|
||||
Connected to the target VM
|
||||
soSimpleInlineIfCondition.kt:5
|
||||
soSimpleInlineIfCondition.kt:8
|
||||
soSimpleInlineIfCondition.kt:11
|
||||
soSimpleInlineIfCondition.kt:12
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
Reference in New Issue
Block a user