diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index cebc835a0ca..ed2f98cfcee 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -101,7 +101,10 @@ public class JvmCodegenUtil { } public static boolean isConst(@NotNull CalculatedClosure closure) { - return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty(); + return closure.getCaptureThis() == null && + closure.getCaptureReceiverType() == null && + closure.getCaptureVariables().isEmpty() && + !closure.isCoroutine(); } private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java index 2c2aab70d23..33d699ca47a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java @@ -40,4 +40,6 @@ public interface CalculatedClosure { @NotNull List> getRecordedFields(); + + boolean isCoroutine(); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 0e42ca27cbf..5285be75a4c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt; import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; +import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; @@ -270,7 +271,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { String name = inventAnonymousClassName(); Collection supertypes = runtimeTypes.getSupertypesForClosure(functionDescriptor); ClassDescriptor classDescriptor = recordClassForCallable(functionLiteral, functionDescriptor, supertypes, name); - recordClosure(classDescriptor, name); + MutableClosure closure = recordClosure(classDescriptor, name); + + if (CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor) != null) { + closure.setCoroutine(true); + } classStack.push(classDescriptor); nameStack.push(name); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java index dd2a011917f..922dadbf73b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java @@ -39,6 +39,7 @@ public final class MutableClosure implements CalculatedClosure { private Map parameterOffsetInConstructor; private List> recordedFields; private KotlinType captureReceiverType; + private boolean isCoroutine; MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) { this.enclosingClass = enclosingClass; @@ -109,6 +110,15 @@ public final class MutableClosure implements CalculatedClosure { return recordedFields != null ? recordedFields : Collections.>emptyList(); } + @Override + public boolean isCoroutine() { + return isCoroutine; + } + + public void setCoroutine(boolean coroutine) { + this.isCoroutine = coroutine; + } + public void recordField(String name, Type type) { if (recordedFields == null) { recordedFields = new LinkedList>(); diff --git a/compiler/testData/codegen/box/coroutines/emptyClosure.kt b/compiler/testData/codegen/box/coroutines/emptyClosure.kt new file mode 100644 index 00000000000..409b9163d2a --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/emptyClosure.kt @@ -0,0 +1,26 @@ +var result = 0 + +class Controller { + suspend fun suspendHere(x: Continuation) { + result++ + x.resume("OK") + } +} + + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + + for (i in 1..3) { + builder { + if (suspendHere() != "OK") throw java.lang.RuntimeException("fail 1") + } + } + + if (result != 3) return "fail 2: $result" + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/constCoroutine.kt b/compiler/testData/codegen/bytecodeText/constCoroutine.kt new file mode 100644 index 00000000000..d1c1a02e2d4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constCoroutine.kt @@ -0,0 +1,23 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + } +} + + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + + for (i in 1..3) { + builder { + if (suspendHere() != "OK") throw java.lang.RuntimeException("fail 1") + } + } + + return "OK" +} + +// 1 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit; +// 1 GETSTATIC diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index ecb2629a266..386991099bb 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4159,6 +4159,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("emptyClosure.kt") + public void testEmptyClosure() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/emptyClosure.kt"); + doTest(fileName); + } + @TestMetadata("generate.kt") public void testGenerate() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/generate.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index a0faa90367f..e3945a589c0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -113,6 +113,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("constCoroutine.kt") + public void testConstCoroutine() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constCoroutine.kt"); + doTest(fileName); + } + @TestMetadata("falseSmartCast.kt") public void testFalseSmartCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/falseSmartCast.kt");