Fix suspend function types (i.e. suspend lambdas) in ObjCExport
#KT-40976 Fixed
This commit is contained in:
committed by
GitHub
parent
494916b19d
commit
685168780b
+6
@@ -65,6 +65,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
if (irClass.isInterface)
|
if (irClass.isInterface)
|
||||||
result = result or TF_INTERFACE
|
result = result or TF_INTERFACE
|
||||||
|
|
||||||
|
if (irClass.defaultType.isSuspendFunction()) {
|
||||||
|
result = result or TF_SUSPEND_FUNCTION
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,4 +628,5 @@ private const val TF_ACYCLIC = 2
|
|||||||
private const val TF_INTERFACE = 4
|
private const val TF_INTERFACE = 4
|
||||||
private const val TF_OBJC_DYNAMIC = 8
|
private const val TF_OBJC_DYNAMIC = 8
|
||||||
private const val TF_LEAK_DETECTOR_CANDIDATE = 16
|
private const val TF_LEAK_DETECTOR_CANDIDATE = 16
|
||||||
|
private const val TF_SUSPEND_FUNCTION = 32
|
||||||
|
|
||||||
|
|||||||
@@ -164,3 +164,15 @@ class ThrowCancellationExceptionImpl : ThrowCancellationException() {
|
|||||||
throw CancellationException()
|
throw CancellationException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSuspendLambda0(): suspend () -> String = { "lambda 0" }
|
||||||
|
|
||||||
|
private suspend fun suspendCallableReference0Target(): String = "callable reference 0"
|
||||||
|
fun getSuspendCallableReference0(): suspend () -> String = ::suspendCallableReference0Target
|
||||||
|
|
||||||
|
fun getSuspendLambda1(): suspend (String) -> String = { "$it 1" }
|
||||||
|
|
||||||
|
private suspend fun suspendCallableReference1Target(str: String): String = "$str 1"
|
||||||
|
fun getSuspendCallableReference1(): suspend (String) -> String = ::suspendCallableReference1Target
|
||||||
|
|
||||||
|
suspend fun invoke1(block: suspend (Any?) -> Any?, argument: Any?): Any? = block(argument)
|
||||||
|
|||||||
@@ -253,6 +253,71 @@ private func testImplicitThrows2() throws {
|
|||||||
try assertTrue(error?.kotlinException is KotlinCancellationException)
|
try assertTrue(error?.kotlinException is KotlinCancellationException)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func testSuspendFunctionType0(f: KotlinSuspendFunction0, expectedResult: String) throws {
|
||||||
|
try assertTrue((f as AnyObject) is KotlinSuspendFunction0)
|
||||||
|
|
||||||
|
var result: String? = nil
|
||||||
|
var error: Error? = nil
|
||||||
|
var completionCalled = 0
|
||||||
|
|
||||||
|
f.invoke { _result, _error in
|
||||||
|
completionCalled += 1
|
||||||
|
result = _result as? String
|
||||||
|
error = _error
|
||||||
|
}
|
||||||
|
|
||||||
|
try assertEquals(actual: completionCalled, expected: 1)
|
||||||
|
try assertEquals(actual: result, expected: expectedResult)
|
||||||
|
try assertNil(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func testSuspendFunctionType1(f: KotlinSuspendFunction1) throws {
|
||||||
|
try assertTrue((f as AnyObject) is KotlinSuspendFunction1)
|
||||||
|
|
||||||
|
var result: String? = nil
|
||||||
|
var error: Error? = nil
|
||||||
|
var completionCalled = 0
|
||||||
|
|
||||||
|
f.invoke(p1: "suspend function type") { _result, _error in
|
||||||
|
completionCalled += 1
|
||||||
|
result = _result as? String
|
||||||
|
error = _error
|
||||||
|
}
|
||||||
|
|
||||||
|
try assertEquals(actual: completionCalled, expected: 1)
|
||||||
|
try assertEquals(actual: result, expected: "suspend function type 1")
|
||||||
|
try assertNil(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func testSuspendFunctionType() throws {
|
||||||
|
try testSuspendFunctionType0(f: CoroutinesKt.getSuspendLambda0(), expectedResult: "lambda 0")
|
||||||
|
try testSuspendFunctionType0(f: CoroutinesKt.getSuspendCallableReference0(), expectedResult: "callable reference 0")
|
||||||
|
try testSuspendFunctionType1(f: CoroutinesKt.getSuspendLambda1())
|
||||||
|
try testSuspendFunctionType1(f: CoroutinesKt.getSuspendCallableReference1())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func testSuspendFunctionSwiftImpl() throws {
|
||||||
|
var result: String? = nil
|
||||||
|
var error: Error? = nil
|
||||||
|
var completionCalled = 0
|
||||||
|
|
||||||
|
CoroutinesKt.invoke1(block: SuspendFunction1SwiftImpl(), argument: "suspend function") { _result, _error in
|
||||||
|
completionCalled += 1
|
||||||
|
result = _result as? String
|
||||||
|
error = _error
|
||||||
|
}
|
||||||
|
|
||||||
|
try assertEquals(actual: completionCalled, expected: 1)
|
||||||
|
try assertEquals(actual: result, expected: "suspend function Swift")
|
||||||
|
try assertNil(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SuspendFunction1SwiftImpl : KotlinSuspendFunction1 {
|
||||||
|
func invoke(p1: Any?, completionHandler: (Any?, Error?) -> Void) {
|
||||||
|
completionHandler("\(p1 ?? "nil") Swift", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CoroutinesTests : SimpleTestProvider {
|
class CoroutinesTests : SimpleTestProvider {
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
@@ -263,5 +328,7 @@ class CoroutinesTests : SimpleTestProvider {
|
|||||||
test("TestBridges", testBridges)
|
test("TestBridges", testBridges)
|
||||||
test("TestImplicitThrows1", testImplicitThrows1)
|
test("TestImplicitThrows1", testImplicitThrows1)
|
||||||
test("TestImplicitThrows2", testImplicitThrows2)
|
test("TestImplicitThrows2", testImplicitThrows2)
|
||||||
|
test("TestSuspendFunctionType", testSuspendFunctionType)
|
||||||
|
test("TestSuspendFunctionSwiftImpl", testSuspendFunctionSwiftImpl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,6 +306,16 @@ __attribute__((swift_name("CoroutinesKt")))
|
|||||||
Other uncaught Kotlin exceptions are fatal.
|
Other uncaught Kotlin exceptions are fatal.
|
||||||
*/
|
*/
|
||||||
+ (void)throwCancellationExceptionWithCompletionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("throwCancellationException(completionHandler:)")));
|
+ (void)throwCancellationExceptionWithCompletionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("throwCancellationException(completionHandler:)")));
|
||||||
|
+ (id<KtKotlinSuspendFunction0>)getSuspendLambda0 __attribute__((swift_name("getSuspendLambda0()")));
|
||||||
|
+ (id<KtKotlinSuspendFunction0>)getSuspendCallableReference0 __attribute__((swift_name("getSuspendCallableReference0()")));
|
||||||
|
+ (id<KtKotlinSuspendFunction1>)getSuspendLambda1 __attribute__((swift_name("getSuspendLambda1()")));
|
||||||
|
+ (id<KtKotlinSuspendFunction1>)getSuspendCallableReference1 __attribute__((swift_name("getSuspendCallableReference1()")));
|
||||||
|
|
||||||
|
/**
|
||||||
|
@note This method converts instances of CancellationException to errors.
|
||||||
|
Other uncaught Kotlin exceptions are fatal.
|
||||||
|
*/
|
||||||
|
+ (void)invoke1Block:(id<KtKotlinSuspendFunction1>)block argument:(id _Nullable)argument completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("invoke1(block:argument:completionHandler:)")));
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
__attribute__((swift_name("FHolder")))
|
__attribute__((swift_name("FHolder")))
|
||||||
|
|||||||
@@ -539,6 +539,26 @@ static convertReferenceToObjC findConverterFromInterfaces(const TypeInfo* typeIn
|
|||||||
|
|
||||||
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
||||||
const TypeInfo* interfaceTypeInfo = typeInfo->implementedInterfaces_[i];
|
const TypeInfo* interfaceTypeInfo = typeInfo->implementedInterfaces_[i];
|
||||||
|
if ((interfaceTypeInfo->flags_ & TF_SUSPEND_FUNCTION) != 0) {
|
||||||
|
// interfaceTypeInfo is a SuspendFunction$N interface.
|
||||||
|
// So any instance of typeInfo is a suspend lambda or a suspend callable reference
|
||||||
|
// (user-defined Kotlin classes implementing SuspendFunction$N are prohibited by the compiler).
|
||||||
|
//
|
||||||
|
// Such types also actually implement Function${N+1} interface as an optimization
|
||||||
|
// (see e.g. [startCoroutineUninterceptedOrReturn implementation).
|
||||||
|
// This fact is not user-visible, so ignoring Function${N+1} interface here
|
||||||
|
// (and thus not converting such objects to Obj-C blocks) should be safe enough
|
||||||
|
// (because such objects aren't expected to be passed from Kotlin to Swift
|
||||||
|
// under formal Function${N+1} type).
|
||||||
|
//
|
||||||
|
// On the other hand, this fixes support for SuspendFunction$N type: it is mapped as
|
||||||
|
// regular Kotlin interface, so its instances should be converted on a general basis
|
||||||
|
// (i.e. to objects implementing Obj-C representation of SuspendFunction$N, not to Obj-C blocks).
|
||||||
|
//
|
||||||
|
// "If typeInfo is a suspend lambda or callable reference type, convert its instances on a regular basis":
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (interfaceTypeInfo->writableInfo_->objCExport.convert != nullptr) {
|
if (interfaceTypeInfo->writableInfo_->objCExport.convert != nullptr) {
|
||||||
if (foundTypeInfo == nullptr || IsSubInterface(interfaceTypeInfo, foundTypeInfo)) {
|
if (foundTypeInfo == nullptr || IsSubInterface(interfaceTypeInfo, foundTypeInfo)) {
|
||||||
foundTypeInfo = interfaceTypeInfo;
|
foundTypeInfo = interfaceTypeInfo;
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ enum Konan_TypeFlags {
|
|||||||
TF_ACYCLIC = 1 << 1,
|
TF_ACYCLIC = 1 << 1,
|
||||||
TF_INTERFACE = 1 << 2,
|
TF_INTERFACE = 1 << 2,
|
||||||
TF_OBJC_DYNAMIC = 1 << 3,
|
TF_OBJC_DYNAMIC = 1 << 3,
|
||||||
TF_LEAK_DETECTOR_CANDIDATE = 1 << 4
|
TF_LEAK_DETECTOR_CANDIDATE = 1 << 4,
|
||||||
|
TF_SUSPEND_FUNCTION = 1 << 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flags per object instance.
|
// Flags per object instance.
|
||||||
|
|||||||
Reference in New Issue
Block a user