diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 4d71709da61..26e7d78fb87 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -1239,11 +1239,26 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: val endLabel = insn as? LabelNode ?: insn.findNextOrNull { it is LabelNode } as? LabelNode ?: continue // startLabel can be null in case of parameters @Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start - // No LINENUMBER in range -> no way to put a breakpoint -> do not bother adding a record - if (InsnSequence(startLabel, endLabel).none { it is LineNumberNode }) continue - method.localVariables.add( - LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) - ) + var recordToExtend: LocalVariableNode? = null + for (record in method.localVariables) { + if (record.name == lvtRecord.name && + record.desc == lvtRecord.desc && + record.signature == lvtRecord.signature && + record.index == lvtRecord.index + ) { + if (InsnSequence(record.end, startLabel).none { isBeforeSuspendMarker(it) }) { + recordToExtend = record + break + } + } + } + if (recordToExtend != null) { + recordToExtend.end = endLabel + } else { + method.localVariables.add( + LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) + ) + } } } } diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java index 203b9654741..5b826190c6f 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java @@ -1431,6 +1431,11 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/coroutines/effectivelyInlineOnly.kt"); } + @TestMetadata("mergeLvt.kt") + public void testMergeLvt() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt"); + } + @TestMetadata("nonLocalReturn.kt") public void testNonLocalReturn() throws Exception { runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt"); diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/dataClass.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/dataClass.kt index 606349833cf..55270f96d5d 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/dataClass.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/dataClass.kt @@ -18,6 +18,7 @@ suspend fun foo(data: Data, body: suspend (Data) -> Unit) { // METHOD : DataClassKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // JVM_TEMPLATES +// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LData; INDEX=2 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=y_param TYPE=I INDEX=4 // VARIABLE : NAME=this TYPE=LDataClassKt$test$2; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/extensionComponents.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/extensionComponents.kt index d50235a0b16..329c7a95838 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/extensionComponents.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/extensionComponents.kt @@ -28,6 +28,7 @@ suspend fun test() = B.bar() // JVM_TEMPLATES // METHOD : ExtensionComponentsKt$bar$3.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; +// VARIABLE : NAME=$dstr$x_param$y_param$z_param TYPE=LA; INDEX=2 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=4 // VARIABLE : NAME=z_param TYPE=I INDEX=5 diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/generic.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/generic.kt index 42e3451e76c..128ad21f6ba 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/generic.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/generic.kt @@ -15,6 +15,7 @@ suspend fun test() = foo(A("OK", 1)) { (x_param, y_param) -> // METHOD : GenericKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // JVM_TEMPLATES +// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=2 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=y_param TYPE=I INDEX=4 // VARIABLE : NAME=this TYPE=LGenericKt$test$2; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/otherParameters.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/otherParameters.kt index 5cdaa5ccfa4..6e9909a9254 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/otherParameters.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/otherParameters.kt @@ -15,6 +15,7 @@ suspend fun test() = foo(A("O", "K")) { i_param, (x_param, y_param), v_param -> // JVM_TEMPLATES // VARIABLE : NAME=i_param TYPE=I INDEX=2 +// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=3 // VARIABLE : NAME=v_param TYPE=Ljava/lang/String; INDEX=4 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=5 // VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=6 diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/parameters.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/parameters.kt index b1dca03400e..3572a208fe1 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/parameters.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/parameters.kt @@ -20,6 +20,7 @@ suspend fun foo(data: Data, body: suspend Long.(String, Data, Int) -> Unit) { // JVM_TEMPLATES // VARIABLE : NAME=$this$foo TYPE=J INDEX=2 // VARIABLE : NAME=str TYPE=Ljava/lang/String; INDEX=4 +// VARIABLE : NAME=$dstr$x$_u24__u24$z TYPE=LData; INDEX=5 // VARIABLE : NAME=i TYPE=I INDEX=6 // VARIABLE : NAME=x TYPE=Ljava/lang/String; INDEX=7 // VARIABLE : NAME=z TYPE=I INDEX=8 diff --git a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/underscoreNames.kt b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/underscoreNames.kt index 6bb5f313fe8..25244e4d07a 100644 --- a/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/underscoreNames.kt +++ b/compiler/testData/checkLocalVariablesTable/parametersInSuspendLambda/underscoreNames.kt @@ -18,6 +18,7 @@ suspend fun test() = foo(A()) { (x_param, _, y_param) -> // METHOD : UnderscoreNamesKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // JVM_TEMPLATES +// VARIABLE : NAME=$dstr$x_param$_u24__u24$y_param TYPE=LA; INDEX=2 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=4 // VARIABLE : NAME=this TYPE=LUnderscoreNamesKt$test$2; INDEX=0 diff --git a/compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt b/compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt new file mode 100644 index 00000000000..71f61f61bb4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt @@ -0,0 +1,25 @@ +import kotlin.coroutines.intrinsics.* + +class AtomicInt(val value: Int) + +fun atomic(i: Int) = AtomicInt(i) + +class MyBlockingAdapter() { + private val state = atomic(0) + private val a = 77 + suspend fun foo() { + val a = suspendBar() + } + private inline fun AtomicInt.extensionFun() { + if (a == 77) throw IllegalStateException("AAAAAAAAAAAA") + value + } + private suspend inline fun suspendBar() { + state.extensionFun() + suspendCoroutineUninterceptedOrReturn { ucont -> + COROUTINE_SUSPENDED + } + } +} + +// 1 LOCALVARIABLE \$this\$extensionFun\$iv\$iv LAtomicInt; diff --git a/compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTableSameSort.kt b/compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTableSameSort.kt index bdfd1f67f0d..68e482c8b3e 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTableSameSort.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTableSameSort.kt @@ -42,8 +42,8 @@ fun box(): String { } // 1 LOCALVARIABLE i Ljava/lang/String; L.* 3 -// From liveness point of view, 's' is dead between 'println' and 's == "OK"', thus the range is split -// 2 LOCALVARIABLE s Ljava/lang/String; L.* 3 +// We merge LVT records for two consequent branches. +// 1 LOCALVARIABLE s Ljava/lang/String; L.* 3 // 1 PUTFIELD VarValueConflictsWithTableSameSortKt\$box\$1.L\$0 : Ljava/lang/Object; /* 1 load in the catch (e: Throwable) { throw e } block which is implicitly wrapped around try/finally */ // 1 ALOAD 3\s+ATHROW diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 19f1f272e6a..50b8a261cb1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1421,6 +1421,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/coroutines/effectivelyInlineOnly.kt"); } + @TestMetadata("mergeLvt.kt") + public void testMergeLvt() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt"); + } + @TestMetadata("nonLocalReturn.kt") public void testNonLocalReturn() throws Exception { runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index d618016320b..a76aa94e511 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -1431,6 +1431,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/coroutines/effectivelyInlineOnly.kt"); } + @TestMetadata("mergeLvt.kt") + public void testMergeLvt() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/coroutines/mergeLvt.kt"); + } + @TestMetadata("nonLocalReturn.kt") public void testNonLocalReturn() throws Exception { runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt");