Add basic support for suspend functions in produced Obj-C header

This commit is contained in:
Svyatoslav Scherbina
2020-02-21 13:49:20 +03:00
committed by SvyatoslavScherbina
parent 4e048b5e25
commit e79b055c81
22 changed files with 780 additions and 35 deletions
@@ -207,6 +207,11 @@ internal class KonanSymbols(
NoLookupLocation.FROM_BACKEND
).single())
val objCExportResumeContinuation = internalFunction("resumeContinuation")
val objCExportResumeContinuationWithException = internalFunction("resumeContinuationWithException")
val objCExportGetCoroutineSuspended = internalFunction("getCoroutineSuspended")
val objCExportInterceptedContinuation = internalFunction("interceptedContinuation")
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.getNativeNullPtr)
val boxCachePredicates = BoxCache.values().associate {
@@ -488,6 +488,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_AllocInstanceWithAssociatedObject by lazyRtFunction
val Kotlin_ObjCExport_createContinuationArgument by lazyRtFunction
val Kotlin_ObjCExport_resumeContinuation by lazyRtFunction
val tlsMode by lazy {
when (target) {
@@ -86,7 +86,7 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
generateBlockToKotlinFunctionConverter(bridge)
}
private val blockGenerator = BlockGenerator(this.codegen)
protected val blockGenerator = BlockGenerator(this.codegen)
private val functionToBlockConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>()
internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef =
@@ -123,6 +123,10 @@ internal class ObjCExportCodeGenerator(
val externalGlobalInitializers = mutableMapOf<LLVMValueRef, ConstValue>()
internal val continuationToCompletionConverter: LLVMValueRef by lazy {
generateContinuationToCompletionConverter(blockGenerator)
}
fun FunctionGenerationContext.genSendMessage(
returnType: LLVMTypeRef,
receiver: LLVMValueRef,
@@ -584,6 +588,20 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
setObjCExportTypeInfo(boxClass, constPointer(converter))
}
private fun ObjCExportCodeGenerator.generateContinuationToCompletionConverter(
blockGenerator: BlockGenerator
): LLVMValueRef = with(blockGenerator) {
generateWrapKotlinObjectToBlock(
BlockType(numberOfParameters = 2, returnsVoid = true),
convertName = "convertContinuation",
invokeName = "invokeCompletion"
) { continuation, arguments ->
check(arguments.size == 2)
callFromBridge(context.llvm.Kotlin_ObjCExport_resumeContinuation, listOf(continuation) + arguments)
ret(null)
}
}
private const val maxConvertorsInCache = 33
private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() {
@@ -760,6 +778,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
}
var errorOutPtr: LLVMValueRef? = null
var continuation: LLVMValueRef? = null
val kotlinArgs = methodBridge.paramBridges.mapIndexedNotNull { index, paramBridge ->
val parameter = param(index)
@@ -777,17 +796,22 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
errorOutPtr = parameter
null
}
MethodBridgeValueParameter.SuspendCompletion -> {
callFromBridge(
context.llvm.Kotlin_ObjCExport_createContinuationArgument,
listOf(parameter, generateExceptionTypeInfoArray(baseMethod!!)),
Lifetime.ARGUMENT
).also {
continuation = it
}
}
}
}
// TODO: consider merging this handler with function cleanup.
val exceptionHandler = if (errorOutPtr == null) {
kotlinExceptionHandler { exception ->
callFromBridge(symbols.objCExportTrapOnUndeclaredException.owner.llvmFunction, listOf(exception))
unreachable()
}
} else {
kotlinExceptionHandler { exception ->
val exceptionHandler = when {
errorOutPtr != null -> kotlinExceptionHandler { exception ->
callFromBridge(
context.llvm.Kotlin_ObjCExport_RethrowExceptionAsNSError,
listOf(exception, errorOutPtr!!, generateExceptionTypeInfoArray(baseMethod!!))
@@ -810,6 +834,22 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
ret(returnValue)
}
continuation != null -> kotlinExceptionHandler { exception ->
// Callee haven't suspended, so it isn't going to call the completion. Call it here:
callFromBridge(
context.ir.symbols.objCExportResumeContinuationWithException.owner.llvmFunction,
listOf(continuation!!, exception)
)
// Note: completion block could be called directly instead, but this implementation is
// simpler and avoids duplication.
ret(null)
}
else -> kotlinExceptionHandler { exception ->
callFromBridge(symbols.objCExportTrapOnUndeclaredException.owner.llvmFunction, listOf(exception))
unreachable()
}
}
val targetResult = callKotlin(kotlinArgs, Lifetime.ARGUMENT, exceptionHandler)
@@ -825,6 +865,23 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
is MethodBridge.ReturnValue.WithError.ZeroForError -> genReturnValueOnSuccess(returnBridge.successBridge)
MethodBridge.ReturnValue.Instance.InitResult -> param(0)
MethodBridge.ReturnValue.Instance.FactoryResult -> kotlinReferenceToObjC(targetResult!!) // provided by [callKotlin]
MethodBridge.ReturnValue.Suspend -> {
val coroutineSuspended = callFromBridge(
codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner),
emptyList(),
Lifetime.LOCAL
)
ifThen(icmpNe(targetResult!!, coroutineSuspended)) {
// Callee haven't suspended, so it isn't going to call the completion. Call it here:
callFromBridge(
context.ir.symbols.objCExportResumeContinuation.owner.llvmFunction,
listOf(continuation!!, targetResult)
)
// Note: completion block could be called directly instead, but this implementation is
// simpler and avoids duplication.
}
null
}
}
ret(genReturnValueOnSuccess(returnType))
@@ -928,6 +985,17 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
store(kNullInt8Ptr, it)
errorOutPtr = it
}
MethodBridgeValueParameter.SuspendCompletion -> {
val continuation = param(irFunction.allParameters.size) // The last argument.
// TODO: consider placing interception into the converter to reduce code size.
val intercepted = callFromBridge(
context.ir.symbols.objCExportInterceptedContinuation.owner.llvmFunction,
listOf(continuation),
Lifetime.ARGUMENT
)
callFromBridge(continuationToCompletionConverter, listOf(intercepted))
}
}
}
@@ -985,12 +1053,25 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
MethodBridge.ReturnValue.Instance.InitResult,
MethodBridge.ReturnValue.Instance.FactoryResult ->
error("init or factory method can't have bridge for overriding: $baseMethod")
MethodBridge.ReturnValue.Suspend -> {
// Objective-C implementation of Kotlin suspend function is always responsible
// for calling the completion, so in Kotlin coroutines machinery terms it suspends,
// which is indicated by the return value:
callFromBridge(
context.ir.symbols.objCExportGetCoroutineSuspended.owner.llvmFunction,
emptyList(),
Lifetime.RETURN_VALUE
)
}
}
val baseReturnType = baseIrFunction.returnType
val actualReturnType = irFunction.returnType
val retVal = when {
baseIrFunction.isSuspend -> genKotlinBaseMethodResult(Lifetime.RETURN_VALUE, methodBridge.returnBridge)
actualReturnType.isUnit() || actualReturnType.isNothing() -> {
genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge)
null
@@ -1433,10 +1514,12 @@ private val MethodBridgeParameter.objCType: LLVMTypeRef get() = when (this) {
is MethodBridgeReceiver -> ReferenceBridge.objCType
MethodBridgeSelector -> int8TypePtr
MethodBridgeValueParameter.ErrorOutParameter -> pointerType(ReferenceBridge.objCType)
MethodBridgeValueParameter.SuspendCompletion -> int8TypePtr
}
private fun MethodBridge.ReturnValue.objCType(context: Context): LLVMTypeRef {
return when (this) {
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void -> voidType
MethodBridge.ReturnValue.HashCode -> if (context.is64BitNSInteger()) int64Type else int32Type
is MethodBridge.ReturnValue.Mapped -> this.bridge.objCType
@@ -1483,6 +1566,7 @@ private val Family.nsUIntegerEncoding: String get() = when (this) {
}
private fun MethodBridge.ReturnValue.getObjCEncoding(targetFamily: Family): String = when (this) {
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void -> "v"
MethodBridge.ReturnValue.HashCode -> targetFamily.nsUIntegerEncoding
is MethodBridge.ReturnValue.Mapped -> this.bridge.objCEncoding
@@ -1498,6 +1582,7 @@ private val MethodBridgeParameter.objCEncoding: String get() = when (this) {
is MethodBridgeReceiver -> ReferenceBridge.objCEncoding
MethodBridgeSelector -> ":"
MethodBridgeValueParameter.ErrorOutParameter -> "^${ReferenceBridge.objCEncoding}"
MethodBridgeValueParameter.SuspendCompletion -> "@"
}
private val TypeBridge.objCEncoding: String get() = when (this) {
@@ -35,6 +35,7 @@ internal object MethodBridgeSelector : MethodBridgeParameter()
internal sealed class MethodBridgeValueParameter : MethodBridgeParameter() {
data class Mapped(val bridge: TypeBridge) : MethodBridgeValueParameter()
object ErrorOutParameter : MethodBridgeValueParameter()
object SuspendCompletion : MethodBridgeValueParameter()
}
internal data class MethodBridge(
@@ -56,6 +57,8 @@ internal data class MethodBridge(
object Success : WithError()
data class ZeroForError(val successBridge: ReturnValue, val successMayBeZero: Boolean) : WithError()
}
object Suspend : ReturnValue()
}
val paramBridges: List<MethodBridgeParameter> =
@@ -86,6 +89,7 @@ internal fun MethodBridge.valueParametersAssociated(
when (it) {
is MethodBridgeValueParameter.Mapped -> it to kotlinParameters.next()
MethodBridgeValueParameter.SuspendCompletion,
is MethodBridgeValueParameter.ErrorOutParameter -> it to null
}
}.also { assert(!kotlinParameters.hasNext()) }
@@ -101,6 +105,7 @@ internal fun MethodBridge.parametersAssociated(
is MethodBridgeValueParameter.Mapped, MethodBridgeReceiver.Instance ->
it to kotlinParameters.next()
MethodBridgeValueParameter.SuspendCompletion,
MethodBridgeReceiver.Static, MethodBridgeSelector, MethodBridgeValueParameter.ErrorOutParameter ->
it to null
@@ -581,6 +581,7 @@ internal class ObjCExportTranslatorImpl(
}
}
MethodBridgeValueParameter.ErrorOutParameter -> "error"
MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
}
val uniqueName = unifyName(candidateName, usedNames)
@@ -590,6 +591,16 @@ internal class ObjCExportTranslatorImpl(
is MethodBridgeValueParameter.Mapped -> mapType(p!!.type, bridge.bridge, objCExportScope)
MethodBridgeValueParameter.ErrorOutParameter ->
ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true)
MethodBridgeValueParameter.SuspendCompletion -> {
ObjCBlockPointerType(
returnType = ObjCVoidType,
parameterTypes = listOf(
mapReferenceType(method.returnType!!, objCExportScope).makeNullable(),
ObjCNullableReferenceType(ObjCClassType("NSError"))
)
)
}
}
parameters += ObjCParameter(uniqueName, p, type)
@@ -652,6 +663,7 @@ internal class ObjCExportTranslatorImpl(
}
private fun mapReturnType(returnBridge: MethodBridge.ReturnValue, method: FunctionDescriptor, objCExportScope: ObjCExportScope): ObjCType = when (returnBridge) {
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void -> ObjCVoidType
MethodBridge.ReturnValue.HashCode -> ObjCPrimitiveType.NSUInteger
is MethodBridge.ReturnValue.Mapped -> mapType(method.returnType!!, returnBridge.bridge, objCExportScope)
@@ -160,7 +160,7 @@ internal class ObjCExportLazyImpl(
file.children.filterIsInstance<KtCallableDeclaration>().forEach {
// Supposed to be similar to ObjCExportMapper.shouldBeVisible.
if ((it is KtFunction || it is KtProperty) && it.isPublic && !it.isSuspend && !it.hasExpectModifier()) {
if ((it is KtFunction || it is KtProperty) && it.isPublic && !it.hasExpectModifier()) {
val classDescriptor = getClassIfExtension(it)
if (classDescriptor != null) {
extensions.getOrPut(classDescriptor, { mutableListOf() }) += it
@@ -438,9 +438,6 @@ private fun ModuleDescriptor.isCommonStdlib() =
private val KtModifierListOwner.isPublic: Boolean
get() = this.visibilityModifierTypeOrDefault() == KtTokens.PUBLIC_KEYWORD
private val KtCallableDeclaration.isSuspend: Boolean
get() = this.hasModifier(KtTokens.SUSPEND_KEYWORD)
internal val KtPureClassOrObject.isInterface: Boolean
get() = this is KtClass && this.isInterface()
@@ -72,7 +72,7 @@ internal fun ObjCExportMapper.getClassIfCategory(extensionReceiverType: KotlinTy
// Note: partially duplicated in ObjCExportLazyImpl.translateTopLevels.
internal fun ObjCExportMapper.shouldBeExposed(descriptor: CallableMemberDescriptor): Boolean =
descriptor.isEffectivelyPublicApi && !descriptor.isSuspend && !descriptor.isExpect &&
descriptor.isEffectivelyPublicApi && !descriptor.isExpect &&
!isHiddenByDeprecation(descriptor)
internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Boolean =
@@ -241,6 +241,8 @@ private fun ObjCExportMapper.bridgeReturnType(
): MethodBridge.ReturnValue {
val returnType = descriptor.returnType!!
return when {
descriptor.isSuspend -> MethodBridge.ReturnValue.Suspend
descriptor is ConstructorDescriptor -> if (descriptor.constructedClass.isArray) {
MethodBridge.ReturnValue.Instance.FactoryResult
} else {
@@ -315,7 +317,10 @@ private fun ObjCExportMapper.bridgeMethodImpl(descriptor: FunctionDescriptor): M
}
val returnBridge = bridgeReturnType(descriptor, convertExceptionsToErrors)
if (convertExceptionsToErrors) {
if (descriptor.isSuspend) {
valueParameters += MethodBridgeValueParameter.SuspendCompletion
} else if (convertExceptionsToErrors) {
// Add error out parameter before tail block parameters. The convention allows this.
// Placing it after would trigger https://bugs.swift.org/browse/SR-12201
// (see also https://github.com/JetBrains/kotlin-native/issues/3825).
@@ -332,6 +337,7 @@ private fun MethodBridgeValueParameter.isBlockPointer(): Boolean = when (this) {
is BlockPointerBridge -> true
}
MethodBridgeValueParameter.ErrorOutParameter -> false
MethodBridgeValueParameter.SuspendCompletion -> true
}
internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor): TypeBridge {
@@ -453,13 +453,17 @@ internal class ObjCExportNamerImpl(
else -> it!!.name.asString().toIdentifier()
}
MethodBridgeValueParameter.ErrorOutParameter -> "error"
MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
}
if (index == 0) {
append(when {
bridge is MethodBridgeValueParameter.ErrorOutParameter -> "AndReturn"
bridge is MethodBridgeValueParameter.SuspendCompletion -> "With"
method is ConstructorDescriptor -> "With"
else -> ""
})
append(name.capitalize())
@@ -502,6 +506,7 @@ internal class ObjCExportNamerImpl(
else -> it!!.name.asString().toIdentifier()
}
MethodBridgeValueParameter.ErrorOutParameter -> continue@parameters
MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
}
append(label)
@@ -163,3 +163,8 @@ internal fun ObjCType.makeNullableIfReferenceOrPointer(): ObjCType = when (this)
is ObjCNullableReferenceType, is ObjCRawType, is ObjCPrimitiveType, ObjCVoidType -> this
}
internal fun ObjCReferenceType.makeNullable(): ObjCNullableReferenceType = when (this) {
is ObjCNonNullReferenceType -> ObjCNullableReferenceType(this)
is ObjCNullableReferenceType -> this
}
@@ -37,6 +37,15 @@ func assertEquals<T: Equatable>(actual: T, expected: T,
}
}
func assertSame(actual: AnyObject?, expected: AnyObject?,
_ message: String = "Assertion failed:",
file: String = #file, line: Int = #line) throws {
if (actual !== expected) {
try throwAssertFailed(message: message + " Expected value: \(expected), but got: \(actual)",
file: file, line: line)
}
}
func assertEquals<T: Equatable>(actual: [T], expected: [T],
_ message: String = "Assertion failed: arrays not equal",
file: String = #file, line: Int = #line) throws {
@@ -0,0 +1,136 @@
package coroutines
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.native.concurrent.isFrozen
import kotlin.native.internal.ObjCErrorException
import kotlin.test.*
class CoroutineException : Throwable()
suspend fun suspendFun() = 42
@Throws(CoroutineException::class)
suspend fun suspendFun(result: Any?, doSuspend: Boolean, doThrow: Boolean): Any? {
if (doSuspend) {
suspendCoroutineUninterceptedOrReturn<Unit> {
it.resume(Unit)
COROUTINE_SUSPENDED
}
}
if (doThrow) throw CoroutineException()
return result
}
class ContinuationHolder<T> {
internal lateinit var continuation: Continuation<T>
fun resume(value: T) {
continuation.resume(value)
}
fun resumeWithException(exception: Throwable) {
continuation.resumeWithException(exception)
}
}
@Throws(CoroutineException::class)
suspend fun suspendFunAsync(result: Any?, continuationHolder: ContinuationHolder<Any?>): Any? =
suspendCoroutineUninterceptedOrReturn<Any?> {
continuationHolder.continuation = it
COROUTINE_SUSPENDED
} ?: result
@Throws(CoroutineException::class)
fun throwException(exception: Throwable) {
throw exception
}
interface SuspendFun {
@Throws(CoroutineException::class)
suspend fun suspendFun(doYield: Boolean, doThrow: Boolean): Int
}
class ResultHolder<T> {
var completed: Int = 0
var result: T? = null
var exception: Throwable? = null
internal fun complete(result: Result<T>) {
this.result = result.getOrNull()
this.exception = result.exceptionOrNull()
this.completed += 1
}
}
private class ResultHolderCompletion<T>(val resultHolder: ResultHolder<T>) : Continuation<T> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<T>) {
resultHolder.complete(result)
}
}
fun callSuspendFun(suspendFun: SuspendFun, doYield: Boolean, doThrow: Boolean, resultHolder: ResultHolder<Int>) {
suspend { suspendFun.suspendFun(doYield = doYield, doThrow = doThrow) }
.startCoroutine(ResultHolderCompletion(resultHolder))
}
@Throws
suspend fun callSuspendFun2(suspendFun: SuspendFun, doYield: Boolean, doThrow: Boolean): Int {
return suspendFun.suspendFun(doYield = doYield, doThrow = doThrow)
}
interface SuspendBridge<T> {
suspend fun int(value: T): Int
suspend fun intAsAny(value: T): Any?
suspend fun unit(value: T): Unit
suspend fun unitAsAny(value: T): Any?
@Throws(CoroutineException::class) suspend fun nothing(value: T): Nothing
@Throws(CoroutineException::class) suspend fun nothingAsInt(value: T): Int
@Throws(CoroutineException::class) suspend fun nothingAsAny(value: T): Any?
@Throws(CoroutineException::class) suspend fun nothingAsUnit(value: T): Unit
}
abstract class AbstractSuspendBridge : SuspendBridge<Int> {
override suspend fun intAsAny(value: Int): Int = TODO()
override suspend fun unitAsAny(value: Int): Unit = TODO()
override suspend fun nothingAsInt(value: Int): Nothing = TODO()
override suspend fun nothingAsAny(value: Int): Nothing = TODO()
override suspend fun nothingAsUnit(value: Int): Nothing = TODO()
}
private suspend fun callSuspendBridgeImpl(bridge: SuspendBridge<Int>) {
assertEquals(1, bridge.intAsAny(1))
assertSame(Unit, bridge.unitAsAny(2))
assertFailsWith<ObjCErrorException> { bridge.nothingAsInt(3) }
assertFailsWith<ObjCErrorException> { bridge.nothingAsAny(4) }
assertFailsWith<ObjCErrorException> { bridge.nothingAsUnit(5) }
}
private suspend fun callAbstractSuspendBridgeImpl(bridge: AbstractSuspendBridge) {
assertEquals(6, bridge.intAsAny(6))
assertSame(Unit, bridge.unitAsAny(7))
assertFailsWith<ObjCErrorException> { bridge.nothingAsInt(8) }
assertFailsWith<ObjCErrorException> { bridge.nothingAsAny(9) }
assertFailsWith<ObjCErrorException> { bridge.nothingAsUnit(10) }
}
@Throws
fun callSuspendBridge(bridge: AbstractSuspendBridge, resultHolder: ResultHolder<Unit>) {
suspend {
callSuspendBridgeImpl(bridge)
callAbstractSuspendBridgeImpl(bridge)
}.startCoroutine(ResultHolderCompletion(resultHolder))
}
@@ -0,0 +1,228 @@
import Kt
private func testCallSimple() throws {
var result: KotlinInt? = nil
var error: Error? = nil
var completionCalled = 0
CoroutinesKt.suspendFun { _result, _error in
completionCalled += 1
result = _result
error = _error
}
try assertEquals(actual: completionCalled, expected: 1)
try assertEquals(actual: result, expected: 42)
try assertNil(error)
}
private func testCallSuspendFun(doSuspend: Bool, doThrow: Bool) throws {
class C {}
let expectedResult = C()
var completionCalled = 0
var result: AnyObject? = nil
var error: Error? = nil
CoroutinesKt.suspendFun(result: expectedResult, doSuspend: doSuspend, doThrow: doThrow) { _result, _error in
completionCalled += 1
result = _result as AnyObject?
error = _error
}
try assertEquals(actual: completionCalled, expected: 1)
if doThrow {
try assertNil(result)
try assertTrue(error?.kotlinException is CoroutineException)
} else {
try assertSame(actual: result, expected: expectedResult)
try assertNil(error)
}
}
private func testSuspendFuncAsync(doThrow: Bool) throws {
var completionCalled = 0
var result: AnyObject? = nil
var error: Error? = nil
let continuationHolder = ContinuationHolder<AnyObject>()
CoroutinesKt.suspendFunAsync(result: nil, continuationHolder: continuationHolder) { _result, _error in
completionCalled += 1
result = _result as AnyObject?
error = _error
}
try assertEquals(actual: completionCalled, expected: 0)
if doThrow {
let exception = CoroutineException()
continuationHolder.resumeWithException(exception: exception)
try assertEquals(actual: completionCalled, expected: 1)
try assertNil(result)
try assertSame(actual: error?.kotlinException as AnyObject?, expected: exception)
} else {
class C {}
let expectedResult = C()
continuationHolder.resume(value: expectedResult)
try assertEquals(actual: completionCalled, expected: 1)
try assertSame(actual: result, expected: expectedResult)
try assertNil(error)
}
}
private func testCall() throws {
try testCallSuspendFun(doSuspend: true, doThrow: false)
try testCallSuspendFun(doSuspend: false, doThrow: false)
try testCallSuspendFun(doSuspend: true, doThrow: true)
try testCallSuspendFun(doSuspend: false, doThrow: true)
try testSuspendFuncAsync(doThrow: false)
try testSuspendFuncAsync(doThrow: true)
}
private class SuspendFunImpl : SuspendFun {
class E : Error {}
var completion: (() -> Void)? = nil
func suspendFun(doYield: Bool, doThrow: Bool, completionHandler: @escaping (KotlinInt?, Error?) -> Void) {
func callCompletion() {
if doThrow {
completionHandler(nil, E())
} else {
completionHandler(17, nil)
}
}
if doYield {
self.completion = callCompletion
} else {
callCompletion()
}
}
}
private func testSuspendFunImpl(doYield: Bool, doThrow: Bool) throws {
let resultHolder = ResultHolder<KotlinInt>()
let impl = SuspendFunImpl()
CoroutinesKt.callSuspendFun(
suspendFun: impl,
doYield: doYield,
doThrow: doThrow,
resultHolder: resultHolder
)
if doYield {
try assertEquals(actual: resultHolder.completed, expected: 0)
guard let completion = impl.completion else { try fail() }
completion()
}
try assertEquals(actual: resultHolder.completed, expected: 1)
if doThrow {
try assertNil(resultHolder.result)
if let e = resultHolder.exception {
try assertFailsWith(SuspendFunImpl.E.self) { try CoroutinesKt.throwException(exception: e) }
} else {
try fail()
}
} else {
try assertEquals(actual: resultHolder.result, expected: 17)
try assertNil(resultHolder.exception)
}
}
private func testSuspendFunImpl2(doYield: Bool, doThrow: Bool) throws {
let impl = SuspendFunImpl()
var completionCalled = 0
var result: KotlinInt? = nil
var error: Error? = nil
CoroutinesKt.callSuspendFun2(suspendFun: impl, doYield: doYield, doThrow: doThrow) { _result, _error in
completionCalled += 1
result = _result
error = _error
}
if doYield {
try assertEquals(actual: completionCalled, expected: 0)
guard let completion = impl.completion else { try fail() }
completion()
}
try assertEquals(actual: completionCalled, expected: 1)
if doThrow {
try assertNil(result)
try assertTrue(error is SuspendFunImpl.E)
} else {
try assertEquals(actual: result, expected: 17)
try assertNil(error)
}
}
private func testOverride() throws {
try testSuspendFunImpl(doYield: false, doThrow: false)
try testSuspendFunImpl(doYield: false, doThrow: true)
try testSuspendFunImpl(doYield: true, doThrow: false)
try testSuspendFunImpl(doYield: true, doThrow: true)
try testSuspendFunImpl2(doYield: false, doThrow: false)
try testSuspendFunImpl2(doYield: false, doThrow: true)
try testSuspendFunImpl2(doYield: true, doThrow: false)
try testSuspendFunImpl2(doYield: true, doThrow: true)
}
private class SwiftSuspendBridge : AbstractSuspendBridge {
class E : Error {}
override func intAsAny(value: KotlinInt, completionHandler: @escaping (KotlinInt?, Error?) -> Void) {
completionHandler(value, nil)
}
override func unitAsAny(value: KotlinInt, completionHandler: @escaping (KotlinUnit?, Error?) -> Void) {
completionHandler(KotlinUnit(), nil)
}
override func nothingAsInt(value: KotlinInt, completionHandler: @escaping (KotlinNothing?, Error?) -> Void) {
completionHandler(nil, E())
}
override func nothingAsAny(value: KotlinInt, completionHandler: @escaping (KotlinNothing?, Error?) -> Void) {
completionHandler(nil, E())
}
override func nothingAsUnit(value: KotlinInt, completionHandler: @escaping (KotlinNothing?, Error?) -> Void) {
completionHandler(nil, E())
}
}
private func testBridges() throws {
let resultHolder = ResultHolder<KotlinUnit>()
try CoroutinesKt.callSuspendBridge(bridge: SwiftSuspendBridge(), resultHolder: resultHolder)
try assertEquals(actual: resultHolder.completed, expected: 1)
try assertNil(resultHolder.exception)
try assertSame(actual: resultHolder.result, expected: KotlinUnit())
}
class CoroutinesTests : SimpleTestProvider {
override init() {
super.init()
test("TestCallSimple", testCallSimple)
test("TestCall", testCall)
test("TestOverride", testOverride)
test("TestBridges", testBridges)
}
}
@@ -120,6 +120,77 @@ __attribute__((swift_name("KotlinBoolean")))
+ (instancetype)numberWithBool:(BOOL)value;
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("CoroutineException")))
@interface KtCoroutineException : KtKotlinThrowable
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (instancetype)initWithMessage:(NSString * _Nullable)message __attribute__((swift_name("init(message:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
- (instancetype)initWithCause:(KtKotlinThrowable * _Nullable)cause __attribute__((swift_name("init(cause:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
- (instancetype)initWithMessage:(NSString * _Nullable)message cause:(KtKotlinThrowable * _Nullable)cause __attribute__((swift_name("init(message:cause:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("ContinuationHolder")))
@interface KtContinuationHolder<T> : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (void)resumeValue:(T _Nullable)value __attribute__((swift_name("resume(value:)")));
- (void)resumeWithExceptionException:(KtKotlinThrowable *)exception __attribute__((swift_name("resumeWithException(exception:)")));
@end;
__attribute__((swift_name("SuspendFun")))
@protocol KtSuspendFun
@required
- (void)suspendFunDoYield:(BOOL)doYield doThrow:(BOOL)doThrow completionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("suspendFun(doYield:doThrow:completionHandler:)")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("ResultHolder")))
@interface KtResultHolder<T> : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@property int32_t completed __attribute__((swift_name("completed")));
@property T _Nullable result __attribute__((swift_name("result")));
@property KtKotlinThrowable * _Nullable exception __attribute__((swift_name("exception")));
@end;
__attribute__((swift_name("SuspendBridge")))
@protocol KtSuspendBridge
@required
- (void)intValue:(id _Nullable)value completionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("int(value:completionHandler:)")));
- (void)intAsAnyValue:(id _Nullable)value completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("intAsAny(value:completionHandler:)")));
- (void)unitValue:(id _Nullable)value completionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("unit(value:completionHandler:)")));
- (void)unitAsAnyValue:(id _Nullable)value completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("unitAsAny(value:completionHandler:)")));
- (void)nothingValue:(id _Nullable)value completionHandler:(void (^)(KtKotlinNothing * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothing(value:completionHandler:)")));
- (void)nothingAsIntValue:(id _Nullable)value completionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsInt(value:completionHandler:)")));
- (void)nothingAsAnyValue:(id _Nullable)value completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsAny(value:completionHandler:)")));
- (void)nothingAsUnitValue:(id _Nullable)value completionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsUnit(value:completionHandler:)")));
@end;
__attribute__((swift_name("AbstractSuspendBridge")))
@interface KtAbstractSuspendBridge : KtBase <KtSuspendBridge>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (void)intAsAnyValue:(KtInt *)value completionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("intAsAny(value:completionHandler:)")));
- (void)unitAsAnyValue:(KtInt *)value completionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("unitAsAny(value:completionHandler:)")));
- (void)nothingAsIntValue:(KtInt *)value completionHandler:(void (^)(KtKotlinNothing * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsInt(value:completionHandler:)")));
- (void)nothingAsAnyValue:(KtInt *)value completionHandler:(void (^)(KtKotlinNothing * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsAny(value:completionHandler:)")));
- (void)nothingAsUnitValue:(KtInt *)value completionHandler:(void (^)(KtKotlinNothing * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("nothingAsUnit(value:completionHandler:)")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("CoroutinesKt")))
@interface KtCoroutinesKt : KtBase
+ (void)suspendFunWithCompletionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("suspendFun(completionHandler:)")));
+ (void)suspendFunResult:(id _Nullable)result doSuspend:(BOOL)doSuspend doThrow:(BOOL)doThrow completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("suspendFun(result:doSuspend:doThrow:completionHandler:)")));
+ (void)suspendFunAsyncResult:(id _Nullable)result continuationHolder:(KtContinuationHolder<id> *)continuationHolder completionHandler:(void (^)(id _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("suspendFunAsync(result:continuationHolder:completionHandler:)")));
+ (BOOL)throwExceptionException:(KtKotlinThrowable *)exception error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("throwException(exception:)")));
+ (void)callSuspendFunSuspendFun:(id<KtSuspendFun>)suspendFun doYield:(BOOL)doYield doThrow:(BOOL)doThrow resultHolder:(KtResultHolder<KtInt *> *)resultHolder __attribute__((swift_name("callSuspendFun(suspendFun:doYield:doThrow:resultHolder:)")));
+ (void)callSuspendFun2SuspendFun:(id<KtSuspendFun>)suspendFun doYield:(BOOL)doYield doThrow:(BOOL)doThrow completionHandler:(void (^)(KtInt * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("callSuspendFun2(suspendFun:doYield:doThrow:completionHandler:)")));
+ (BOOL)callSuspendBridgeBridge:(KtAbstractSuspendBridge *)bridge resultHolder:(KtResultHolder<KtKotlinUnit *> *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("callSuspendBridge(bridge:resultHolder:)")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("DelegateClass")))
@interface KtDelegateClass : KtBase <KtKotlinReadWriteProperty>
+1
View File
@@ -9,6 +9,7 @@
#import "Memory.h"
extern "C" id objc_retain(id self);
extern "C" id objc_retainBlock(id self);
extern "C" void objc_release(id self);
inline static id GetAssociatedObject(ObjHeader* obj) {
-2
View File
@@ -444,8 +444,6 @@ static const char* getBlockEncoding(id block) {
extern "C" convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters;
extern "C" int Kotlin_ObjCExport_blockToFunctionConverters_size;
extern "C" id objc_retainBlock(id self);
static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
const char* encoding = getBlockEncoding(block);
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#if KONAN_OBJC_INTEROP
#import <pthread.h>
#import <Foundation/NSException.h>
#import <Foundation/NSObject.h>
#import "ObjCExport.h"
#import "ObjCExportErrors.h"
typedef void (^Completion)(id _Nullable, NSError* _Nullable);
extern "C" void Kotlin_ObjCExport_runCompletionSuccess(KRef completionHolder, KRef result) {
Completion completion = (Completion)GetAssociatedObject(completionHolder);
completion(Kotlin_ObjCExport_refToObjC(result), nullptr);
}
extern "C" void Kotlin_ObjCExport_runCompletionFailure(
KRef completionHolder,
KRef exception,
const TypeInfo** exceptionTypes
) {
id error = Kotlin_ObjCExport_ExceptionAsNSError(exception, exceptionTypes);
Completion completion = (Completion)GetAssociatedObject(completionHolder);
completion(nullptr, error);
}
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgumentImpl,
KRef completionHolder, const TypeInfo** exceptionTypes);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgument, id completion, const TypeInfo** exceptionTypes) {
if (pthread_main_np() != 1) {
[NSException raise:NSGenericException
format:@"Calling Kotlin suspend functions from Swift/Objective-C is currently supported only on main thread"];
}
ObjHolder slot;
KRef completionHolder = AllocInstanceWithAssociatedObject(theForeignObjCObjectTypeInfo,
objc_retainBlock(completion), slot.slot());
RETURN_RESULT_OF(Kotlin_ObjCExport_createContinuationArgumentImpl, completionHolder, exceptionTypes);
}
extern "C" void Kotlin_ObjCExport_resumeContinuationSuccess(KRef continuation, KRef result);
extern "C" void Kotlin_ObjCExport_resumeContinuationFailure(KRef continuation, KRef exception);
extern "C" void Kotlin_ObjCExport_resumeContinuation(KRef continuation, id result, id error) {
ObjHolder holder;
if (error != nullptr) {
if (result != nullptr) {
[NSException raise:NSGenericException
format:@"Kotlin completion handler is called with both result (%@) and error (%@) specified",
result, error];
}
KRef exception = Kotlin_ObjCExport_NSErrorAsException(error, holder.slot());
Kotlin_ObjCExport_resumeContinuationFailure(continuation, exception);
} else {
KRef kotlinResult = Kotlin_ObjCExport_refFromObjC(result, holder.slot());
Kotlin_ObjCExport_resumeContinuationSuccess(continuation, kotlinResult);
}
}
#endif
+9
View File
@@ -0,0 +1,9 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#import "Types.h"
extern "C" id Kotlin_ObjCExport_ExceptionAsNSError(KRef exception, const TypeInfo** types);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error);
+20 -15
View File
@@ -26,6 +26,8 @@
#import "Runtime.h"
#import "Utils.h"
#import "ObjCExportErrors.h"
extern "C" OBJ_GETTER(Kotlin_Throwable_getMessage, KRef throwable);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_getWrappedError, KRef throwable);
@@ -52,14 +54,13 @@ static bool isExceptionOfType(KRef exception, const TypeInfo** types) {
return false;
}
extern "C" void Kotlin_ObjCExport_RethrowExceptionAsNSError(KRef exception, id* outError, const TypeInfo** types) {
extern "C" id Kotlin_ObjCExport_ExceptionAsNSError(KRef exception, const TypeInfo** types) {
ObjHolder errorHolder;
KRef error = Kotlin_ObjCExport_getWrappedError(exception, errorHolder.slot());
if (error != nullptr) {
// Thrown originally by Swift/Objective-C.
// Not actually a Kotlin exception, so don't check if it matches [types].
if (outError != nullptr) *outError = Kotlin_ObjCExport_refToObjC(error);
return;
return Kotlin_ObjCExport_refToObjC(error);
}
if (!isExceptionOfType(exception, types)) {
@@ -69,10 +70,6 @@ extern "C" void Kotlin_ObjCExport_RethrowExceptionAsNSError(KRef exception, id*
TerminateWithUnhandledException(exception);
}
if (outError == nullptr) {
return;
}
NSMutableDictionary<NSErrorUserInfoKey, id>* userInfo = [[NSMutableDictionary new] autorelease];
userInfo[@"KotlinException"] = Kotlin_ObjCExport_refToObjC(exception);
userInfo[@"KotlinExceptionOrigin"] = @(&kotlinExceptionOriginChar); // Support for different Kotlin runtimes loaded.
@@ -84,13 +81,17 @@ extern "C" void Kotlin_ObjCExport_RethrowExceptionAsNSError(KRef exception, id*
userInfo[NSLocalizedDescriptionKey] = description;
}
*outError = [NSError errorWithDomain:@"KotlinException" code:0 userInfo:userInfo];
return;
return [NSError errorWithDomain:@"KotlinException" code:0 userInfo:userInfo];
}
extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsExceptionImpl(KRef message, KRef error);
extern "C" void Kotlin_ObjCExport_RethrowExceptionAsNSError(KRef exception, id* outError, const TypeInfo** types) {
id error = Kotlin_ObjCExport_ExceptionAsNSError(exception, types); // Also traps on unexpected exception.
if (outError != nullptr) *outError = error;
}
extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsExceptionImpl, KRef message, KRef error);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) {
NSString* description;
NSError* e = (NSError*) error;
@@ -102,9 +103,7 @@ extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
if (kotlinException != nullptr &&
kotlinExceptionOrigin != nullptr && [kotlinExceptionOrigin isEqual:@(&kotlinExceptionOriginChar)]
) {
ObjHolder kotlinExceptionHolder;
ThrowException(Kotlin_ObjCExport_refFromObjC(kotlinException, kotlinExceptionHolder.slot()));
return;
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, kotlinException);
}
}
description = e.localizedDescription;
@@ -116,7 +115,13 @@ extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
KRef message = Kotlin_Interop_CreateKStringFromNSString(description, messageHolder.slot());
KRef kotlinError = Kotlin_ObjCExport_refFromObjC(error, errorHolder.slot()); // TODO: a simple opaque wrapper would be enough.
Kotlin_ObjCExport_RethrowNSErrorAsExceptionImpl(message, kotlinError);
RETURN_RESULT_OF(Kotlin_ObjCExport_NSErrorAsExceptionImpl, message, kotlinError);
}
extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
ObjHolder holder;
KRef exception = Kotlin_ObjCExport_NSErrorAsException(error, holder.slot());
ThrowException(exception);
}
@interface NSError (NSErrorKotlinException)
@@ -193,4 +193,35 @@ private inline fun <T> createCoroutineFromSuspendFunction(
else -> error("This coroutine had already completed")
}
}
}
/**
* This function creates continuation suitable for passing as implicit argument to suspend functions.
* The continuation calls [callback] and then delegates to [completion].
*
* The result is [ContinuationImpl] because that is an expectation of all coroutines machinery.
*
* It can be thought as a state machine of
* ```
* suspend fun foo() {
* val result = runCatching { <suspended here> }
* callback(result)
* }
* ```
*/
@Suppress("UNCHECKED_CAST")
internal inline fun createContinuationArgumentFromCallback(
completion: Continuation<Unit>,
crossinline callback: (Result<Any?>) -> Unit
): Continuation<Any?> = object : ContinuationImpl(completion as Continuation<Any?>) {
private var invoked = false
override fun invokeSuspend(result: Result<Any?>): Any? {
if (invoked) error("This coroutine had already completed")
invoked = true
callback(result)
return Unit // Not suspended.
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package kotlin.native.internal
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.coroutines.native.internal.*
import kotlin.native.concurrent.*
@ExportForCppRuntime
private fun Kotlin_ObjCExport_createContinuationArgumentImpl(
completionHolder: Any,
exceptionTypes: NativePtr
): Continuation<Any?> = createContinuationArgumentFromCallback(EmptyCompletion) { result ->
result.fold(
onSuccess = { value ->
runCompletionSuccess(completionHolder, value)
},
onFailure = { exception ->
runCompletionFailure(completionHolder, exception, exceptionTypes)
}
)
}
private object EmptyCompletion : Continuation<Any?> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Any?>) {
val exception = result.exceptionOrNull() ?: return
TerminateWithUnhandledException(exception)
// Throwing the exception from [resumeWith] is not generally expected.
// Also terminating is consistent with other pieces of ObjCExport machinery.
}
}
@PublishedApi
@ExportForCppRuntime("Kotlin_ObjCExport_resumeContinuationSuccess") // Also makes it a data flow root.
internal fun resumeContinuation(continuation: Continuation<Any?>, value: Any?) {
continuation.resume(value)
}
@PublishedApi
@ExportForCppRuntime("Kotlin_ObjCExport_resumeContinuationFailure") // Also makes it a data flow root.
internal fun resumeContinuationWithException(continuation: Continuation<Any?>, exception: Throwable) {
continuation.resumeWithException(exception)
}
@PublishedApi
@ExportForCompiler // Mark as data flow root.
internal fun getCoroutineSuspended(): Any = COROUTINE_SUSPENDED
@PublishedApi
@ExportForCompiler // Mark as data flow root.
internal fun interceptedContinuation(continuation: Continuation<Any?>): Continuation<Any?> = continuation.intercepted()
@FilterExceptions
@SymbolName("Kotlin_ObjCExport_runCompletionSuccess")
private external fun runCompletionSuccess(completionHolder: Any, result: Any?)
@FilterExceptions
@SymbolName("Kotlin_ObjCExport_runCompletionFailure")
private external fun runCompletionFailure(completionHolder: Any, exception: Throwable, exceptionTypes: NativePtr)
@@ -266,12 +266,10 @@ internal class NSEnumeratorAsKIterator : AbstractIterator<Any?>() {
@ExportForCppRuntime private fun Kotlin_NSSetAsKSet_create() = NSSetAsKSet()
@ExportForCppRuntime private fun Kotlin_NSDictionaryAsKMap_create() = NSDictionaryAsKMap()
@ExportForCppRuntime private fun Kotlin_ObjCExport_RethrowNSErrorAsExceptionImpl(
@ExportForCppRuntime private fun Kotlin_ObjCExport_NSErrorAsExceptionImpl(
message: String?,
error: Any
) {
throw ObjCErrorException(message, error)
}
) = ObjCErrorException(message, error)
class ObjCErrorException(
message: String?,
@@ -110,6 +110,9 @@ internal fun ReportUnhandledException(throwable: Throwable) {
throwable.printStackTrace()
}
@SymbolName("TerminateWithUnhandledException")
internal external fun TerminateWithUnhandledException(throwable: Throwable)
@ExportForCppRuntime
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
try {