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.
This commit is contained in:
committed by
Space
parent
75a3070067
commit
8c923f6504
+4
-3
@@ -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
|
||||
|
||||
+14
@@ -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),
|
||||
|
||||
+8
-14
@@ -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<LLVMValueRef>) -> Unit
|
||||
genBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> 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<LLVMValueRef>) -> Unit
|
||||
genBlockBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> 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)
|
||||
}
|
||||
|
||||
+100
-77
@@ -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<BlockPointerBridge, LLVMValueRef>()
|
||||
private val functionToRetainedBlockConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>()
|
||||
|
||||
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<LLVMValueRef, ConstValue>()
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <bool retainAutorelease>
|
||||
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 <bool retain>
|
||||
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<true>(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<true>(obj);
|
||||
return objc_autorelease(Kotlin_ObjCExport_refToObjCImpl<true>(obj));
|
||||
}
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToLocalObjC(ObjHeader* obj) {
|
||||
return Kotlin_ObjCExport_refToObjCImpl<false>(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<ClassId, KStdVector<VTableElement>>& interfaceVTables) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#import "ObjCExport.h"
|
||||
|
||||
@interface KotlinBase : NSObject <NSCopying>
|
||||
+(instancetype)createWrapper:(ObjHeader*)obj;
|
||||
+(instancetype)createRetainedWrapper:(ObjHeader*)obj;
|
||||
@end;
|
||||
|
||||
enum class ReleaseMode {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.*",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user