diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index edf49bd9974..0fa2dbe8a35 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -12546,6 +12546,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt index e53d1af4b45..f389c2f04c8 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt @@ -55,6 +55,17 @@ fun box(): String { if (res != "OK") { return "fail 3 $res" } + res = builder(::suspendHere) + if (res != "OK") { + return "fail 4 $res" + } + res = builder { + suspend {}() + suspendHere() + } + if (res != "OK") { + return "fail 5 $res" + } return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt index 074cf0b5b8a..ff4c8603c66 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt @@ -56,6 +56,30 @@ fun box(): String { if (res != "OK") { return "fail 3 $res" } + res = c.builder { + suspend {}() + controllerSuspendHere() + } + if (res != "OK") { + return "fail 4 $res" + } + res = c.builder { + suspend {}() + controllerMultipleArgs(1, 1, 1) + } + if (res != "OK") { + return "fail 5 $res" + } + res = c.builder { + suspend {}() + if (coroutineContext != EmptyCoroutineContext) + "${coroutineContext} != $EmptyCoroutineContext" + else + "OK" + } + if (res != "OK") { + return "fail 6 $res" + } return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt index 65fd9f3c783..95d3b0a99ab 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt @@ -55,6 +55,27 @@ fun box(): String { if (res != "OK") { return "fail 3 $res" } + res = v.builder { + suspend {}() + controllerMultipleArgs(1, 1, 1) + } + if (res != "OK") { + return "fail 4 $res" + } + res = v.builder { + suspend {}() + if (coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK" + } + if (res != "OK") { + return "fail 5 $res" + } + res = v.builder { + suspend {}() + controllerSuspendHere() + } + if (res != "OK") { + return "fail 6 $res" + } return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt index 7a0b806bfee..76e97718ac2 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt @@ -1,5 +1,6 @@ // WITH_STDLIB // WITH_COROUTINES +// IGNORE_BACKEND: JS_IR, NATIVE import helpers.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* @@ -25,28 +26,24 @@ suspend fun suspendWithExceptionIntercepted(): String = suspendCoroutineUninterc COROUTINE_SUSPENDED } -fun builder(expectedCount: Int, c: suspend () -> String): String { +fun builder(testNum: Int, expectedCount: Int, c: suspend () -> String): String { var fromSuspension: String? = null var counter = 0 - val result = try { - c.startCoroutineUninterceptedOrReturn(object: ContinuationAdapter() { - override val context: CoroutineContext - get() = ContinuationDispatcher { counter++ } + c.startCoroutineUninterceptedOrReturn(object : ContinuationAdapter() { + override val context: CoroutineContext + get() = ContinuationDispatcher { counter++ } - override fun resumeWithException(exception: Throwable) { - fromSuspension = "Exception: " + exception.message!! - } + override fun resumeWithException(exception: Throwable) { + fromSuspension = "Exception: " + exception.message!! + } - override fun resume(value: String) { - fromSuspension = value - } - }) - } catch (e: Exception) { - "Exception: ${e.message}" - } + override fun resume(value: String) { + fromSuspension = value + } + }) - if (counter != expectedCount) throw RuntimeException("fail 0") + if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount") return fromSuspension!! } @@ -72,11 +69,35 @@ private class DispatchedContinuation( } fun box(): String { - if (builder(0) { suspendHereUnintercepted() } != "OK") return "fail 2" - if (builder(1) { suspendHereIntercepted() } != "OK") return "fail 3" + if (builder(0, 0) { suspendHereUnintercepted() } != "OK") return "fail 2" + if (builder(1, 1) { suspendHereIntercepted() } != "OK") return "fail 3" - if (builder(0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4" - if (builder(1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5" + if (builder(2, 0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4" + if (builder(3, 1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5" + + if (builder(4, 0, ::suspendHereUnintercepted) != "OK") return "fail 6" + if (builder(5, 1, ::suspendHereIntercepted) != "OK") return "fail 7" + + if (builder(6, 0, ::suspendWithExceptionUnintercepted) != "Exception: OK") return "fail 8" + if (builder(7, 1, ::suspendWithExceptionIntercepted) != "Exception: OK") return "fail 9" + + if (builder(8, 0) { + suspend {}() + suspendHereUnintercepted() + } != "OK") return "fail 21" + if (builder(9, 1) { + suspend {}() + suspendHereIntercepted() + } != "OK") return "fail 31" + + if (builder(10, 0) { + suspend {}() + suspendWithExceptionUnintercepted() + } != "Exception: OK") return "fail 41" + if (builder(11, 1) { + suspend {}() + suspendWithExceptionIntercepted() + } != "Exception: OK") return "fail 51" return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt new file mode 100644 index 00000000000..ba528c665d9 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt @@ -0,0 +1,113 @@ +// WITH_STDLIB +// IGNORE_BACKEND: JS_IR, NATIVE +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +var c: Continuation? = null +var interceptions: Int = 0 +var releases: Int = 0 +var invocations: Int = 0 + +suspend fun suspendHere(): String = suspendCoroutineUninterceptedOrReturn { x -> + invocations++ + c = x + COROUTINE_SUSPENDED +} + +suspend fun suspendHereIntercepted(): String = suspendCoroutineUninterceptedOrReturn { x -> + invocations++ + c = x.intercepted() + COROUTINE_SUSPENDED +} + +fun builder(testNum: Int, expectedResult: Result, c: suspend () -> String) { + c.startCoroutineUninterceptedOrReturn(object : Continuation { + override val context: CoroutineContext + get() = ContinuationDispatcher() + + override fun resumeWith(result: Result) { + if (result != expectedResult) { + error("fail $testNum: unexpected result: $expectedResult != $result") + } + } + }) +} + +class ContinuationDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { + override fun interceptContinuation(continuation: Continuation): Continuation = DispatchedContinuation(continuation) + + override fun releaseInterceptedContinuation(continuation: Continuation<*>) { + releases++ + } +} + +private class DispatchedContinuation( + val continuation: Continuation +): Continuation { + init { + interceptions++ + } + + override val context: CoroutineContext = continuation.context + + override fun resumeWith(result: Result) { + continuation.resumeWith(result) + } +} + +fun box(): String { + val success = Result.success("OK") + + builder(0, success) { suspendHere() } + if (invocations != 1) return "fail 00: $invocations" + if (interceptions != 0) return "fail 01: $interceptions" + if (releases != 0) return "fail 02: $releases" + c = c?.intercepted() + if (interceptions != 1) return "fail 03: $interceptions" + c?.resumeWith(success) + if (releases != 1) return "fail 04: $releases" + + builder(1, success) { suspendHereIntercepted() } + if (invocations != 2) return "fail 10: $invocations" + if (interceptions != 2) return "fail 11: $interceptions" + if (releases != 1) return "fail 12: $interceptions" + c?.resumeWith(success) + if (interceptions != 2) return "fail 13: $interceptions" + if (releases != 2) return "fail 14: $releases" + + builder(2, success, ::suspendHere) + if (invocations != 3) return "fail 20: $invocations" + if (interceptions != 2) return "fail 21: $interceptions" + if (releases != 2) return "fail 22: $releases" + c = c?.intercepted() + if (interceptions != 3) return "fail 23: $interceptions" + c?.resumeWith(success) + if (releases != 3) return "fail 24: $releases" + + builder(3, success, ::suspendHereIntercepted) + if (invocations != 4) return "fail 30: $invocations" + if (interceptions != 4) return "fail 31: $interceptions" + if (releases != 3) return "fail 32: $interceptions" + c?.resumeWith(success) + if (interceptions != 4) return "fail 33: $interceptions" + if (releases != 4) return "fail 34: $releases" + + builder(4, success) { suspend {}(); suspendHere() } + if (invocations != 5) return "fail 40: $invocations" + if (interceptions != 4) return "fail 41: $interceptions" + if (releases != 4) return "fail 42: $releases" + c = c?.intercepted() + if (interceptions != 5) return "fail 43: $interceptions" + c?.resumeWith(success) + if (releases != 5) return "fail 44: $releases" + + builder(5, success) { suspend {}(); suspendHereIntercepted() } + if (invocations != 6) return "fail 50: $invocations" + if (interceptions != 6) return "fail 51: $interceptions" + if (releases != 5) return "fail 52: $interceptions" + c?.resumeWith(success) + if (interceptions != 6) return "fail 53: $interceptions" + if (releases != 6) return "fail 54: $releases" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt index 155b3083ae4..4e06eeec4e6 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt @@ -37,9 +37,20 @@ fun builder(c: suspend () -> String): String { fun box(): String { if (builder { "OK" } != "OK") return "fail 4" if (builder { suspendHere() } != "OK") return "fail 5" + if (builder { + suspend {}() + suspendHere() + } != "OK") return "fail 51" if (builder { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" if (builder { suspendWithException() } != "Exception: OK") return "fail 7" + if (builder { + suspend {}() + suspendWithException() + } != "Exception: OK") return "fail 71" + + if (builder(::suspendHere) != "OK") return "fail 8" + if (builder(::suspendWithException) != "Exception: OK") return "fail 9" return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturn.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturn.kt index 36487e463e3..7028166b383 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturn.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturn.kt @@ -48,9 +48,14 @@ fun builder(shouldSuspend: Boolean, c: suspend () -> String): String { fun box(): String { if (builder(false) { "OK" } != "OK") return "fail 4" if (builder(true) { suspendHere() } != "OK") return "fail 5" + if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51" if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7" + if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71" + + if (builder(true, ::suspendHere) != "OK") return "fail 8" + if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9" return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt index f69e714faac..038e072810d 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt @@ -1,5 +1,6 @@ // WITH_STDLIB // WITH_COROUTINES +// IGNORE_BACKEND: NATIVE import helpers.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* @@ -19,7 +20,7 @@ suspend fun suspendWithException(): String = suspendCoroutine { x -> } } -fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String { +fun builder(testNum: Int, shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String { callback = {} var fromSuspension: String? = null var counter = 0 @@ -43,15 +44,15 @@ fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String) callback() - if (counter != expectedCount) throw RuntimeException("fail 0") + if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount") if (shouldSuspend) { - if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1") - if (fromSuspension == null) throw RuntimeException("fail 2") + if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1 $testNum $result") + if (fromSuspension == null) throw RuntimeException("fail 2 $testNum") return fromSuspension!! } - if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3") + if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3 $testNum") return result as String } @@ -77,11 +78,16 @@ private class DispatchedContinuation( } fun box(): String { - if (builder(false, 0) { "OK" } != "OK") return "fail 4" - if (builder(true, 1) { suspendHere() } != "OK") return "fail 5" + if (builder(0, false, 0) { "OK" } != "OK") return "fail 4" + if (builder(1, true, 1) { suspendHere() } != "OK") return "fail 5" + if (builder(2, true, 1) { suspend{}(); suspendHere() } != "OK") return "fail 51" - if (builder(false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" - if (builder(true, 1) { suspendWithException() } != "Exception: OK") return "fail 7" + if (builder(3, false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" + if (builder(4, true, 1) { suspendWithException() } != "Exception: OK") return "fail 7" + if (builder(5, true, 1) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71" + + if (builder(6, true, 1, ::suspendHere) != "OK") return "fail 8" + if (builder(7, true, 1, ::suspendWithException) != "Exception: OK") return "fail 9" return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt index d5d3d10de04..68a6a0bc6db 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt @@ -72,9 +72,14 @@ private class DispatchedContinuation( fun box(): String { if (builder(false) { "OK" } != "OK") return "fail 4" if (builder(true) { suspendHere() } != "OK") return "fail 5" + if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51" if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7" + if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71" + + if (builder(true, ::suspendHere) != "OK") return "fail 8" + if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9" return "OK" } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 2b09eb91dac..d1729ff3970 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -12336,6 +12336,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index c2d5362a52f..0e4cb8d50af 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -12546,6 +12546,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index e1320a486a7..aff4a8dafd4 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -9875,6 +9875,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/resultExceptionOrNullInLambda.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index d1ebbf38d5f..eb61490dd13 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -9158,6 +9158,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java index 252c11058ff..fc401a3621f 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java @@ -9254,6 +9254,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 76658243e22..7019e499242 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -9254,6 +9254,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 9245c62ef3a..88ac9b48056 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -8135,6 +8135,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/resultExceptionOrNullInLambda.kt"); diff --git a/libraries/stdlib/js-v1/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt b/libraries/stdlib/js-v1/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt index ac3419090b4..1cf5254b98c 100644 --- a/libraries/stdlib/js-v1/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt +++ b/libraries/stdlib/js-v1/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt @@ -24,7 +24,7 @@ import kotlin.internal.InlineOnly @InlineOnly public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( completion: Continuation -): Any? = this.asDynamic()(completion, false) +): Any? = this.asDynamic()(if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false) /** * Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension. @@ -43,14 +43,18 @@ public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrRetu public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( receiver: R, completion: Continuation -): Any? = this.asDynamic()(receiver, completion, false) +): Any? = this.asDynamic()( + receiver, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false +) @InlineOnly internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn( receiver: R, param: P, completion: Continuation -): Any? = this.asDynamic()(receiver, param, completion, false) +): Any? = this.asDynamic()( + receiver, param, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false +) /** * Creates unintercepted coroutine without receiver and with result type [T]. @@ -149,3 +153,13 @@ private inline fun createCoroutineFromSuspendFunction( } } } + +@PublishedApi +internal fun createSimpleCoroutineFromSuspendFunction( + completion: Continuation +): CoroutineImpl = object : CoroutineImpl(completion as Continuation) { + override fun doResume(): Any? { + if (exception != null) throw exception as Throwable + return result + } +} diff --git a/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt b/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt index 3e8c4323304..3c23290b8f3 100644 --- a/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt +++ b/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt @@ -29,7 +29,19 @@ import kotlin.internal.InlineOnly @InlineOnly public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( completion: Continuation -): Any? = (this as Function1, Any?>).invoke(completion) +): Any? = + // Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869 + if (this !is BaseContinuationImpl) wrapWithContinuationImpl(completion) + else (this as Function1, Any?>).invoke(completion) + +// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated]. +@PublishedApi +internal fun (suspend () -> T).wrapWithContinuationImpl( + completion: Continuation +): Any? { + val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion)) + return (this as Function1, Any?>).invoke(newCompletion) +} /** * Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension. @@ -48,14 +60,41 @@ public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrRetu public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( receiver: R, completion: Continuation -): Any? = (this as Function2, Any?>).invoke(receiver, completion) +): Any? = + // Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869 + if (this !is BaseContinuationImpl) wrapWithContinuationImpl(receiver, completion) + else (this as Function2, Any?>).invoke(receiver, completion) + +// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated]. +@PublishedApi +internal fun (suspend R.() -> T).wrapWithContinuationImpl( + receiver: R, + completion: Continuation +): Any? { + val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion)) + return (this as Function2, Any?>).invoke(receiver, newCompletion) +} @InlineOnly internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn( receiver: R, param: P, completion: Continuation -): Any? = (this as Function3, Any?>).invoke(receiver, param, completion) +): Any? = + // Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869 + if (this !is BaseContinuationImpl) wrapWithContinuationImpl(receiver, param, completion) + else (this as Function3, Any?>).invoke(receiver, param, completion) + +// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated]. +@PublishedApi +internal fun (suspend R.(P) -> T).wrapWithContinuationImpl( + receiver: R, + param: P, + completion: Continuation +): Any? { + val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion)) + return (this as Function3, Any?>).invoke(receiver, param, newCompletion) +} // JVM declarations @@ -201,3 +240,30 @@ private inline fun createCoroutineFromSuspendFunction( } } } + +/** + * This function is used when [startCoroutineUninterceptedOrReturn] encounters suspending lambda that does not extend BaseContinuationImpl. + * + * It happens in two cases: + * 1. Callable reference to suspending function or tail-call lambdas, + * 2. Suspending function reference implemented by Java code. + * + * This function is the same as above, but does not run lambda itself - the caller is expected to call [invoke] manually. + */ +private fun createSimpleCoroutineForSuspendFunction( + completion: Continuation +): Continuation { + val context = completion.context + return if (context === EmptyCoroutineContext) + object : RestrictedContinuationImpl(completion as Continuation) { + override fun invokeSuspend(result: Result): Any? { + return result.getOrThrow() + } + } + else + object : ContinuationImpl(completion as Continuation, context) { + override fun invokeSuspend(result: Result): Any? { + return result.getOrThrow() + } + } +} \ No newline at end of file diff --git a/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt index c6668bec2de..5b7eff49628 100644 --- a/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt +++ b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt @@ -24,7 +24,9 @@ import kotlin.wasm.internal.* @kotlin.internal.InlineOnly public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( completion: Continuation -): Any? = startCoroutineUninterceptedOrReturnIntrinsic0(this, completion) +): Any? = startCoroutineUninterceptedOrReturnIntrinsic0( + this, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion +) /** * Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension. @@ -43,7 +45,9 @@ public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrRetu public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( receiver: R, completion: Continuation -): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(this, receiver, completion) +): Any? = startCoroutineUninterceptedOrReturnIntrinsic1( + this, receiver, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion +) @Suppress("UNCHECKED_CAST") @kotlin.internal.InlineOnly @@ -51,7 +55,9 @@ internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterc receiver: R, param: P, completion: Continuation -): Any? = startCoroutineUninterceptedOrReturnIntrinsic2(this, receiver, param, completion) +): Any? = startCoroutineUninterceptedOrReturnIntrinsic2( + this, receiver, param, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion +) /** * Creates unintercepted coroutine without receiver and with result type [T]. @@ -137,4 +143,14 @@ private inline fun createCoroutineFromSuspendFunction( return block() } } -} \ No newline at end of file +} + +@PublishedApi +internal fun createSimpleCoroutineFromSuspendFunction( + completion: Continuation +): CoroutineImpl = object : CoroutineImpl(completion as Continuation) { + override fun doResume(): Any? { + if (exception != null) throw exception as Throwable + return result + } +} diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 7072ecf29fe..e685e3fc8ea 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3090,6 +3090,9 @@ public final class kotlin/coroutines/intrinsics/IntrinsicsKt { public static final fun createCoroutineUnintercepted (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; public static final fun getCOROUTINE_SUSPENDED ()Ljava/lang/Object; public static final fun intercepted (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; + public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function3;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlin/coroutines/jvm/internal/Boxing { diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java index dbd49ba91b1..f21101d538d 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java @@ -10259,6 +10259,12 @@ public class K2NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTes runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception { diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index dcc1b1dee3b..c382d3e01d4 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -10152,6 +10152,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt"); } + @Test + @TestMetadata("releaseIntercepted.kt") + public void testReleaseIntercepted() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt"); + } + @Test @TestMetadata("resultExceptionOrNullInLambda.kt") public void testResultExceptionOrNullInLambda() throws Exception {