From 8c923f650497974e303d1dd687e06ff0c3cf9445 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Thu, 26 Aug 2021 17:40:58 +0300 Subject: [PATCH] Native: optimize autorelease in ObjCExport calls to Kotlin Use objc_autoreleaseReturnValue to eliminate the autorelease operation for return value if the caller is optimized (usually it is). This required moving autorelease operation from Kotlin -> ObjC ref conversion to bridge epilogue. To achieve this, also make ObjCExport Kotlin ref -> ObjC ref dynamic converters return retained reference (instead of autoreleased one). To reflect this, rename the corresponding entities in the code. --- .../kotlin/backend/konan/llvm/ContextUtils.kt | 7 +- .../konan/llvm/objc/ObjCCodeGenerator.kt | 14 ++ .../llvm/objcexport/BlockPointerSupport.kt | 22 +-- .../objcexport/ObjCExportCodeGenerator.kt | 177 ++++++++++-------- .../runtime/src/main/cpp/ObjCExport.h | 1 + .../runtime/src/main/cpp/ObjCExport.mm | 83 ++++---- .../src/main/cpp/ObjCExportCoroutines.mm | 2 +- .../runtime/src/main/cpp/ObjCExportErrors.mm | 2 +- .../main/cpp/ObjCExportExceptionDetails.mm | 2 +- .../runtime/src/main/cpp/ObjCExportPrivate.h | 2 +- .../runtime/src/main/cpp/ObjCInteropUtils.mm | 6 +- .../runtime/src/mm/cpp/CallsChecker.cpp | 2 + .../runtime/src/objc/cpp/ObjCExportClasses.mm | 9 +- .../src/objc/cpp/ObjCExportCollections.mm | 40 ++-- 14 files changed, 209 insertions(+), 160 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 69a1794869c..7bcacc06d41 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -518,10 +518,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val Kotlin_Interop_DoesObjectConformToProtocol by lazyRtFunction val Kotlin_Interop_IsObjectKindOfClass by lazyRtFunction - val Kotlin_ObjCExport_refToObjC by lazyRtFunction + val Kotlin_ObjCExport_refToLocalObjC by lazyRtFunction + val Kotlin_ObjCExport_refToRetainedObjC by lazyRtFunction val Kotlin_ObjCExport_refFromObjC by lazyRtFunction - val Kotlin_ObjCExport_CreateNSStringFromKString by lazyRtFunction - val Kotlin_ObjCExport_convertUnit by lazyRtFunction + val Kotlin_ObjCExport_CreateRetainedNSStringFromKString by lazyRtFunction + val Kotlin_ObjCExport_convertUnitToRetained by lazyRtFunction val Kotlin_ObjCExport_GetAssociatedObject by lazyRtFunction val Kotlin_ObjCExport_AbstractMethodCalled by lazyRtFunction val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt index e8b741ecebf..0c3992ad5ed 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt @@ -35,6 +35,20 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) { context.stdlibModule.llvmSymbolOrigin ) + val objcAlloc = context.llvm.externalFunction( + "objc_alloc", + functionType(int8TypePtr, false, int8TypePtr), + context.stdlibModule.llvmSymbolOrigin + ) + + val objcAutorelease = context.llvm.externalFunction( + "llvm.objc.autorelease", + functionType(int8TypePtr, false, int8TypePtr), + context.stdlibModule.llvmSymbolOrigin + ).also { + setFunctionNoUnwind(it) + } + val objcAutoreleaseReturnValue = context.llvm.externalFunction( "llvm.objc.autoreleaseReturnValue", functionType(int8TypePtr, false, int8TypePtr), diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt index e2c57aa3f7f..5e37c33f87f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt @@ -35,7 +35,7 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter( "invokeFunction${bridge.nameSuffix}" ).generate { val args = (0 until bridge.numberOfParameters).map { index -> - kotlinReferenceToObjC(param(index + 1)) + kotlinReferenceToLocalObjC(param(index + 1)) } val thisRef = param(0) @@ -231,7 +231,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { private fun ObjCExportCodeGeneratorBase.generateInvoke( blockType: BlockType, invokeName: String, - genBody: FunctionGenerationContext.(LLVMValueRef, List) -> Unit + genBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List) -> Unit ): ConstPointer { val result = functionGenerator(blockType.blockInvokeLlvmType, invokeName) { switchToRunnable = true @@ -254,10 +254,10 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { return constPointer(result) } - fun ObjCExportCodeGeneratorBase.generateConvertFunctionToBlock( + fun ObjCExportCodeGeneratorBase.generateConvertFunctionToRetainedBlock( bridge: BlockPointerBridge ): LLVMValueRef { - return generateWrapKotlinObjectToBlock( + return generateWrapKotlinObjectToRetainedBlock( bridge.blockType, convertName = "convertFunction${bridge.nameSuffix}", invokeName = "invokeBlock${bridge.nameSuffix}" @@ -276,16 +276,16 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { if (bridge.returnsVoid) { ret(null) } else { - ret(kotlinReferenceToObjC(result)) + autoreleaseAndRet(kotlinReferenceToRetainedObjC(result)) } } } - internal fun ObjCExportCodeGeneratorBase.generateWrapKotlinObjectToBlock( + internal fun ObjCExportCodeGeneratorBase.generateWrapKotlinObjectToRetainedBlock( blockType: BlockType, convertName: String, invokeName: String, - genBlockBody: FunctionGenerationContext.(LLVMValueRef, List) -> Unit + genBlockBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List) -> Unit ): LLVMValueRef { val blockDescriptor = codegen.staticData.placeGlobal( "", @@ -328,13 +328,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) { val copiedBlock = callFromBridge(retainBlock, listOf(bitcast(int8TypePtr, blockOnStack))) - val autoreleaseReturnValue = context.llvm.externalFunction( - "objc_autoreleaseReturnValue", - functionType(int8TypePtr, false, int8TypePtr), - CurrentKlibModuleOrigin - ) - - ret(callFromBridge(autoreleaseReturnValue, listOf(copiedBlock))) + ret(copiedBlock) }.also { LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 46f3f16ecb0..dda76e29a35 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -162,8 +162,11 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo return result } - fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) = - callFromBridge(context.llvm.Kotlin_ObjCExport_refToObjC, listOf(value)) + fun FunctionGenerationContext.kotlinReferenceToLocalObjC(value: LLVMValueRef) = + callFromBridge(context.llvm.Kotlin_ObjCExport_refToLocalObjC, listOf(value)) + + fun FunctionGenerationContext.kotlinReferenceToRetainedObjC(value: LLVMValueRef) = + callFromBridge(context.llvm.Kotlin_ObjCExport_refToRetainedObjC, listOf(value)) fun FunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) = callFromBridge(context.llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime) @@ -176,12 +179,12 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo } protected val blockGenerator = BlockGenerator(this.codegen) - private val functionToBlockConverterCache = mutableMapOf() + private val functionToRetainedBlockConverterCache = mutableMapOf() - internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = - functionToBlockConverterCache.getOrPut(bridge) { + internal fun kotlinFunctionToRetainedBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = + functionToRetainedBlockConverterCache.getOrPut(bridge) { blockGenerator.run { - generateConvertFunctionToBlock(bridge) + generateConvertFunctionToRetainedBlock(bridge) } } } @@ -212,8 +215,8 @@ internal class ObjCExportCodeGenerator( val externalGlobalInitializers = mutableMapOf() - internal val continuationToCompletionConverter: LLVMValueRef by lazy { - generateContinuationToCompletionConverter(blockGenerator) + internal val continuationToRetainedCompletionConverter: LLVMValueRef by lazy { + generateContinuationToRetainedCompletionConverter(blockGenerator) } fun FunctionGenerationContext.genSendMessage( @@ -272,17 +275,22 @@ internal class ObjCExportCodeGenerator( private fun FunctionGenerationContext.kotlinFunctionToObjCBlockPointer( typeBridge: BlockPointerBridge, value: LLVMValueRef - ) = callFromBridge(kotlinFunctionToBlockConverter(typeBridge), listOf(value)) + ) = callFromBridge(objcAutorelease, listOf(kotlinFunctionToRetainedObjCBlockPointer(typeBridge, value))) - fun FunctionGenerationContext.kotlinToObjC( + internal fun FunctionGenerationContext.kotlinFunctionToRetainedObjCBlockPointer( + typeBridge: BlockPointerBridge, + value: LLVMValueRef + ) = callFromBridge(kotlinFunctionToRetainedBlockConverter(typeBridge), listOf(value)) + + fun FunctionGenerationContext.kotlinToLocalObjC( value: LLVMValueRef, typeBridge: TypeBridge ): LLVMValueRef = if (LLVMTypeOf(value) == voidType) { typeBridge.makeNothing() } else { when (typeBridge) { - is ReferenceBridge -> kotlinReferenceToObjC(value) - is BlockPointerBridge -> kotlinFunctionToObjCBlockPointer(typeBridge, value) + is ReferenceBridge -> kotlinReferenceToLocalObjC(value) + is BlockPointerBridge -> kotlinFunctionToObjCBlockPointer(typeBridge, value) // TODO: use stack-allocated block here. is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType) } } @@ -619,12 +627,12 @@ private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal( private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( irClass: IrClass, - converter: ConstPointer? = null, + convertToRetained: ConstPointer? = null, objCClass: ConstPointer? = null, typeAdapter: ConstPointer? = null ) { val writableTypeInfoValue = buildWritableTypeInfoValue( - converter = converter, + convertToRetained = convertToRetained, objCClass = objCClass, typeAdapter = typeAdapter ) @@ -649,16 +657,16 @@ private fun ObjCExportCodeGeneratorBase.setOwnWritableTypeInfo(irClass: IrClass, } private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue( - converter: ConstPointer? = null, + convertToRetained: ConstPointer? = null, objCClass: ConstPointer? = null, typeAdapter: ConstPointer? = null ): Struct { - if (converter != null) { - assert(converter.llvmType == pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr))) + if (convertToRetained != null) { + assert(convertToRetained.llvmType == pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr))) } val objCExportAddition = Struct(runtime.typeInfoObjCExportAddition, - converter?.bitcast(int8TypePtr), + convertToRetained?.bitcast(int8TypePtr), objCClass, typeAdapter ) @@ -676,23 +684,23 @@ private val ObjCExportCodeGeneratorBase.objCToKotlinFunctionType: LLVMTypeRef private fun ObjCExportCodeGenerator.emitBoxConverters() { val irBuiltIns = context.irBuiltIns - emitBoxConverter(irBuiltIns.booleanClass, ObjCValueType.BOOL, "numberWithBool:") - emitBoxConverter(irBuiltIns.byteClass, ObjCValueType.CHAR, "numberWithChar:") - emitBoxConverter(irBuiltIns.shortClass, ObjCValueType.SHORT, "numberWithShort:") - emitBoxConverter(irBuiltIns.intClass, ObjCValueType.INT, "numberWithInt:") - emitBoxConverter(irBuiltIns.longClass, ObjCValueType.LONG_LONG, "numberWithLongLong:") - emitBoxConverter(symbols.uByte!!, ObjCValueType.UNSIGNED_CHAR, "numberWithUnsignedChar:") - emitBoxConverter(symbols.uShort!!, ObjCValueType.UNSIGNED_SHORT, "numberWithUnsignedShort:") - emitBoxConverter(symbols.uInt!!, ObjCValueType.UNSIGNED_INT, "numberWithUnsignedInt:") - emitBoxConverter(symbols.uLong!!, ObjCValueType.UNSIGNED_LONG_LONG, "numberWithUnsignedLongLong:") - emitBoxConverter(irBuiltIns.floatClass, ObjCValueType.FLOAT, "numberWithFloat:") - emitBoxConverter(irBuiltIns.doubleClass, ObjCValueType.DOUBLE, "numberWithDouble:") + emitBoxConverter(irBuiltIns.booleanClass, ObjCValueType.BOOL, "initWithBool:") + emitBoxConverter(irBuiltIns.byteClass, ObjCValueType.CHAR, "initWithChar:") + emitBoxConverter(irBuiltIns.shortClass, ObjCValueType.SHORT, "initWithShort:") + emitBoxConverter(irBuiltIns.intClass, ObjCValueType.INT, "initWithInt:") + emitBoxConverter(irBuiltIns.longClass, ObjCValueType.LONG_LONG, "initWithLongLong:") + emitBoxConverter(symbols.uByte!!, ObjCValueType.UNSIGNED_CHAR, "initWithUnsignedChar:") + emitBoxConverter(symbols.uShort!!, ObjCValueType.UNSIGNED_SHORT, "initWithUnsignedShort:") + emitBoxConverter(symbols.uInt!!, ObjCValueType.UNSIGNED_INT, "initWithUnsignedInt:") + emitBoxConverter(symbols.uLong!!, ObjCValueType.UNSIGNED_LONG_LONG, "initWithUnsignedLongLong:") + emitBoxConverter(irBuiltIns.floatClass, ObjCValueType.FLOAT, "initWithFloat:") + emitBoxConverter(irBuiltIns.doubleClass, ObjCValueType.DOUBLE, "initWithDouble:") } private fun ObjCExportCodeGenerator.emitBoxConverter( boxClassSymbol: IrClassSymbol, objCValueType: ObjCValueType, - nsNumberFactorySelector: String + nsNumberInitSelector: String ) { val boxClass = boxClassSymbol.owner val name = "${boxClass.name}ToNSNumber" @@ -709,17 +717,18 @@ private fun ObjCExportCodeGenerator.emitBoxConverter( val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName) val switchToNative = false // We consider these methods fast enough. - ret(genSendMessage(int8TypePtr, nsNumberSubclass, nsNumberFactorySelector, switchToNative, value)) + val instance = callFromBridge(objcAlloc, listOf(nsNumberSubclass), toNative = switchToNative) + ret(genSendMessage(int8TypePtr, instance, nsNumberInitSelector, switchToNative, value)) } LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage) setObjCExportTypeInfo(boxClass, constPointer(converter)) } -private fun ObjCExportCodeGenerator.generateContinuationToCompletionConverter( +private fun ObjCExportCodeGenerator.generateContinuationToRetainedCompletionConverter( blockGenerator: BlockGenerator ): LLVMValueRef = with(blockGenerator) { - generateWrapKotlinObjectToBlock( + generateWrapKotlinObjectToRetainedBlock( BlockType(numberOfParameters = 2, returnsVoid = true), convertName = "convertContinuation", invokeName = "invokeCompletion" @@ -739,9 +748,9 @@ private val ObjCExportBlockCodeGenerator.mappedFunctionNClasses get() = private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() { require(context.producedLlvmModuleContainsStdlib) mappedFunctionNClasses.forEach { functionClass -> - val converter = kotlinFunctionToBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false)) + val convertToRetained = kotlinFunctionToRetainedBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false)) - val writableTypeInfoValue = buildWritableTypeInfoValue(converter = constPointer(converter)) + val writableTypeInfoValue = buildWritableTypeInfoValue(convertToRetained = constPointer(convertToRetained)) setOwnWritableTypeInfo(functionClass.irClass, writableTypeInfoValue) } } @@ -773,7 +782,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() { private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { setObjCExportTypeInfo( symbols.string.owner, - constPointer(context.llvm.Kotlin_ObjCExport_CreateNSStringFromKString) + constPointer(context.llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString) ) emitCollectionConverters() @@ -791,32 +800,32 @@ private fun ObjCExportCodeGenerator.emitCollectionConverters() { setObjCExportTypeInfo( symbols.list.owner, - importConverter("Kotlin_Interop_CreateNSArrayFromKList") + importConverter("Kotlin_Interop_CreateRetainedNSArrayFromKList") ) setObjCExportTypeInfo( symbols.mutableList.owner, - importConverter("Kotlin_Interop_CreateNSMutableArrayFromKList") + importConverter("Kotlin_Interop_CreateRetainedNSMutableArrayFromKList") ) setObjCExportTypeInfo( symbols.set.owner, - importConverter("Kotlin_Interop_CreateNSSetFromKSet") + importConverter("Kotlin_Interop_CreateRetainedNSSetFromKSet") ) setObjCExportTypeInfo( symbols.mutableSet.owner, - importConverter("Kotlin_Interop_CreateKotlinMutableSetFromKSet") + importConverter("Kotlin_Interop_CreateRetainedKotlinMutableSetFromKSet") ) setObjCExportTypeInfo( symbols.map.owner, - importConverter("Kotlin_Interop_CreateNSDictionaryFromKMap") + importConverter("Kotlin_Interop_CreateRetainedNSDictionaryFromKMap") ) setObjCExportTypeInfo( symbols.mutableMap.owner, - importConverter("Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap") + importConverter("Kotlin_Interop_CreateRetainedKotlinMutableDictionaryFromKMap") ) } @@ -829,7 +838,7 @@ private fun ObjCExportFunctionGenerationContextBuilder.setupBridgeDebugInfo() { private inline fun ObjCExportCodeGenerator.generateObjCImpBy( methodBridge: MethodBridge, debugInfo: Boolean = false, - genBody: FunctionGenerationContext.() -> Unit + genBody: ObjCExportFunctionGenerationContext.() -> Unit ): LLVMValueRef { val result = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") { if (debugInfo) { @@ -978,37 +987,50 @@ private fun ObjCExportCodeGenerator.generateObjCImp( val targetResult = callKotlin(kotlinArgs, Lifetime.ARGUMENT, exceptionHandler) - tailrec fun genReturnValueOnSuccess(returnBridge: MethodBridge.ReturnValue): LLVMValueRef? = when (returnBridge) { - MethodBridge.ReturnValue.Void -> null - MethodBridge.ReturnValue.HashCode -> { - val kotlinHashCode = targetResult!! - if (codegen.context.is64BitNSInteger()) zext(kotlinHashCode, int64Type) else kotlinHashCode - } - is MethodBridge.ReturnValue.Mapped -> kotlinToObjC(targetResult!!, returnBridge.bridge) - MethodBridge.ReturnValue.WithError.Success -> Int8(1).llvm // true - 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.STACK - ) - 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. + tailrec fun genReturnOnSuccess(returnBridge: MethodBridge.ReturnValue) { + val returnValue: LLVMValueRef? = when (returnBridge) { + MethodBridge.ReturnValue.Void -> null + MethodBridge.ReturnValue.HashCode -> { + val kotlinHashCode = targetResult!! + if (codegen.context.is64BitNSInteger()) zext(kotlinHashCode, int64Type) else kotlinHashCode + } + is MethodBridge.ReturnValue.Mapped -> if (LLVMTypeOf(targetResult!!) == voidType) { + returnBridge.bridge.makeNothing() + } else { + when (returnBridge.bridge) { + is ReferenceBridge -> return autoreleaseAndRet(kotlinReferenceToRetainedObjC(targetResult)) + is BlockPointerBridge -> return autoreleaseAndRet(kotlinFunctionToRetainedObjCBlockPointer(returnBridge.bridge, targetResult)) + is ValueTypeBridge -> kotlinToObjC(targetResult, returnBridge.bridge.objCValueType) + } + } + MethodBridge.ReturnValue.WithError.Success -> Int8(1).llvm // true + is MethodBridge.ReturnValue.WithError.ZeroForError -> return genReturnOnSuccess(returnBridge.successBridge) + MethodBridge.ReturnValue.Instance.InitResult -> param(0) + MethodBridge.ReturnValue.Instance.FactoryResult -> return autoreleaseAndRet(kotlinReferenceToRetainedObjC(targetResult!!)) // provided by [callKotlin] + MethodBridge.ReturnValue.Suspend -> { + val coroutineSuspended = callFromBridge( + codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner), + emptyList(), + Lifetime.STACK + ) + 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 } - null } + + // Note: some branches above don't reach here, because emit their own optimized return code. + ret(returnValue) } - ret(genReturnValueOnSuccess(returnType)) + genReturnOnSuccess(returnType) } private fun ObjCExportCodeGenerator.generateExceptionTypeInfoArray(baseMethod: IrFunction): LLVMValueRef = @@ -1095,10 +1117,10 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge( expectedType = parameterToBase[parameter]!!.type, resultLifetime = Lifetime.ARGUMENT ) - kotlinToObjC(kotlinValue, bridge.bridge) + kotlinToLocalObjC(kotlinValue, bridge.bridge) } - MethodBridgeReceiver.Instance -> kotlinReferenceToObjC(parameters[parameter]!!) + MethodBridgeReceiver.Instance -> kotlinReferenceToLocalObjC(parameters[parameter]!!) MethodBridgeSelector -> { val selector = baseMethod.selector // Selector is referenced thus should be defined to avoid false positive non-public API rejection: @@ -1124,7 +1146,8 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge( listOf(continuation), Lifetime.ARGUMENT ) - callFromBridge(continuationToCompletionConverter, listOf(intercepted)) + val retainedCompletion = callFromBridge(continuationToRetainedCompletionConverter, listOf(intercepted)) + callFromBridge(objcAutorelease, listOf(retainedCompletion)) // TODO: use stack-allocated block here instead. } } } @@ -1577,7 +1600,7 @@ private fun findImplementation(irClass: IrClass, method: IrSimpleFunction, conte private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter( selector: String, - block: FunctionGenerationContext.() -> Unit + block: ObjCExportFunctionGenerationContext.() -> Unit ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { val methodBridge = MethodBridge( @@ -1611,7 +1634,7 @@ private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) // Note: generateObjCToKotlinSyntheticGetter switches to Runnable, which is probably not required here and thus suboptimal. initRuntimeIfNeeded() // For instance methods it gets called when allocating. - ret(callFromBridge(context.llvm.Kotlin_ObjCExport_convertUnit, listOf(codegen.theUnitInstanceRef.llvm))) + autoreleaseAndRet(callFromBridge(context.llvm.Kotlin_ObjCExport_convertUnitToRetained, listOf(codegen.theUnitInstanceRef.llvm))) } private fun ObjCExportCodeGenerator.createObjectInstanceAdapter( @@ -1624,7 +1647,7 @@ private fun ObjCExportCodeGenerator.createObjectInstanceAdapter( return generateObjCToKotlinSyntheticGetter(selector) { initRuntimeIfNeeded() // For instance methods it gets called when allocating. val value = getObjectValue(irClass, startLocationInfo = null, exceptionHandler = ExceptionHandler.Caller) - ret(kotlinToObjC(value, ReferenceBridge)) + autoreleaseAndRet(kotlinReferenceToRetainedObjC(value)) } } @@ -1636,7 +1659,7 @@ private fun ObjCExportCodeGenerator.createEnumEntryAdapter( initRuntimeIfNeeded() // For instance methods it gets called when allocating. val value = getEnumEntry(irEnumEntry, ExceptionHandler.Caller) - ret(kotlinToObjC(value, ReferenceBridge)) + autoreleaseAndRet(kotlinReferenceToRetainedObjC(value)) } } diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExport.h b/kotlin-native/runtime/src/main/cpp/ObjCExport.h index e2b637ba48e..8fa2744ff6f 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExport.h +++ b/kotlin-native/runtime/src/main/cpp/ObjCExport.h @@ -34,6 +34,7 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type } extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj); +extern "C" id Kotlin_ObjCExport_refToLocalObjC(ObjHeader* obj); extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj); extern "C" id Kotlin_Interop_CreateNSStringFromKString(KRef str); diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm index 759251f9bb5..8cc6c492168 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm @@ -83,11 +83,11 @@ struct ObjCTypeAdapter { int reverseAdapterNum; }; -typedef id (*convertReferenceToObjC)(ObjHeader* obj); +typedef id (*convertReferenceToRetainedObjC)(ObjHeader* obj); typedef OBJ_GETTER((*convertReferenceFromObjC), id obj); struct TypeInfoObjCExportAddition { - /*convertReferenceToObjC*/ void* convert; + /*convertReferenceToRetainedObjC*/ void* convertToRetained; Class objCClass; const ObjCTypeAdapter* typeAdapter; }; @@ -138,8 +138,6 @@ RUNTIME_NOTHROW extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssocia static Class getOrCreateClass(const TypeInfo* typeInfo); -extern "C" id objc_retainAutoreleaseReturnValue(id self); - namespace { ALWAYS_INLINE void send_releaseAsAssociatedObject(void* associatedObject, ReleaseMode mode) { @@ -171,25 +169,25 @@ extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_detachAssociatedObject(void* ass } } -extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) { +extern "C" id Kotlin_ObjCExport_convertUnitToRetained(ObjHeader* unitInstance) { static dispatch_once_t onceToken; static id instance = nullptr; dispatch_once(&onceToken, ^{ Class unitClass = getOrCreateClass(unitInstance->type_info()); - instance = [[unitClass createWrapper:unitInstance] retain]; + instance = [unitClass createRetainedWrapper:unitInstance]; }); - return instance; + return objc_retain(instance); } -extern "C" id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str) { +extern "C" id Kotlin_ObjCExport_CreateRetainedNSStringFromKString(ObjHeader* str) { KChar* utf16Chars = CharArrayAddressOfElementAt(str->array(), 0); auto numBytes = str->array()->count_ * sizeof(KChar); if (str->permanent()) { - return [[[NSString alloc] initWithBytesNoCopy:utf16Chars + return [[NSString alloc] initWithBytesNoCopy:utf16Chars length:numBytes encoding:NSUTF16LittleEndianStringEncoding - freeWhenDone:NO] autorelease]; + freeWhenDone:NO]; } else { // TODO: consider making NSString subclass to avoid copying here. NSString* candidate = [[NSString alloc] initWithBytes:utf16Chars @@ -202,11 +200,11 @@ extern "C" id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str) { id old = AtomicCompareAndSwapAssociatedObject(str, nullptr, candidate); if (old != nullptr) { objc_release(candidate); - return objc_retainAutoreleaseReturnValue(old); + return objc_retain(old); } } - return objc_retainAutoreleaseReturnValue(candidate); + return objc_retain(candidate); } } static const ObjCTypeAdapter* findAdapterByName( @@ -519,9 +517,16 @@ static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) { } } -static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj); +static id Kotlin_ObjCExport_refToRetainedObjC_slowpath(ObjHeader* obj); -template +extern "C" id objc_autorelease(id self); + +// retain = true means that it returns retained result, which must be eventually released by the caller. +// +// retain = false means that it returns unretained result, which is not guaranteed to outlive [obj], +// but doesn't require any balancing release operation. +// It might use autorelease though, which will be suboptimal. +template static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) { kotlin::AssertThreadState(kotlin::ThreadState::kRunnable); @@ -529,22 +534,34 @@ static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) { id associatedObject = GetAssociatedObject(obj); if (associatedObject != nullptr) { - return retainAutorelease ? objc_retainAutoreleaseReturnValue(associatedObject) : associatedObject; + return retain ? objc_retain(associatedObject) : associatedObject; } // TODO: propagate [retainAutorelease] to the code below. - convertReferenceToObjC converter = (convertReferenceToObjC)obj->type_info()->writableInfo_->objCExport.convert; - if (converter != nullptr) { - return converter(obj); + convertReferenceToRetainedObjC convertToRetained = (convertReferenceToRetainedObjC)obj->type_info()->writableInfo_->objCExport.convertToRetained; + + id retainedResult; + if (convertToRetained != nullptr) { + retainedResult = convertToRetained(obj); + } else { + retainedResult = Kotlin_ObjCExport_refToRetainedObjC_slowpath(obj); } - return Kotlin_ObjCExport_refToObjC_slowpath(obj); + // Balance retain with objc_autorelease if required: + return retain ? retainedResult : objc_autorelease(retainedResult); +} + +extern "C" id Kotlin_ObjCExport_refToRetainedObjC(ObjHeader* obj) { + return Kotlin_ObjCExport_refToObjCImpl(obj); } extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) { - // TODO: in some cases (e.g. when converting a bridge argument) performing retain-autorelease is not necessary. - return Kotlin_ObjCExport_refToObjCImpl(obj); + return objc_autorelease(Kotlin_ObjCExport_refToObjCImpl(obj)); +} + +extern "C" id Kotlin_ObjCExport_refToLocalObjC(ObjHeader* obj) { + return Kotlin_ObjCExport_refToObjCImpl(obj); } extern "C" ALWAYS_INLINE id Kotlin_Interop_refToObjC(ObjHeader* obj) { @@ -570,13 +587,13 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) { RETURN_RESULT_OF(msgSend, obj, Kotlin_ObjCExport_toKotlinSelector); } -static id convertKotlinObject(ObjHeader* obj) { +static id convertKotlinObjectToRetained(ObjHeader* obj) { Class clazz = obj->type_info()->writableInfo_->objCExport.objCClass; RuntimeAssert(clazz != nullptr, ""); - return [clazz createWrapper:obj]; + return [clazz createRetainedWrapper:obj]; } -static convertReferenceToObjC findConverterFromInterfaces(const TypeInfo* typeInfo) { +static convertReferenceToRetainedObjC findConvertToRetainedFromInterfaces(const TypeInfo* typeInfo) { const TypeInfo* foundTypeInfo = nullptr; for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) { @@ -601,7 +618,7 @@ static convertReferenceToObjC findConverterFromInterfaces(const TypeInfo* typeIn return nullptr; } - if (interfaceTypeInfo->writableInfo_->objCExport.convert != nullptr) { + if (interfaceTypeInfo->writableInfo_->objCExport.convertToRetained != nullptr) { if (foundTypeInfo == nullptr || IsSubInterface(interfaceTypeInfo, foundTypeInfo)) { foundTypeInfo = interfaceTypeInfo; } else if (!IsSubInterface(foundTypeInfo, interfaceTypeInfo)) { @@ -615,23 +632,21 @@ static convertReferenceToObjC findConverterFromInterfaces(const TypeInfo* typeIn return foundTypeInfo == nullptr ? nullptr : - (convertReferenceToObjC)foundTypeInfo->writableInfo_->objCExport.convert; + (convertReferenceToRetainedObjC)foundTypeInfo->writableInfo_->objCExport.convertToRetained; } -static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) { +static id Kotlin_ObjCExport_refToRetainedObjC_slowpath(ObjHeader* obj) { const TypeInfo* typeInfo = obj->type_info(); - convertReferenceToObjC converter = nullptr; + convertReferenceToRetainedObjC convertToRetained = findConvertToRetainedFromInterfaces(typeInfo); - converter = findConverterFromInterfaces(typeInfo); - - if (converter == nullptr) { + if (convertToRetained == nullptr) { getOrCreateClass(typeInfo); - converter = (typeInfo == theUnitTypeInfo) ? &Kotlin_ObjCExport_convertUnit : &convertKotlinObject; + convertToRetained = (typeInfo == theUnitTypeInfo) ? &Kotlin_ObjCExport_convertUnitToRetained : &convertKotlinObjectToRetained; } - typeInfo->writableInfo_->objCExport.convert = (void*)converter; + typeInfo->writableInfo_->objCExport.convertToRetained = (void*)convertToRetained; - return converter(obj); + return convertToRetained(obj); } static void buildITable(TypeInfo* result, const KStdOrderedMap>& interfaceVTables) { diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportCoroutines.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportCoroutines.mm index f7022ab87eb..66bfdc12683 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportCoroutines.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportCoroutines.mm @@ -17,7 +17,7 @@ typedef void (^Completion)(id _Nullable, NSError* _Nullable); extern "C" void Kotlin_ObjCExport_runCompletionSuccess(KRef completionHolder, KRef result) { Completion completion = (Completion)GetAssociatedObject(completionHolder); - id objCResult = Kotlin_ObjCExport_refToObjC(result); + id objCResult = Kotlin_ObjCExport_refToLocalObjC(result); kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); completion(objCResult, nullptr); } diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm index 22308f610de..ba97fc9320d 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm @@ -82,7 +82,7 @@ extern "C" id Kotlin_ObjCExport_WrapExceptionToNSError(KRef exception) { KRef message = Kotlin_Throwable_getMessage(exception, messageHolder.slot()); NSString* description = Kotlin_Interop_CreateNSStringFromKString(message); - id exceptionObjCRef = Kotlin_ObjCExport_refToObjC(exception); + id exceptionObjCRef = Kotlin_ObjCExport_refToLocalObjC(exception); kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportExceptionDetails.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportExceptionDetails.mm index dcb5795dea7..d1a0e55a303 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportExceptionDetails.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportExceptionDetails.mm @@ -24,7 +24,7 @@ //! TODO: Use not_null signature. OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef /*thiz*/, KRef exceptionHolder) { - if (NSException* exception = (NSException*)Kotlin_ObjCExport_refToObjC(exceptionHolder)) { + if (NSException* exception = (NSException*)Kotlin_ObjCExport_refToLocalObjC(exceptionHolder)) { RuntimeAssert([exception isKindOfClass:[NSException class]], "Illegal type: NSException expected"); NSString* ret = [NSString stringWithFormat: @"%@:: %@", exception.name, exception.reason]; RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, ret); diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportPrivate.h b/kotlin-native/runtime/src/main/cpp/ObjCExportPrivate.h index 2a3d1b4deb4..048677082ea 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportPrivate.h +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportPrivate.h @@ -15,7 +15,7 @@ #import "ObjCExport.h" @interface KotlinBase : NSObject -+(instancetype)createWrapper:(ObjHeader*)obj; ++(instancetype)createRetainedWrapper:(ObjHeader*)obj; @end; enum class ReleaseMode { diff --git a/kotlin-native/runtime/src/main/cpp/ObjCInteropUtils.mm b/kotlin-native/runtime/src/main/cpp/ObjCInteropUtils.mm index a5c73c7b743..fa5416641e4 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCInteropUtils.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCInteropUtils.mm @@ -47,7 +47,9 @@ namespace { extern "C" { -id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str); +id Kotlin_ObjCExport_CreateRetainedNSStringFromKString(ObjHeader* str); + +extern "C" id objc_autorelease(id self); id Kotlin_Interop_CreateNSStringFromKString(ObjHeader* str) { // Note: this function is just a bit specialized [Kotlin_Interop_refToObjC]. @@ -59,7 +61,7 @@ id Kotlin_Interop_CreateNSStringFromKString(ObjHeader* str) { return (id)associatedObject; } - return Kotlin_ObjCExport_CreateNSStringFromKString(str); + return objc_autorelease(Kotlin_ObjCExport_CreateRetainedNSStringFromKString(str)); } OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str) { diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index 597779c73be..b72c3d20d55 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -168,6 +168,7 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "-[NSObject retain]", "-[NSPlaceholderString initWithBytes:length:encoding:]", "-[NSPlaceholderString initWithBytesNoCopy:length:encoding:freeWhenDone:]", + "-[NSValue init]", "-[NSValue pointerValue]", "-[__NSCFBoolean boolValue]", "-[__NSCFNumber doubleValue]", @@ -237,6 +238,7 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "llvm.memcpy.*", "llvm.memmove.*", "llvm.memset.*", + "llvm.objc.autorelease", "llvm.objc.autoreleaseReturnValue", "llvm.objc.retain", "llvm.objectsize.*", diff --git a/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm b/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm index ac01969fe12..b6ad15fe4ad 100644 --- a/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm +++ b/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm @@ -25,9 +25,6 @@ #import "Mutex.hpp" #import "Exceptions.h" -extern "C" id objc_retainAutoreleaseReturnValue(id self); -extern "C" id objc_autoreleaseReturnValue(id self); - @interface NSObject (NSObjectPrivateMethods) // Implemented for NSObject in libobjc/NSObject.mm -(BOOL)_tryRetain; @@ -85,7 +82,7 @@ static void injectToRuntime(); return result; } -+(instancetype)createWrapper:(ObjHeader*)obj { ++(instancetype)createRetainedWrapper:(ObjHeader*)obj { kotlin::AssertThreadState(kotlin::ThreadState::kRunnable); KotlinBase* candidate = [super allocWithZone:nil]; @@ -104,12 +101,12 @@ static void injectToRuntime(); candidate->refHolder.releaseRef(); [candidate releaseAsAssociatedObject:ReleaseMode::kDetachAndRelease]; } - return objc_retainAutoreleaseReturnValue(old); + return objc_retain(old); } } } - return objc_autoreleaseReturnValue(candidate); + return candidate; } -(instancetype)retain { diff --git a/kotlin-native/runtime/src/objc/cpp/ObjCExportCollections.mm b/kotlin-native/runtime/src/objc/cpp/ObjCExportCollections.mm index 2345f8b0fc9..6051e333857 100644 --- a/kotlin-native/runtime/src/objc/cpp/ObjCExportCollections.mm +++ b/kotlin-native/runtime/src/objc/cpp/ObjCExportCollections.mm @@ -190,8 +190,8 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) { [super dealloc]; } -+(id)createWithKList:(KRef)list { - KListAsNSArray* result = [[[KListAsNSArray alloc] init] autorelease]; ++(id)createRetainedWithKList:(KRef)list { + KListAsNSArray* result = [[KListAsNSArray alloc] init]; result->listHolder.init(list); return result; } @@ -226,8 +226,8 @@ static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) { [super dealloc]; } -+(id)createWithKList:(KRef)list { - KMutableListAsNSMutableArray* result = [[[KMutableListAsNSMutableArray alloc] init] autorelease]; ++(id)createRetainedWithKList:(KRef)list { + KMutableListAsNSMutableArray* result = [[KMutableListAsNSMutableArray alloc] init]; result->listHolder.init(list); return result; } @@ -306,8 +306,8 @@ static inline id KSet_getElement(KRef set, id object) { [super dealloc]; } -+(id)createWithKSet:(KRef)set { - KSetAsNSSet* result = [[[KSetAsNSSet alloc] init] autorelease]; ++(id)createRetainedWithKSet:(KRef)set { + KSetAsNSSet* result = [[KSetAsNSSet alloc] init]; result->setHolder.init(set); return result; } @@ -468,8 +468,8 @@ static inline id KMap_get(KRef map, id aKey) { [super dealloc]; } -+(id)createWithKMap:(KRef)map { - KMapAsNSDictionary* result = [[[KMapAsNSDictionary alloc] init] autorelease]; ++(id)createRetainedWithKMap:(KRef)map { + KMapAsNSDictionary* result = [[KMapAsNSDictionary alloc] init]; result->mapHolder.init(map); return result; } @@ -608,28 +608,28 @@ static inline id KMap_get(KRef map, id aKey) { // Referenced from the generated code: -extern "C" id Kotlin_Interop_CreateNSArrayFromKList(KRef obj) { - return [KListAsNSArray createWithKList:obj]; +extern "C" id Kotlin_Interop_CreateRetainedNSArrayFromKList(KRef obj) { + return [KListAsNSArray createRetainedWithKList:obj]; } -extern "C" id Kotlin_Interop_CreateNSMutableArrayFromKList(KRef obj) { - return [KMutableListAsNSMutableArray createWithKList:obj]; +extern "C" id Kotlin_Interop_CreateRetainedNSMutableArrayFromKList(KRef obj) { + return [KMutableListAsNSMutableArray createRetainedWithKList:obj]; } -extern "C" id Kotlin_Interop_CreateNSSetFromKSet(KRef obj) { - return [KSetAsNSSet createWithKSet:obj]; +extern "C" id Kotlin_Interop_CreateRetainedNSSetFromKSet(KRef obj) { + return [KSetAsNSSet createRetainedWithKSet:obj]; } -extern "C" id Kotlin_Interop_CreateKotlinMutableSetFromKSet(KRef obj) { - return [[[KotlinMutableSet alloc] initWithKSet:obj] autorelease]; +extern "C" id Kotlin_Interop_CreateRetainedKotlinMutableSetFromKSet(KRef obj) { + return [[KotlinMutableSet alloc] initWithKSet:obj]; } -extern "C" id Kotlin_Interop_CreateNSDictionaryFromKMap(KRef obj) { - return [KMapAsNSDictionary createWithKMap:obj]; +extern "C" id Kotlin_Interop_CreateRetainedNSDictionaryFromKMap(KRef obj) { + return [KMapAsNSDictionary createRetainedWithKMap:obj]; } -extern "C" id Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap(KRef obj) { - return [[[KotlinMutableDictionary alloc] initWithKMap:obj] autorelease]; +extern "C" id Kotlin_Interop_CreateRetainedKotlinMutableDictionaryFromKMap(KRef obj) { + return [[KotlinMutableDictionary alloc] initWithKMap:obj]; } #endif // KONAN_OBJC_INTEROP