diff --git a/compiler/testData/codegen/box/coroutines/await.kt b/compiler/testData/codegen/box/coroutines/await.kt new file mode 100644 index 00000000000..79ee8edaa77 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/await.kt @@ -0,0 +1,117 @@ +// WITH_RUNTIME +// NO_INTERCEPT_RESUME_TESTS +// FILE: promise.kt +class Promise(private val executor: ((T) -> Unit) -> Unit) { + private var value: Any? = null + private var thenList: MutableList<(T) -> Unit>? = mutableListOf() + + init { + executor { + value = it + for (resolve in thenList!!) { + resolve(it) + } + thenList = null + } + } + + fun then(onFulfilled: (T) -> S): Promise { + return Promise { resolve -> + if (thenList != null) { + thenList!!.add { resolve(onFulfilled(it)) } + } + else { + resolve(onFulfilled(value as T)) + } + } + } +} + +// FILE: queue.kt + +private val queue = mutableListOf<() -> Unit>() + +fun postpone(computation: () -> T): Promise { + return Promise { resolve -> + queue += { + resolve(computation()) + } + } +} + +fun processQueue() { + while (queue.isNotEmpty()) { + queue.removeAt(0)() + } +} + +// FILE: await.kt +private var log = "" + +class Controller(private val resolve: (T) -> Unit) { + operator fun handleResult(result: T, c: Continuation) { + resolve(result) + } +} + +private var inAwait = false + +suspend fun await(value: Promise): S = suspendWithCurrentContinuation { continuation: Continuation -> + if (inAwait) { + throw IllegalStateException("Can't call await recursively") + } + inAwait = true + postpone { + value.then { result -> + continuation.resume(result) + } + } + inAwait = false + Suspend +} + +suspend fun awaitAndLog(value: Promise): S { + log += "before await;" + return await(value.then { result -> + log += "after await: $result;" + result + }) +} + +fun async(coroutine c: Controller.() -> Continuation): Promise { + return Promise { resolve -> + val controller = Controller { resolve(it) } + c(controller).resume(Unit) + } +} + +fun asyncOperation(resultSupplier: () -> T) = Promise { resolve -> + log += "before async;" + postpone { + val result = resultSupplier() + log += "after async $result;" + resolve(result) + } +} + +fun getLog() = log + +// FILE: main.kt + +private fun test() = async { + val o = await(asyncOperation { "O" }) + val k = awaitAndLog(asyncOperation { "K" }) + return@async o + k +} + +fun box(): String { + val resultPromise = test() + var result: String? = null + resultPromise.then { result = it } + processQueue() + + if (result != "OK") return "fail1: $result" + if (getLog() != "before async;after async O;before async;before await;after async K;after await: K;") return "fail2: ${getLog()}" + + return "OK" +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index a617bdcaa9c..f17e1e16628 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -4493,6 +4493,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("await.kt") + public void testAwait() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); + doTest(fileName); + } + @TestMetadata("beginWithException.kt") public void testBeginWithException() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithException.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 0eff7853017..2772f722f2d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4493,6 +4493,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("await.kt") + public void testAwait() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); + doTest(fileName); + } + @TestMetadata("beginWithException.kt") public void testBeginWithException() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithException.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 91bf7b928dd..1c7e4603d50 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -4493,6 +4493,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("await.kt") + public void testAwait() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); + doTest(fileName); + } + @TestMetadata("beginWithException.kt") public void testBeginWithException() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithException.kt"); @@ -4595,6 +4601,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("interceptResume.kt") + public void testInterceptResume() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/interceptResume.kt"); + doTest(fileName); + } + @TestMetadata("iterateOverArray.kt") public void testIterateOverArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 07cbbbda277..10ae4847e83 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -5310,6 +5310,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("await.kt") + public void testAwait() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); + doTest(fileName); + } + @TestMetadata("beginWithException.kt") public void testBeginWithException() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithException.kt");