Compare commits

...

7 Commits

Author SHA1 Message Date
Svyatoslav Scherbina
05ce35a255 WIP tests 2021-08-13 16:27:52 +03:00
Svyatoslav Scherbina
6fe1cfab63 WIP test 2021-08-12 20:22:55 +03:00
Svyatoslav Scherbina
3736f1919d more direct interop tests 2021-08-11 19:11:48 +03:00
Svyatoslav Scherbina
40c1b65f95 WIP fix 2021-08-11 14:41:36 +03:00
Svyatoslav Scherbina
7e95c07d06 WIP 2021-08-11 14:41:36 +03:00
Svyatoslav Scherbina
14eeabab62 WIP fix 2021-08-10 19:25:41 +03:00
Svyatoslav Scherbina
fca8c50f65 WIP 2021-08-10 18:33:45 +03:00
13 changed files with 610 additions and 10 deletions

View File

@@ -243,7 +243,7 @@ sealed class TypeInfo {
// it first gets wrapped by a holder in [argToBridged],
// and then converted to block in [cFromBridged].
override fun argToBridged(expr: KotlinExpression): KotlinExpression = "createKotlinObjectHolder($expr)"
override fun argToBridged(expr: KotlinExpression): KotlinExpression = TODO("should not reach here")
override fun cFromBridged(
expr: NativeExpression,

View File

@@ -1290,8 +1290,9 @@ private class ObjCBlockPointerValuePassing(
} else {
"(id)$block"
}
val blockAsIdVar = "blockAsId"
return "({ id $kotlinFunctionHolder = $expression; $kotlinFunctionHolder ? $blockAsId : (id)0; })"
return "({ id $kotlinFunctionHolder = $expression; id $blockAsIdVar = $kotlinFunctionHolder ? $blockAsId : (id)0; (__bridge_transfer id)(__bridge void*)$kotlinFunctionHolder; $blockAsIdVar; })"
}
override fun cToBridged(expression: String) = expression

View File

@@ -1327,6 +1327,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
}
}
var autoreleaseReturnValue = false
internal fun prologue() {
assert(returns.isEmpty())
if (isObjectType(returnType!!)) {
@@ -1408,7 +1410,15 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
}
releaseVars()
handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue)
LLVMBuildRet(builder, returnPhi)
val result = if (autoreleaseReturnValue) {
call(context.llvm.objcAutoreleaseReturnValue, listOf(returnPhi)).also {
LLVMSetTailCall(it, 1)
}
} else {
returnPhi
}
LLVMBuildRet(builder, result)
}
// Do nothing, all paths throw.
else -> LLVMBuildUnreachable(builder)

View File

@@ -519,6 +519,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val Kotlin_Interop_IsObjectKindOfClass by lazyRtFunction
val Kotlin_ObjCExport_refToObjC by lazyRtFunction
val Kotlin_ObjCExport_refToObjCRetained by lazyRtFunction
val Kotlin_ObjCExport_refFromObjC by lazyRtFunction
val Kotlin_ObjCExport_CreateNSStringFromKString by lazyRtFunction
val Kotlin_ObjCExport_convertUnit by lazyRtFunction
@@ -561,6 +562,12 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
else -> "__gxx_personality_v0"
}
val objcAutoreleaseReturnValue = externalNounwindFunction(
"objc_autoreleaseReturnValue",
functionType(int8TypePtr, false, int8TypePtr),
context.standardLlvmSymbolsOrigin
)
val cxxStdTerminate = externalNounwindFunction(
"_ZSt9terminatev", // mangled C++ 'std::terminate'
functionType(voidType, false),

View File

@@ -102,8 +102,8 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
return result
}
fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) =
callFromBridge(context.llvm.Kotlin_ObjCExport_refToObjC, listOf(value))
fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef, retained: Boolean = false) =
callFromBridge(if (retained) context.llvm.Kotlin_ObjCExport_refToObjCRetained else context.llvm.Kotlin_ObjCExport_refToObjC, listOf(value))
fun FunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) =
callFromBridge(context.llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime)
@@ -216,12 +216,13 @@ internal class ObjCExportCodeGenerator(
fun FunctionGenerationContext.kotlinToObjC(
value: LLVMValueRef,
typeBridge: TypeBridge
typeBridge: TypeBridge,
retained: Boolean = false
): LLVMValueRef = if (LLVMTypeOf(value) == voidType) {
typeBridge.makeNothing()
} else {
when (typeBridge) {
is ReferenceBridge -> kotlinReferenceToObjC(value)
is ReferenceBridge -> kotlinReferenceToObjC(value, retained = retained)
is BlockPointerBridge -> kotlinFunctionToObjCBlockPointer(typeBridge, value)
is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
}
@@ -925,11 +926,11 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
val kotlinHashCode = targetResult!!
if (codegen.context.is64BitNSInteger()) zext(kotlinHashCode, int64Type) else kotlinHashCode
}
is MethodBridge.ReturnValue.Mapped -> kotlinToObjC(targetResult!!, returnBridge.bridge)
is MethodBridge.ReturnValue.Mapped -> kotlinToObjC(targetResult!!, returnBridge.bridge, retained = true)
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.Instance.FactoryResult -> kotlinReferenceToObjC(targetResult!!, retained = true) // provided by [callKotlin]
MethodBridge.ReturnValue.Suspend -> {
val coroutineSuspended = callFromBridge(
codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner),
@@ -949,9 +950,26 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
}
}
autoreleaseReturnValue = returnType.isObjCReference()
ret(genReturnValueOnSuccess(returnType))
}
private fun MethodBridge.ReturnValue.isObjCReference(): Boolean = when (this) {
MethodBridge.ReturnValue.Instance.FactoryResult -> true
MethodBridge.ReturnValue.Instance.InitResult -> false
is MethodBridge.ReturnValue.Mapped -> when (this.bridge) {
is BlockPointerBridge -> false
ReferenceBridge -> true
is ValueTypeBridge -> false
}
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void,
MethodBridge.ReturnValue.HashCode,
MethodBridge.ReturnValue.WithError.Success -> false
is MethodBridge.ReturnValue.WithError.ZeroForError -> this.successBridge.isObjCReference()
}
private fun ObjCExportCodeGenerator.generateExceptionTypeInfoArray(baseMethod: IrFunction): LLVMValueRef =
exceptionTypeInfoArrays.getOrPut(baseMethod) {
val types = effectiveThrowsClasses(baseMethod, symbols)

View File

@@ -0,0 +1,106 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
@interface ObjCLivenessTracker : NSObject
-(void)add:(id)obj;
-(Boolean)isEmpty;
-(Boolean)objectsAreAlive;
-(Boolean)objectsAreDead;
@end;
@interface NoAutoreleaseCustomObject : NSObject
@end;
@protocol NoAutoreleaseHelper
@required
@property id kotlinObject;
-(void)sendKotlinObject:(id)kotlinObject;
-(id)receiveKotlinObject;
-(void)sendObjCObject:(NoAutoreleaseCustomObject*)objCObject;
-(NoAutoreleaseCustomObject*)receiveObjCObject;
-(void)sendArray:(NSArray*)array;
-(NSArray*)receiveArray;
-(void)sendString:(NSString*)string;
-(NSString*)receiveString;
-(void)sendBlock:(int (^)(void))block;
-(int (^)(void))receiveBlock;
@end;
id<NoAutoreleaseHelper> getNoAutoreleaseHelperImpl(ObjCLivenessTracker*);
void callSendKotlinObject(id<NoAutoreleaseHelper> helper, id obj, ObjCLivenessTracker* tracker) {
[helper sendKotlinObject:obj];
[helper sendKotlinObject:obj];
[tracker add:obj];
}
void callReceiveKotlinObject(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
[tracker add:[helper receiveKotlinObject]];
[tracker add:[helper receiveKotlinObject]];
}
void callSendObjCObject(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
NoAutoreleaseCustomObject* obj = [NoAutoreleaseCustomObject new];
[helper sendObjCObject:obj];
[helper sendObjCObject:obj];
[tracker add:obj];
}
void callReceiveObjCObject(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
[tracker add:[helper receiveObjCObject]];
[tracker add:[helper receiveObjCObject]];
}
void callSendArray(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
NSArray* array = @[[NSObject new]];
[helper sendArray:array];
[helper sendArray:array];
[tracker add:array];
}
void callReceiveArray(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
[tracker add:[helper receiveArray]];
[tracker add:[helper receiveArray]];
}
void callSendString(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
NSString* string = [[NSObject new] description]; // To make it dynamic.
[helper sendString:string];
[helper sendString:string];
[tracker add:string];
}
void callReceiveString(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
[tracker add:[helper receiveString]];
[tracker add:[helper receiveString]];
}
extern int blockResult;
int (^createBlock(void))() {
int localBlockResult = blockResult;
return ^{ return localBlockResult; }; // Try to make it capturing thus dynamic.
}
void callSendBlock(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
int (^block)(void) = createBlock(); // Try to make it heap-allocated.
[helper sendBlock:block];
[helper sendBlock:block];
[tracker add:block];
}
void callReceiveBlock(id<NoAutoreleaseHelper> helper, ObjCLivenessTracker* tracker) {
[tracker add:[helper receiveBlock]];
[tracker add:[helper receiveBlock]];
}

View File

@@ -0,0 +1,251 @@
import kotlinx.cinterop.*
import kotlin.native.internal.GC
import kotlin.native.ref.*
import kotlin.test.*
import objcTests.*
// The tests below make best efforts to ensure that objects don't "leak" to autoreleasepool
// when simply passing them between Kotlin and Objective-C.
private class KotlinLivenessTracker {
val refs = mutableListOf<WeakReference<Any>>()
fun add(obj: Any) {
refs += WeakReference(obj)
}
fun isEmpty() = refs.isEmpty()
fun objectsAreAlive() = refs.all { it.value !== null }
fun objectsAreDead() = refs.all { it.value === null }
}
private fun test(
kotlinPeerRetainsObjC: Boolean = true,
block: (kotlinLivenessTracker: KotlinLivenessTracker, objCLivenessTracker: ObjCLivenessTracker) -> Unit
) = repeat(2) {
val kotlinLivenessTracker = KotlinLivenessTracker()
val objCLivenessTracker = ObjCLivenessTracker()
GC.collect() // Make predictable
autoreleasepool { // FIXME: remove!!
block(kotlinLivenessTracker, objCLivenessTracker)
}
assertFalse(kotlinLivenessTracker.isEmpty())
assertTrue(kotlinLivenessTracker.objectsAreAlive())
assertFalse(objCLivenessTracker.isEmpty())
if (kotlinPeerRetainsObjC) {
assertTrue(objCLivenessTracker.objectsAreAlive())
} else {
assertTrue(objCLivenessTracker.objectsAreDead())
}
GC.collect()
assertTrue(kotlinLivenessTracker.objectsAreDead())
assertTrue(objCLivenessTracker.objectsAreDead())
// TODO: can we create an autoreleasepool and actually check that it is not used?
}
private fun <T> testSendToObjC(
createObject: () -> T,
kotlinPeerRetainsObjC: Boolean = true,
sendObject: NoAutoreleaseHelperProtocol.(T) -> Unit
) = test(kotlinPeerRetainsObjC) { kotlinLivenessTracker, objCLivenessTracker ->
val helper = getNoAutoreleaseHelperImpl(objCLivenessTracker)!!
val obj = createObject()!!
kotlinLivenessTracker.add(obj)
helper.sendObject(obj)
helper.sendObject(obj)
}
private fun <T> testReceiveFromObjC(
kotlinPeerRetainsObjC: Boolean = true, // FIXME: is it used?
receiveObject: NoAutoreleaseHelperProtocol.() -> T
) = test(kotlinPeerRetainsObjC) { kotlinLivenessTracker, objCLivenessTracker ->
val helper = getNoAutoreleaseHelperImpl(objCLivenessTracker)!!
val obj1 = helper.receiveObject()!!
val obj2 = helper.receiveObject()!!
kotlinLivenessTracker.add(obj1)
kotlinLivenessTracker.add(obj2)
}
private fun testCallToKotlin(
kotlinPeerRetainsObjC: Boolean = true, // FIXME: is it used?
callObjC: NoAutoreleaseHelperProtocol.(ObjCLivenessTracker) -> Unit
) = test { kotlinLivenessTracker, objCLivenessTracker ->
val helper = object : NSObject(), NoAutoreleaseHelperProtocol {
val myKotlinObject = KotlinObject4()
val objCObject = NoAutoreleaseCustomObject()
val array = listOf(Any())
val string = Any().toString()
val block = createLambda()
override fun kotlinObject(): Any? {
error("should not be used")
}
override fun setKotlinObject(value: Any?) {
error("should not be used")
}
override fun sendKotlinObject(kotlinObject: Any?) {
kotlinLivenessTracker.add(kotlinObject!!)
}
override fun receiveKotlinObject(): Any? {
val result = myKotlinObject
kotlinLivenessTracker.add(result)
return result
}
override fun sendObjCObject(objCObject: NoAutoreleaseCustomObject?) {
kotlinLivenessTracker.add(objCObject!!)
}
override fun receiveObjCObject(): NoAutoreleaseCustomObject? {
val result = objCObject
kotlinLivenessTracker.add(result)
return result
}
override fun sendArray(array: List<*>?) {
kotlinLivenessTracker.add(array!!)
}
override fun receiveArray(): List<*>? {
val result = array
kotlinLivenessTracker.add(result)
return result
}
override fun sendString(string: String?) {
kotlinLivenessTracker.add(string!!)
}
override fun receiveString(): String? {
val result = string
kotlinLivenessTracker.add(result)
return result
}
override fun sendBlock(block: (() -> Int)?) {
kotlinLivenessTracker.add(block!!)
}
override fun receiveBlock(): (() -> Int)? {
val result = block
kotlinLivenessTracker.add(result)
return result
}
}
helper.callObjC(objCLivenessTracker)
}
private class KotlinObject1
private class KotlinObject2
private class KotlinObject3
private class KotlinObject4
@Test fun testSendKotlinObjectToObjC() = testSendToObjC({ KotlinObject1() }) {
sendKotlinObject(it)
}
@Test fun testReceiveKotlinObjectFromObjC() {
val obj = KotlinObject2()
testReceiveFromObjC {
this.kotlinObject = obj
receiveKotlinObject()
}
}
@Test fun testSendObjCObjectToObjC() = testSendToObjC({ NoAutoreleaseCustomObject() }) {
sendObjCObject(it)
}
@Test fun testReceiveObjCObjectFromObjC() = testReceiveFromObjC {
receiveObjCObject()
}
@Test fun testSendArrayToObjC() = testSendToObjC({ listOf(KotlinObject1()) }, kotlinPeerRetainsObjC = false) {
sendArray(it)
}
@Test fun testReceiveArrayFromObjC() = testReceiveFromObjC {
receiveArray()
}
@Test fun testSendStringToObjC() = testSendToObjC({ Any().toString() }) {
sendString(it)
}
@Test fun testReceiveStringFromObjC() = testReceiveFromObjC {
receiveString()
}
@Test fun testSendBlockToObjC() = testSendToObjC({ createLambda() }, kotlinPeerRetainsObjC = false) {
sendBlock(it)
}
@Test fun testReceiveBlockFromObjC() = testReceiveFromObjC {
receiveBlock()
}
@Test fun testSendKotlinObjectToKotlin() = testCallToKotlin {
callSendKotlinObject(this, KotlinObject3(), it)
}
@Test fun testReceiveKotlinObjectFromKotlin() = testCallToKotlin {
callReceiveKotlinObject(this, it)
}
@Test fun testSendObjCObjectToKotlin() = testCallToKotlin {
callSendObjCObject(this, it)
}
@Test fun testReceiveObjCObjectFromKotlin() = testCallToKotlin {
callReceiveObjCObject(this, it)
}
@Test fun testSendArrayToKotlin() = testCallToKotlin {
callSendArray(this, it)
}
@Test fun testReceiveArrayFromKotlin() = testCallToKotlin {
callReceiveArray(this, it)
}
@Test fun testSendStringToKotlin() = testCallToKotlin {
callSendString(this, it)
}
@Test fun testReceiveStringFromKotlin() = testCallToKotlin {
callReceiveString(this, it)
}
@Test fun testSendBlockToKotlin() = testCallToKotlin {
callSendBlock(this, it)
}
@Test fun testReceiveBlockFromKotlin() = testCallToKotlin {
callReceiveBlock(this, it)
}
private fun createLambda(): () -> Int {
val lambdaResult = 123
return { lambdaResult } // make it capturing
}
// FIXME: remove
// Note: this executes code with a separate stack frame,
// so no stack refs will remain after it, and GC will be able to collect the garbage.
private fun <R> runNoInline(block: () -> R) = block()

View File

@@ -0,0 +1,130 @@
#import "noAutorelease.h"
@implementation NoAutoreleaseCustomObject
@end;
@interface NoAutoreleaseHelperImpl : NSObject <NoAutoreleaseHelper>
@property ObjCLivenessTracker* objCLivenessTracker;
@property id kotlinObject;
@property NoAutoreleaseCustomObject* objCObject;
@property NSArray* array;
@property NSString* string;
@property int (^block)(void);
@end;
@implementation NoAutoreleaseHelperImpl
-(instancetype)init {
if (self = [super init]) {
self.objCObject = [NoAutoreleaseCustomObject new];
self.array = @[[NSObject new]];
self.string = [[NSObject new] description];
self.block = createBlock();
}
return self;
}
-(void)sendKotlinObject:(id)kotlinObject {
[self.objCLivenessTracker add:kotlinObject];
}
-(id)receiveKotlinObject {
id result = self.kotlinObject;
[self.objCLivenessTracker add:result];
return result;
}
-(void)sendObjCObject:(NoAutoreleaseCustomObject*)objCObject {
[self.objCLivenessTracker add:objCObject];
}
-(NoAutoreleaseCustomObject*)receiveObjCObject {
NoAutoreleaseCustomObject* result = self.objCObject;
[self.objCLivenessTracker add:result];
return result;
}
-(void)sendArray:(NSArray*)array {
[self.objCLivenessTracker add:array];
}
-(NSArray*)receiveArray {
NSArray* result = self.array;
[self.objCLivenessTracker add:result];
return result;
}
-(void)sendString:(NSString*)string {
[self.objCLivenessTracker add:string];
}
-(NSString*)receiveString {
NSString* result = self.string;
[self.objCLivenessTracker add:result];
return result;
}
-(void)sendBlock:(int (^)(void))block {
[self.objCLivenessTracker add:block];
}
-(int (^)(void))receiveBlock {
int (^result)(void) = self.block;
[self.objCLivenessTracker add:result];
return result;
}
@end;
id<NoAutoreleaseHelper> getNoAutoreleaseHelperImpl(ObjCLivenessTracker* objCLivenessTracker) {
NoAutoreleaseHelperImpl* result = [NoAutoreleaseHelperImpl new];
result.objCLivenessTracker = objCLivenessTracker;
return result;
}
int blockResult = 42;
@interface ObjCWeakRef : NSObject
@property (weak) id value;
@end;
@implementation ObjCWeakRef;
@end;
@implementation ObjCLivenessTracker {
NSMutableArray<ObjCWeakRef*>* weakRefs;
}
-(instancetype)init {
if (self = [super init]) {
self->weakRefs = [NSMutableArray new];
}
return self;
}
-(void)add:(id)obj {
ObjCWeakRef* weakRef = [ObjCWeakRef new];
weakRef.value = obj;
[weakRefs addObject:weakRef];
}
-(Boolean)isEmpty {
return [weakRefs count] == 0;
}
-(Boolean)objectsAreAlive {
for (ObjCWeakRef* weakRef in weakRefs) {
if (weakRef.value == nil) return NO;
}
return YES;
}
-(Boolean)objectsAreDead {
for (ObjCWeakRef* weakRef in weakRefs) {
if (weakRef.value != nil) return NO;
}
return YES;
}
@end;

View File

@@ -923,6 +923,24 @@ __attribute__((swift_name("ArraysInitBlock")))
- (NSString *)log __attribute__((swift_name("log()")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("TestNoAutorelease")))
@interface KtTestNoAutorelease : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (KtTestNoAutoreleaseNoAutorelease * _Nullable)returnObj __attribute__((swift_name("returnObj()")));
- (void)clearObj __attribute__((swift_name("clearObj()")));
- (BOOL)isObjUnreachable __attribute__((swift_name("isObjUnreachable()")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("TestNoAutorelease.NoAutorelease")))
@interface KtTestNoAutoreleaseNoAutorelease : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@property (readonly) int32_t x __attribute__((swift_name("x")));
@end;
__attribute__((swift_name("OverrideKotlinMethods2")))
@protocol KtOverrideKotlinMethods2
@required

View File

@@ -0,0 +1,20 @@
package noAutorelease
class TestNoAutorelease {
class NoAutorelease {
val x = 11
}
private var obj: NoAutorelease? = NoAutorelease()
private val weakObj = kotlin.native.ref.WeakReference(obj!!)
fun returnObj() = obj
fun clearObj() {
obj = null
}
fun isObjUnreachable(): Boolean {
kotlin.native.internal.GC.collect()
return weakObj.value == null
}
}

View File

@@ -0,0 +1,18 @@
import Kt
private func test1() throws {
let test = TestNoAutorelease()
try assertEquals(actual: test.returnObj()?.x, expected: 11)
try assertEquals(actual: test.returnObj()?.x, expected: 11)
test.clearObj()
try assertTrue(test.isObjUnreachable())
}
class NoAutoreleaseTests : SimpleTestProvider {
override init() {
super.init()
test("Test1", test1)
test("Test1_2", test1)
}
}

View File

@@ -18,6 +18,7 @@
#import "Memory.h"
#include "Natives.h"
#include "ObjCInterop.h"
#include "ObjCMMAPI.h"
#if KONAN_OBJC_INTEROP
@@ -525,6 +526,26 @@ static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) {
return Kotlin_ObjCExport_refToObjC_slowpath(obj);
}
extern "C" id Kotlin_ObjCExport_refToObjCRetained(ObjHeader* obj) {
kotlin::AssertThreadState(kotlin::ThreadState::kRunnable);
if (obj == nullptr) return nullptr;
id associatedObject = GetAssociatedObject(obj);
if (associatedObject != nullptr) {
return objc_retain(associatedObject);
}
konan::AutoreleasePool pool;
convertReferenceToObjC converter = (convertReferenceToObjC)obj->type_info()->writableInfo_->objCExport.convert;
if (converter != nullptr) {
return objc_retain(converter(obj));
}
return objc_retain(Kotlin_ObjCExport_refToObjC_slowpath(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);

View File

@@ -46,7 +46,7 @@ static id Kotlin_Interop_createKotlinObjectHolder(KRef any) {
return nullptr;
}
return [[[KotlinObjectHolder alloc] initWithRef:any] autorelease];
return [[KotlinObjectHolder alloc] initWithRef:any];
}
static KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) {