ObjCExport: don't use autorelease when sending arguments to Swift/Obj-C

This commit is contained in:
Svyatoslav Scherbina
2022-01-12 18:12:45 +03:00
committed by Space
parent 9326978327
commit 085e2448cd
8 changed files with 170 additions and 40 deletions
@@ -48,14 +48,6 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
origin = context.stdlibModule.llvmSymbolOrigin
))
val objcAutorelease = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autorelease",
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.stdlibModule.llvmSymbolOrigin
))
val objcAutoreleaseReturnValue = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autoreleaseReturnValue",
LlvmRetType(int8TypePtr),
@@ -34,10 +34,6 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
LlvmFunctionSignature(invokeMethod, codegen),
"invokeFunction${bridge.nameSuffix}"
).generate {
val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToLocalObjC(param(index + 1))
}
val thisRef = param(0)
val associatedObjectHolder = if (useSeparateHolder) {
val bodyPtr = bitcast(pointerType(bodyType), thisRef)
@@ -51,10 +47,20 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
)
val invoke = loadBlockInvoke(blockPtr, bridge)
val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToRetainedObjC(param(index + 1))
}
switchThreadStateIfExperimentalMM(ThreadState.Native)
// Using terminatingExceptionHandler, so any exception thrown by `invoke` will lead to the termination,
// and switching the thread state back to `Runnable` on exceptional path is not required.
val result = call(invoke, listOf(blockPtr) + args, exceptionHandler = terminatingExceptionHandler)
args.forEach {
objcReleaseFromNativeThreadState(it)
}
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
val kotlinResult = if (bridge.returnsVoid) {
@@ -87,9 +87,13 @@ internal class ObjCExportFunctionGenerationContext(
fun objcReleaseFromRunnableThreadState(objCReference: LLVMValueRef) {
switchThreadStateIfExperimentalMM(ThreadState.Native)
objcReleaseFromNativeThreadState(objCReference)
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
}
fun objcReleaseFromNativeThreadState(objCReference: LLVMValueRef) {
// It is nounwind, so no exception handler is required.
call(objCExportCodegen.objcRelease, listOf(objCReference), exceptionHandler = ExceptionHandler.None)
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
}
override fun processReturns() {
@@ -300,29 +304,11 @@ internal class ObjCExportCodeGenerator(
resultLifetime
)
private fun ObjCExportFunctionGenerationContext.kotlinFunctionToObjCBlockPointer(
typeBridge: BlockPointerBridge,
value: LLVMValueRef
) = callFromBridge(objcAutorelease, listOf(kotlinFunctionToRetainedObjCBlockPointer(typeBridge, value)))
internal fun ObjCExportFunctionGenerationContext.kotlinFunctionToRetainedObjCBlockPointer(
typeBridge: BlockPointerBridge,
value: LLVMValueRef
) = callFromBridge(kotlinFunctionToRetainedBlockConverter(typeBridge), listOf(value))
fun ObjCExportFunctionGenerationContext.kotlinToLocalObjC(
value: LLVMValueRef,
typeBridge: TypeBridge
): LLVMValueRef = if (LLVMTypeOf(value) == voidType) {
typeBridge.makeNothing()
} else {
when (typeBridge) {
is ReferenceBridge -> kotlinReferenceToLocalObjC(value)
is BlockPointerBridge -> kotlinFunctionToObjCBlockPointer(typeBridge, value) // TODO: use stack-allocated block here.
is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
}
}
fun ObjCExportFunctionGenerationContext.objCToKotlin(
value: LLVMValueRef,
typeBridge: TypeBridge,
@@ -1184,6 +1170,8 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
parameterDescriptor to param(index)
}.toMap()
val objCReferenceArgsToRelease = mutableListOf<LLVMValueRef>()
val objCArgs = methodBridge.parametersAssociated(irFunction).map { (bridge, parameter) ->
when (bridge) {
is MethodBridgeValueParameter.Mapped -> {
@@ -1194,10 +1182,26 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
expectedType = parameterToBase[parameter]!!.type,
resultLifetime = Lifetime.ARGUMENT
)
kotlinToLocalObjC(kotlinValue, bridge.bridge)
if (LLVMTypeOf(kotlinValue) == voidType) {
bridge.bridge.makeNothing()
} else {
when (bridge.bridge) {
is ReferenceBridge -> kotlinReferenceToRetainedObjC(kotlinValue).also { objCReferenceArgsToRelease += it }
is BlockPointerBridge -> kotlinFunctionToRetainedObjCBlockPointer(bridge.bridge, kotlinValue) // TODO: use stack-allocated block here.
.also { objCReferenceArgsToRelease += it }
is ValueTypeBridge -> kotlinToObjC(kotlinValue, bridge.bridge.objCValueType)
}
}
}
MethodBridgeReceiver.Instance -> kotlinReferenceToLocalObjC(parameters[parameter]!!)
MethodBridgeReceiver.Instance -> {
// `kotlinReferenceToLocalObjC` can add the result to autoreleasepool in some cases. But not here.
// Because this `parameter` is the receiver of a bridge in an Obj-C/Swift class extending
// Kotlin class or interface.
// So `kotlinReferenceToLocalObjC` here just unwraps the Obj-C reference
// without using autoreleasepool or any other reference counting operations.
kotlinReferenceToLocalObjC(parameters[parameter]!!)
}
MethodBridgeSelector -> {
val selector = baseMethod.selector
// Selector is referenced thus should be defined to avoid false positive non-public API rejection:
@@ -1224,13 +1228,14 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
Lifetime.ARGUMENT
)
// TODO: use stack-allocated block here instead.
val converter = if (bridge.useUnitCompletion) {
unitContinuationToRetainedCompletionConverter
} else {
continuationToRetainedCompletionConverter
}
val retainedCompletion = callFromBridge(converter, listOf(intercepted))
callFromBridge(objcAutorelease, listOf(retainedCompletion)) // TODO: use stack-allocated block here instead.
callFromBridge(converter, listOf(intercepted))
.also { objCReferenceArgsToRelease += it }
}
}
}
@@ -1239,6 +1244,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
// Using terminatingExceptionHandler, so any exception thrown by the method will lead to the termination,
// and switching the thread state back to `Runnable` on exceptional path is not required.
val targetResult = call(objcMsgSend, objCArgs, exceptionHandler = terminatingExceptionHandler)
objCReferenceArgsToRelease.forEach {
objcReleaseFromNativeThreadState(it)
}
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
assert(baseMethod.symbol !is IrConstructorSymbol)
@@ -1020,11 +1020,18 @@ __attribute__((swift_name("NoAutoreleaseSendHelper")))
@protocol KtNoAutoreleaseSendHelper
@required
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@end;
__attribute__((swift_name("NoAutoreleaseReceiveHelper")))
@@ -1043,11 +1050,18 @@ __attribute__((swift_name("NoAutoreleaseKotlinSendHelper")))
@interface KtNoAutoreleaseKotlinSendHelper : KtBase <KtNoAutoreleaseSendHelper>
- (instancetype)initWithKotlinLivenessTracker:(KtKotlinLivenessTracker *)kotlinLivenessTracker __attribute__((swift_name("init(kotlinLivenessTracker:)"))) __attribute__((objc_designated_initializer));
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@property (readonly) KtKotlinLivenessTracker *kotlinLivenessTracker __attribute__((swift_name("kotlinLivenessTracker")));
@end;
@@ -1091,11 +1105,13 @@ __attribute__((swift_name("NoAutoreleaseKt")))
@interface KtNoAutoreleaseKt : KtBase
+ (void)gc __attribute__((swift_name("gc()")));
+ (void)callSendKotlinObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendKotlinObject(helper:tracker:)")));
+ (void)sendKotlinObjectToBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("sendKotlinObjectToBlock(helper:tracker:)")));
+ (void)callSendSwiftObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker swiftObject:(id)swiftObject __attribute__((swift_name("callSendSwiftObject(helper:tracker:swiftObject:)")));
+ (void)callSendListHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendList(helper:tracker:)")));
+ (void)callSendStringHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendString(helper:tracker:)")));
+ (void)callSendNumberHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendNumber(helper:tracker:)")));
+ (void)callSendBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendBlock(helper:tracker:)")));
+ (void)callSendCompletionHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendCompletion(helper:tracker:)")));
+ (void)callReceiveKotlinObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveKotlinObject(helper:tracker:)")));
+ (void)callReceiveSwiftObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveSwiftObject(helper:tracker:)")));
+ (void)callReceiveListHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveList(helper:tracker:)")));
@@ -962,11 +962,18 @@ __attribute__((swift_name("NoAutoreleaseSendHelper")))
@protocol KtNoAutoreleaseSendHelper
@required
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@end;
__attribute__((swift_name("NoAutoreleaseReceiveHelper")))
@@ -985,11 +992,18 @@ __attribute__((swift_name("NoAutoreleaseKotlinSendHelper")))
@interface KtNoAutoreleaseKotlinSendHelper : KtBase <KtNoAutoreleaseSendHelper>
- (instancetype)initWithKotlinLivenessTracker:(KtKotlinLivenessTracker *)kotlinLivenessTracker __attribute__((swift_name("init(kotlinLivenessTracker:)"))) __attribute__((objc_designated_initializer));
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@property (readonly) KtKotlinLivenessTracker *kotlinLivenessTracker __attribute__((swift_name("kotlinLivenessTracker")));
@end;
@@ -1033,11 +1047,13 @@ __attribute__((swift_name("NoAutoreleaseKt")))
@interface KtNoAutoreleaseKt : KtBase
+ (void)gc __attribute__((swift_name("gc()")));
+ (void)callSendKotlinObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendKotlinObject(helper:tracker:)")));
+ (void)sendKotlinObjectToBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("sendKotlinObjectToBlock(helper:tracker:)")));
+ (void)callSendSwiftObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker swiftObject:(id)swiftObject __attribute__((swift_name("callSendSwiftObject(helper:tracker:swiftObject:)")));
+ (void)callSendListHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendList(helper:tracker:)")));
+ (void)callSendStringHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendString(helper:tracker:)")));
+ (void)callSendNumberHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendNumber(helper:tracker:)")));
+ (void)callSendBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendBlock(helper:tracker:)")));
+ (void)callSendCompletionHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendCompletion(helper:tracker:)")));
+ (void)callReceiveKotlinObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveKotlinObject(helper:tracker:)")));
+ (void)callReceiveSwiftObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveSwiftObject(helper:tracker:)")));
+ (void)callReceiveListHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveList(helper:tracker:)")));
@@ -962,11 +962,18 @@ __attribute__((swift_name("NoAutoreleaseSendHelper")))
@protocol KtNoAutoreleaseSendHelper
@required
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@end;
__attribute__((swift_name("NoAutoreleaseReceiveHelper")))
@@ -985,11 +992,18 @@ __attribute__((swift_name("NoAutoreleaseKotlinSendHelper")))
@interface KtNoAutoreleaseKotlinSendHelper : KtBase <KtNoAutoreleaseSendHelper>
- (instancetype)initWithKotlinLivenessTracker:(KtKotlinLivenessTracker *)kotlinLivenessTracker __attribute__((swift_name("init(kotlinLivenessTracker:)"))) __attribute__((objc_designated_initializer));
- (void)sendKotlinObjectKotlinObject:(KtKotlinObject *)kotlinObject __attribute__((swift_name("sendKotlinObject(kotlinObject:)")));
- (void (^)(KtKotlinObject *))blockReceivingKotlinObject __attribute__((swift_name("blockReceivingKotlinObject()")));
- (void)sendSwiftObjectSwiftObject:(id)swiftObject __attribute__((swift_name("sendSwiftObject(swiftObject:)")));
- (void)sendListList:(NSArray<id> *)list __attribute__((swift_name("sendList(list:)")));
- (void)sendStringString:(NSString *)string __attribute__((swift_name("sendString(string:)")));
- (void)sendNumberNumber:(id)number __attribute__((swift_name("sendNumber(number:)")));
- (void)sendBlockBlock:(KtKotlinObject *(^)(void))block __attribute__((swift_name("sendBlock(block:)")));
/**
@note This method converts instances of CancellationException to errors.
Other uncaught Kotlin exceptions are fatal.
*/
- (void)sendCompletionWithCompletionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("sendCompletion(completionHandler:)")));
@property (readonly) KtKotlinLivenessTracker *kotlinLivenessTracker __attribute__((swift_name("kotlinLivenessTracker")));
@end;
@@ -1033,11 +1047,13 @@ __attribute__((swift_name("NoAutoreleaseKt")))
@interface KtNoAutoreleaseKt : KtBase
+ (void)gc __attribute__((swift_name("gc()")));
+ (void)callSendKotlinObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendKotlinObject(helper:tracker:)")));
+ (void)sendKotlinObjectToBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("sendKotlinObjectToBlock(helper:tracker:)")));
+ (void)callSendSwiftObjectHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker swiftObject:(id)swiftObject __attribute__((swift_name("callSendSwiftObject(helper:tracker:swiftObject:)")));
+ (void)callSendListHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendList(helper:tracker:)")));
+ (void)callSendStringHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendString(helper:tracker:)")));
+ (void)callSendNumberHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendNumber(helper:tracker:)")));
+ (void)callSendBlockHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendBlock(helper:tracker:)")));
+ (void)callSendCompletionHelper:(id<KtNoAutoreleaseSendHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callSendCompletion(helper:tracker:)")));
+ (void)callReceiveKotlinObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveKotlinObject(helper:tracker:)")));
+ (void)callReceiveSwiftObjectHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveSwiftObject(helper:tracker:)")));
+ (void)callReceiveListHelper:(id<KtNoAutoreleaseReceiveHelper>)helper tracker:(KtKotlinLivenessTracker *)tracker __attribute__((swift_name("callReceiveList(helper:tracker:)")));
@@ -1,5 +1,7 @@
package noAutorelease
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.native.internal.NativePtr
import kotlin.native.ref.WeakReference
import kotlin.test.*
@@ -23,11 +25,13 @@ class KotlinObject
interface NoAutoreleaseSendHelper {
fun sendKotlinObject(kotlinObject: KotlinObject)
fun blockReceivingKotlinObject(): (KotlinObject) -> Unit
fun sendSwiftObject(swiftObject: Any)
fun sendList(list: List<*>)
fun sendString(string: String)
fun sendNumber(number: Any)
fun sendBlock(block: () -> KotlinObject)
suspend fun sendCompletion(): Any?
}
interface NoAutoreleaseReceiveHelper {
@@ -41,11 +45,16 @@ interface NoAutoreleaseReceiveHelper {
class NoAutoreleaseKotlinSendHelper(val kotlinLivenessTracker: KotlinLivenessTracker) : NoAutoreleaseSendHelper {
override fun sendKotlinObject(kotlinObject: KotlinObject) = kotlinLivenessTracker.add(kotlinObject)
override fun blockReceivingKotlinObject(): (KotlinObject) -> Unit = { kotlinLivenessTracker.add(it) }
override fun sendSwiftObject(swiftObject: Any) = kotlinLivenessTracker.add(swiftObject)
override fun sendList(list: List<*>) = kotlinLivenessTracker.add(list)
override fun sendString(string: String) = kotlinLivenessTracker.add(string)
override fun sendNumber(number: Any) = kotlinLivenessTracker.add(number)
override fun sendBlock(block: () -> KotlinObject) = kotlinLivenessTracker.add(block)
override suspend fun sendCompletion() = suspendCoroutineUninterceptedOrReturn<Any?> { continuation ->
kotlinLivenessTracker.add(continuation)
null
}
}
class NoAutoreleaseKotlinReceiveHelper(val kotlinLivenessTracker: KotlinLivenessTracker) : NoAutoreleaseReceiveHelper {
@@ -82,6 +91,14 @@ fun callSendKotlinObject(helper: NoAutoreleaseSendHelper, tracker: KotlinLivenes
tracker.add(kotlinObject)
}
fun sendKotlinObjectToBlock(helper: NoAutoreleaseSendHelper, tracker: KotlinLivenessTracker) {
val kotlinObject = KotlinObject()
helper.blockReceivingKotlinObject()(kotlinObject)
helper.blockReceivingKotlinObject()(kotlinObject)
tracker.add(kotlinObject)
}
fun callSendSwiftObject(helper: NoAutoreleaseSendHelper, tracker: KotlinLivenessTracker, swiftObject: Any) {
helper.sendSwiftObject(swiftObject)
helper.sendSwiftObject(swiftObject)
@@ -120,6 +137,22 @@ fun callSendBlock(helper: NoAutoreleaseSendHelper, tracker: KotlinLivenessTracke
tracker.add(block)
}
private class EmptyContinuation : Continuation<Any?> {
override val context: CoroutineContext = EmptyCoroutineContext
override fun resumeWith(result: Result<Any?>) { result.getOrThrow() }
}
fun callSendCompletion(helper: NoAutoreleaseSendHelper, tracker: KotlinLivenessTracker) {
val completion = EmptyContinuation()
suspend {
helper.sendCompletion()
helper.sendCompletion()
}.startCoroutine(completion)
tracker.add(completion)
}
fun callReceiveKotlinObject(helper: NoAutoreleaseReceiveHelper, tracker: KotlinLivenessTracker) = repeat(2) {
tracker.add(helper.receiveKotlinObject())
}
@@ -40,6 +40,12 @@ private class NoAutoreleaseSwiftHelper : NoAutoreleaseSendHelper, NoAutoreleaseR
swiftLivenessTracker.add(kotlinObject)
}
func blockReceivingKotlinObject() -> (KotlinObject) -> Void {
return {
self.swiftLivenessTracker.add($0)
}
}
func sendSwiftObject(swiftObject: Any) {
swiftLivenessTracker.add(swiftObject as AnyObject)
}
@@ -57,6 +63,10 @@ private class NoAutoreleaseSwiftHelper : NoAutoreleaseSendHelper, NoAutoreleaseR
func sendBlock(block: @escaping () -> KotlinObject) {
}
func sendCompletion(completionHandler: @escaping (Any?, Error?) -> Void) {
completionHandler(nil, nil)
}
let kotlinObject = KotlinObject()
let swiftObject = SwiftObject2()
let list = createList()
@@ -220,6 +230,12 @@ private func testSendKotlinObjectToKotlin() throws {
}
}
private func testSendKotlinObjectToKotlinBlock() throws {
try testSendToKotlin({ KotlinObject() }) {
$0.blockReceivingKotlinObject()($1)
}
}
private func testSendSwiftObjectToKotlin() throws {
try testSendToKotlin({ SwiftObject1() }) {
$0.sendSwiftObject(swiftObject: $1)
@@ -250,6 +266,12 @@ private func testSendBlockToKotlin() throws {
}
}
private func testSendCompletionToKotlin() throws {
try testSendToKotlin({ createCompletion() }, flags: TestFlags(trackSwiftLifetime: false)) {
$0.sendCompletion(completionHandler: $1)
}
}
private func testReceiveKotlinObjectFromKotlin() throws {
try testReceiveFromKotlin {
$0.receiveKotlinObject()
@@ -328,6 +350,15 @@ private func testSendKotlinObjectToSwift() throws {
}
}
private func testSendKotlinObjectToSwiftBlock() throws {
// Getting Swift block in Kotlin still adds it to the autoreleasepool.
// The block is not our target, so just use `checkAutorelease: false` flag
// to disable the relevant check for now.
try testCallToSwift(flags: TestFlags(checkAutorelease: false)) {
NoAutoreleaseKt.sendKotlinObjectToBlock(helper: $0, tracker: $1)
}
}
private func testSendSwiftObjectToSwift() throws {
try testCallToSwift {
NoAutoreleaseKt.callSendSwiftObject(helper: $0, tracker: $1, swiftObject: SwiftObject4())
@@ -361,6 +392,12 @@ private func testSendBlockToSwift() throws {
}
}
private func testSendCompletionToSwift() throws {
try testCallToSwift(flags: TestFlags(trackSwiftLifetime: false)) {
NoAutoreleaseKt.callSendCompletion(helper: $0, tracker: $1)
}
}
private func testReceiveKotlinObjectFromSwift() throws {
try testCallToSwift(flags: TestFlags(runGCTwice: true)) {
NoAutoreleaseKt.callReceiveKotlinObject(helper: $0, tracker: $1)
@@ -421,6 +458,10 @@ private func createBlock(swiftLivenessTracker: SwiftLivenessTracker) -> () -> Ko
}
}
private func createCompletion() -> (Any?, Error?) -> Void {
return { _, _ in }
}
private func createString() -> String {
return NSObject().description // Make it dynamic.
}
@@ -434,11 +475,13 @@ class NoAutoreleaseTests : SimpleTestProvider {
super.init()
test("testSendKotlinObjectToKotlin", testSendKotlinObjectToKotlin)
test("testSendKotlinObjectToKotlinBlock", testSendKotlinObjectToKotlinBlock)
test("testSendSwiftObjectToKotlin", testSendSwiftObjectToKotlin)
test("testSendListToKotlin", testSendListToKotlin)
test("testSendStringToKotlin", testSendStringToKotlin)
test("testSendNumberToKotlin", testSendNumberToKotlin)
test("testSendBlockToKotlin", testSendBlockToKotlin)
test("testSendCompletionToKotlin", testSendCompletionToKotlin)
test("testReceiveKotlinObjectFromKotlin", testReceiveKotlinObjectFromKotlin)
test("testReceiveSwiftObjectFromKotlin", testReceiveSwiftObjectFromKotlin)
@@ -454,18 +497,16 @@ class NoAutoreleaseTests : SimpleTestProvider {
test("testGetKotlinSingleton", testGetKotlinSingleton)
test("testGetKotlinEnumEntry", testGetKotlinEnumEntry)
#if false
test("testSendKotlinObjectToSwift", testSendKotlinObjectToSwift)
#endif
test("testSendKotlinObjectToSwiftBlock", testSendKotlinObjectToSwiftBlock)
test("testSendSwiftObjectToSwift", testSendSwiftObjectToSwift)
#if false
test("testSendListToSwift", testSendListToSwift)
test("testSendStringToSwift", testSendStringToSwift)
test("testSendNumberToSwift", testSendNumberToSwift)
test("testSendBlockToSwift", testSendBlockToSwift)
test("testSendCompletionToSwift", testSendCompletionToSwift)
#if false
test("testReceiveKotlinObjectFromSwift", testReceiveKotlinObjectFromSwift)
test("testReceiveSwiftObjectFromSwift", testReceiveSwiftObjectFromSwift)
test("testReceiveListFromSwift", testReceiveListFromSwift)