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 d32d8816e2a..2529e1a80f2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -73,6 +73,7 @@ class CoroutineTransformerMethodVisitor( // First instruction in the method node may change in case of named function val actualCoroutineStart = methodNode.instructions.first + var startLabelNode: LabelNode? = null if (isForNamedFunction) { ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode) @@ -87,7 +88,7 @@ class CoroutineTransformerMethodVisitor( } continuationIndex = methodNode.maxLocals++ - prepareMethodNodePreludeForNamedFunction(methodNode) + startLabelNode = prepareMethodNodePreludeForNamedFunction(methodNode) } else { ReturnUnitMethodTransformer.cleanUpReturnsUnitMarkers(methodNode, ReturnUnitMethodTransformer.findReturnsUnitMarks(methodNode)) } @@ -113,9 +114,9 @@ class CoroutineTransformerMethodVisitor( transformCallAndReturnContinuationLabel(it.index + 1, it.value, methodNode, suspendMarkerVarIndex) } + val defaultLabel = LabelNode() methodNode.instructions.apply { val startLabel = LabelNode() - val defaultLabel = LabelNode() val tableSwitchLabel = LabelNode() // tableswitch(this.label) @@ -152,6 +153,27 @@ class CoroutineTransformerMethodVisitor( dropSuspensionMarkers(methodNode, suspensionPoints) methodNode.removeEmptyCatchBlocks() + + if (isForNamedFunction) { + addContinuationToLvt( + methodNode, + startLabelNode.sure { "start label has not been initialized during prelude generation" }, + defaultLabel + ) + } + } + + private fun addContinuationToLvt(methodNode: MethodNode, startLabel: LabelNode, endLabel: LabelNode) { + methodNode.localVariables.add( + LocalVariableNode( + "\$continuation", + languageVersionSettings.continuationAsmType().descriptor, + null, + startLabel, + endLabel, + continuationIndex + ) + ) } private fun removeFakeContinuationConstructorCall(methodNode: MethodNode) { @@ -209,7 +231,7 @@ class CoroutineTransformerMethodVisitor( ) } - private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode) { + private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode): LabelNode { val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName) val continuationArgumentIndex = getLastParameterIndex(methodNode.desc, methodNode.access) methodNode.instructions.asSequence().filterIsInstance().forEach { @@ -218,6 +240,8 @@ class CoroutineTransformerMethodVisitor( it.`var` = continuationIndex } + val startLabel = LabelNode() + methodNode.instructions.insert(withInstructionAdapter { val createStateInstance = Label() val afterCoroutineStateCreated = Label() @@ -237,6 +261,7 @@ class CoroutineTransformerMethodVisitor( // `doResume` just before calling the suspend function (see kotlin.coroutines.experimental.jvm.internal.CoroutineImplForNamedFunction). // So, if it's set we're in continuation. + visitLabel(startLabel.label) visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex) instanceOf(objectTypeForState) ifeq(createStateInstance) @@ -288,6 +313,7 @@ class CoroutineTransformerMethodVisitor( visitVarInsn(Opcodes.ASTORE, exceptionIndex) } }) + return startLabel } private fun removeUnreachableSuspensionPointsAndExitPoints(methodNode: MethodNode, suspensionPoints: MutableList) { diff --git a/compiler/testData/codegen/bytecodeText/coroutines/debug/continuationInLvt.kt b/compiler/testData/codegen/bytecodeText/coroutines/debug/continuationInLvt.kt new file mode 100644 index 00000000000..05bf57da4e1 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/debug/continuationInLvt.kt @@ -0,0 +1,15 @@ +// LANGUAGE_VERSION: 1.3 + +suspend fun dummy() {} + +suspend fun tailCall() { + dummy() +} + +suspend fun stateMachine() { + dummy() + dummy() +} + +// for tail-calls there is no need to add continuation to LVT +// 1 LOCALVARIABLE \$continuation Lkotlin/coroutines/Continuation; L.* 2 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index dead1921340..0060254d2b8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1114,6 +1114,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines/debug"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("continuationInLvt.kt") + public void testContinuationInLvt() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/coroutines/debug/continuationInLvt.kt"); + } + @TestMetadata("probeCoroutineSuspended.kt") public void testProbeCoroutineSuspended() throws Exception { runTest("compiler/testData/codegen/bytecodeText/coroutines/debug/probeCoroutineSuspended.kt");