diff --git a/kotlin-native/backend.native/tests/objcexport/coroutines.kt b/kotlin-native/backend.native/tests/objcexport/coroutines.kt index 3acf45144b7..2e361cc90f4 100644 --- a/kotlin-native/backend.native/tests/objcexport/coroutines.kt +++ b/kotlin-native/backend.native/tests/objcexport/coroutines.kt @@ -222,4 +222,25 @@ suspend fun invoke1(block: suspend (Any?) -> Any?, argument: Any?): Any? = block fun getKSuspendCallableReference0(): KSuspendFunction0 = ::suspendCallableReference0Target fun getKSuspendCallableReference1(): KSuspendFunction1 = ::suspendCallableReference1Target +@Throws(Throwable::class) +fun startCoroutineUninterceptedOrReturn(fn: suspend () -> Any?, resultHolder: ResultHolder) = + fn.startCoroutineUninterceptedOrReturn(ResultHolderCompletion(resultHolder)) + +@Throws(Throwable::class) +fun startCoroutineUninterceptedOrReturn(fn: suspend Any?.() -> Any?, receiver: Any?, resultHolder: ResultHolder) = + fn.startCoroutineUninterceptedOrReturn(receiver, ResultHolderCompletion(resultHolder)) + +@Throws(Throwable::class) +@Suppress("INVISIBLE_MEMBER") +fun startCoroutineUninterceptedOrReturn(fn: suspend Any?.(Any?) -> Any?, receiver: Any?, param: Any?, resultHolder: ResultHolder) = + fn.startCoroutineUninterceptedOrReturn(receiver, param, ResultHolderCompletion(resultHolder)) + +@Throws(Throwable::class) +fun createCoroutineUninterceptedAndResume(fn: suspend () -> Any?, resultHolder: ResultHolder) = + fn.createCoroutine(ResultHolderCompletion(resultHolder)).resume(Unit) + +@Throws(Throwable::class) +fun createCoroutineUninterceptedAndResume(fn: suspend Any?.() -> Any?, receiver: Any?, resultHolder: ResultHolder) = + fn.createCoroutine(receiver, ResultHolderCompletion(resultHolder)).resume(Unit) + fun gc() = kotlin.native.internal.GC.collect() diff --git a/kotlin-native/backend.native/tests/objcexport/coroutines.swift b/kotlin-native/backend.native/tests/objcexport/coroutines.swift index 819e094ba46..7d00f76641d 100644 --- a/kotlin-native/backend.native/tests/objcexport/coroutines.swift +++ b/kotlin-native/backend.native/tests/objcexport/coroutines.swift @@ -653,12 +653,69 @@ private func testSuspendFunctionSwiftImpl() throws { try assertNil(error) } +#if NO_GENERICS +typealias AnyResultHolder = ResultHolder +#else +typealias AnyResultHolder = ResultHolder +#endif + +private extension AnyResultHolder { + func getSuccessfulResult() throws -> Any? { + try assertEquals(actual: completed, expected: 1) + try assertNil(exception) + return result + } +} + +// Reported in https://youtrack.jetbrains.com/issue/KT-51043 +private func testSuspendFunction0SwiftImplStartInKotlin() throws { + let resultHolder = AnyResultHolder() + try CoroutinesKt.startCoroutineUninterceptedOrReturn(fn: SuspendFunction0SwiftImpl(), resultHolder: resultHolder) + try assertEquals(actual: resultHolder.getSuccessfulResult() as? String, expected: "Swift") +} + +private func testSuspendFunction1SwiftImplStartInKotlin() throws { + let resultHolder = AnyResultHolder() + try CoroutinesKt.startCoroutineUninterceptedOrReturn(fn: SuspendFunction1SwiftImpl(), receiver: "receiver", resultHolder: resultHolder) + try assertEquals(actual: resultHolder.getSuccessfulResult() as? String, expected: "receiver Swift") +} + +private func testSuspendFunction2SwiftImplStartInKotlin() throws { + let resultHolder = AnyResultHolder() + try CoroutinesKt.startCoroutineUninterceptedOrReturn(fn: SuspendFunction2SwiftImpl(), receiver: "receiver", param: "param", resultHolder: resultHolder) + try assertEquals(actual: resultHolder.getSuccessfulResult() as? String, expected: "receiver param Swift") +} + +private func testSuspendFunction0SwiftImplCreateInKotlin() throws { + let resultHolder = AnyResultHolder() + try CoroutinesKt.createCoroutineUninterceptedAndResume(fn: SuspendFunction0SwiftImpl(), resultHolder: resultHolder) + try assertEquals(actual: resultHolder.getSuccessfulResult() as? String, expected: "Swift") +} + +private func testSuspendFunction1SwiftImplCreateInKotlin() throws { + let resultHolder = AnyResultHolder() + try CoroutinesKt.createCoroutineUninterceptedAndResume(fn: SuspendFunction1SwiftImpl(), receiver: "receiver", resultHolder: resultHolder) + try assertEquals(actual: resultHolder.getSuccessfulResult() as? String, expected: "receiver Swift") +} + +private class SuspendFunction0SwiftImpl : KotlinSuspendFunction0 { + func invoke(completionHandler: (Any?, Error?) -> Void) { + completionHandler("Swift", nil) + } +} + private class SuspendFunction1SwiftImpl : KotlinSuspendFunction1 { func invoke(p1: Any?, completionHandler: (Any?, Error?) -> Void) { completionHandler("\(p1 ?? "nil") Swift", nil) } } +private class SuspendFunction2SwiftImpl : KotlinSuspendFunction2 { + func invoke(p1: Any?, p2: Any?, completionHandler: (Any?, Error?) -> Void) { + completionHandler("\(p1 ?? "nil") \(p2 ?? "nil") Swift", nil) + } +} + class CoroutinesTests : SimpleTestProvider { override init() { super.init() @@ -675,5 +732,10 @@ class CoroutinesTests : SimpleTestProvider { test("TestSuspendFunctionType", testSuspendFunctionType) test("TestKSuspendFunctionType", testSuspendFunctionType) test("TestSuspendFunctionSwiftImpl", testSuspendFunctionSwiftImpl) + test("TestSuspendFunction0SwiftImplStartInKotlin", testSuspendFunction0SwiftImplStartInKotlin) + test("TestSuspendFunction1SwiftImplStartInKotlin", testSuspendFunction1SwiftImplStartInKotlin) + test("TestSuspendFunction2SwiftImplStartInKotlin", testSuspendFunction2SwiftImplStartInKotlin) + test("TestSuspendFunction0SwiftImplCreateInKotlin", testSuspendFunction0SwiftImplCreateInKotlin) + test("TestSuspendFunction1SwiftImplCreateInKotlin", testSuspendFunction1SwiftImplCreateInKotlin) } } diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h index e7d55a1d9ea..82c842b7c79 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h @@ -382,6 +382,31 @@ __attribute__((swift_name("CoroutinesKt"))) + (void)invoke1Block:(id)block argument:(id _Nullable)argument completionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("invoke1(block:argument:completionHandler:)"))); + (id)getKSuspendCallableReference0 __attribute__((swift_name("getKSuspendCallableReference0()"))); + (id)getKSuspendCallableReference1 __attribute__((swift_name("getKSuspendCallableReference1()"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver param:(id _Nullable)param resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:param:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:resultHolder:)"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:receiver:resultHolder:)"))); + (void)gc __attribute__((swift_name("gc()"))); @end diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h index b6ef75db3be..5c1cc063616 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h @@ -382,6 +382,31 @@ __attribute__((swift_name("CoroutinesKt"))) + (void)invoke1Block:(id)block argument:(id _Nullable)argument completionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("invoke1(block:argument:completionHandler:)"))); + (id)getKSuspendCallableReference0 __attribute__((swift_name("getKSuspendCallableReference0()"))); + (id)getKSuspendCallableReference1 __attribute__((swift_name("getKSuspendCallableReference1()"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver param:(id _Nullable)param resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:param:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:resultHolder:)"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:receiver:resultHolder:)"))); + (void)gc __attribute__((swift_name("gc()"))); @end diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h index ad94862b71d..83faf80c6b7 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h @@ -382,6 +382,31 @@ __attribute__((swift_name("CoroutinesKt"))) + (void)invoke1Block:(id)block argument:(id _Nullable)argument completionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("invoke1(block:argument:completionHandler:)"))); + (id)getKSuspendCallableReference0 __attribute__((swift_name("getKSuspendCallableReference0()"))); + (id)getKSuspendCallableReference1 __attribute__((swift_name("getKSuspendCallableReference1()"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (id _Nullable)startCoroutineUninterceptedOrReturnFn:(id)fn receiver:(id _Nullable)receiver param:(id _Nullable)param resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("startCoroutineUninterceptedOrReturn(fn:receiver:param:resultHolder:)"))) __attribute__((swift_error(nonnull_error))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:resultHolder:)"))); + +/** + * @note This method converts all Kotlin exceptions to errors. +*/ ++ (BOOL)createCoroutineUninterceptedAndResumeFn:(id)fn receiver:(id _Nullable)receiver resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("createCoroutineUninterceptedAndResume(fn:receiver:resultHolder:)"))); + (void)gc __attribute__((swift_name("gc()"))); @end diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/coroutines/intrinsics/IntrinsicsNative.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/coroutines/intrinsics/IntrinsicsNative.kt index a9c88b5e08d..ef48e866700 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/coroutines/intrinsics/IntrinsicsNative.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/coroutines/intrinsics/IntrinsicsNative.kt @@ -25,7 +25,25 @@ import kotlin.native.internal.* @kotlin.internal.InlineOnly public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( completion: Continuation -): Any? = (this as Function1, Any?>).invoke(completion) +): Any? { + val function = this as? Function1, Any?> + return if (function != null) + function.invoke(completion) + else + startCoroutineUninterceptedOrReturnFallback(this, completion) +} + +@Suppress("UNCHECKED_CAST") +@PublishedApi +internal fun startCoroutineUninterceptedOrReturnFallback( + function: suspend () -> T, + completion: Continuation +): Any? { + // Unlike `function`, `wrapper` class is generated and lowered entirely by Kotlin compiler, + // so the cast below will succeed. + val wrapper: suspend () -> T = { function() } + return (wrapper as Function1, Any?>).invoke(completion) +} /** * Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension. @@ -44,7 +62,26 @@ 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? { + val function = this as? Function2, Any?> + return if (function != null) + function.invoke(receiver, completion) + else + startCoroutineUninterceptedOrReturnFallback(this, receiver, completion) +} + +@Suppress("UNCHECKED_CAST") +@PublishedApi +internal fun startCoroutineUninterceptedOrReturnFallback( + function: suspend R.() -> T, + receiver: R, + completion: Continuation +): Any? { + // Unlike `function`, `wrapper` class is generated and lowered entirely by Kotlin compiler, + // so the cast below will succeed. + val wrapper: suspend R.() -> T = { this.function() } + return (wrapper as Function2, Any?>).invoke(receiver, completion) +} @Suppress("UNCHECKED_CAST") @kotlin.internal.InlineOnly @@ -52,7 +89,26 @@ internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterc receiver: R, param: P, completion: Continuation -): Any? = (this as Function3, Any?>).invoke(receiver, param, completion) +): Any? { + val function = this as? Function3, Any?> + return if (function != null) + function.invoke(receiver, param, completion) + else + startCoroutineUninterceptedOrReturnFallback(this, receiver, param, completion) +} + +@Suppress("UNCHECKED_CAST") +internal fun startCoroutineUninterceptedOrReturnFallback( + function: suspend R.(P) -> T, + receiver: R, + param: P, + completion: Continuation +): Any? { + // Unlike `function`, `wrapper` class is generated and lowered entirely by Kotlin compiler, + // so the cast below will succeed. + val wrapper: suspend R.(P) -> T = { this.function(it) } + return (wrapper as Function3, Any?>).invoke(receiver, param, completion) +} private object CoroutineSuspendedMarker