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 77e460d6511..6d9f6fab627 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -63,7 +63,7 @@ class CoroutineTransformerMethodVisitor( private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState) - private val continuationIndex = if (isForNamedFunction) getLastParameterIndex(desc, access) else 0 + private var continuationIndex = if (isForNamedFunction) -1 else 0 private var dataIndex = if (isForNamedFunction) -1 else 1 private var exceptionIndex = if (isForNamedFunction) -1 else 2 @@ -83,6 +83,7 @@ class CoroutineTransformerMethodVisitor( dataIndex = methodNode.maxLocals++ exceptionIndex = methodNode.maxLocals++ + continuationIndex = methodNode.maxLocals++ prepareMethodNodePreludeForNamedFunction(methodNode) } @@ -194,6 +195,13 @@ class CoroutineTransformerMethodVisitor( private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode) { val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName) + val continuationArgumentIndex = getLastParameterIndex(methodNode.desc, methodNode.access) + methodNode.instructions.asSequence().filterIsInstance().forEach { + if (it.`var` != continuationArgumentIndex) return@forEach + assert(it.opcode == Opcodes.ALOAD) { "Only ALOADs are allowed for continuation arguments" } + it.`var` = continuationIndex + } + methodNode.instructions.insert(withInstructionAdapter { val createStateInstance = Label() val afterCoroutineStateCreated = Label() @@ -212,11 +220,12 @@ class CoroutineTransformerMethodVisitor( // - Otherwise it's still can be a recursive call. To check it's not the case we set the last bit in the label in // `doResume` just before calling the suspend function (see kotlin.coroutines.experimental.jvm.internal.CoroutineImplForNamedFunction). // So, if it's set we're in continuation. - visitVarInsn(Opcodes.ALOAD, continuationIndex) + + visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex) instanceOf(objectTypeForState) ifeq(createStateInstance) - visitVarInsn(Opcodes.ALOAD, continuationIndex) + visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex) checkcast(objectTypeForState) visitVarInsn(Opcodes.ASTORE, continuationIndex) diff --git a/compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt b/compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt new file mode 100644 index 00000000000..6185ede2818 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import helpers.* +// TREAT_AS_ONE_FILE +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* +suspend fun suspendHere(): String = suspendCoroutineOrReturn { x -> + x.resume("OK") +} + +suspend fun suspendThere(param: Int, param2: String, param3: Long): String { + val a = suspendHere() + val b = suspendHere() + return a + b +} + +/* 2 stores happen because the continuation parameter is visible in debug and should be spilled */ +// 2 ASTORE 4 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 1e68a49a0a2..54bf3934f7f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1037,6 +1037,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("doNotReassignContinuation.kt") + public void testDoNotReassignContinuation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt"); + doTest(fileName); + } + @TestMetadata("varValueConflictsWithTable.kt") public void testVarValueConflictsWithTable() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTable.kt");