Fix Objective-C block to kotlin.Function* dynamic conversion (#3499)
after migrating to synthetic function classes.
This commit is contained in:
committed by
GitHub
parent
fe5ef5bde9
commit
aac2de48fc
+24
-5
@@ -487,6 +487,9 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo(
|
|||||||
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef
|
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef
|
||||||
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
|
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
|
||||||
|
|
||||||
|
private val ObjCExportCodeGenerator.objCToKotlinFunctionType: LLVMTypeRef
|
||||||
|
get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr)
|
||||||
|
|
||||||
private fun ObjCExportCodeGenerator.emitBoxConverters() {
|
private fun ObjCExportCodeGenerator.emitBoxConverters() {
|
||||||
val irBuiltIns = context.irBuiltIns
|
val irBuiltIns = context.irBuiltIns
|
||||||
|
|
||||||
@@ -537,22 +540,38 @@ private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() {
|
private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() {
|
||||||
val converters = context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.map {
|
val functionClassesByArity =
|
||||||
val bridge = BlockPointerBridge(numberOfParameters = it.arity, returnsVoid = false)
|
context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.associateBy { it.arity }
|
||||||
constPointer(blockToKotlinFunctionConverter(bridge))
|
|
||||||
|
val count = (functionClassesByArity.keys.max() ?: -1) + 1
|
||||||
|
|
||||||
|
val converters = (0 until count).map { arity ->
|
||||||
|
val functionClass = functionClassesByArity[arity]
|
||||||
|
if (functionClass != null) {
|
||||||
|
val bridge = BlockPointerBridge(numberOfParameters = functionClass.arity, returnsVoid = false)
|
||||||
|
constPointer(blockToKotlinFunctionConverter(bridge))
|
||||||
|
} else {
|
||||||
|
NullPointer(objCToKotlinFunctionType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val ptr = staticData.placeGlobalArray(
|
val ptr = staticData.placeGlobalArray(
|
||||||
"",
|
"",
|
||||||
converters.first().llvmType,
|
pointerType(objCToKotlinFunctionType),
|
||||||
converters
|
converters
|
||||||
).pointer.getElementPtr(0)
|
).pointer.getElementPtr(0)
|
||||||
|
|
||||||
// Note: this global replaces the weak global defined in runtime.
|
// Note: replacing weak globals defined in runtime.
|
||||||
replaceExternalWeakOrCommonGlobal(
|
replaceExternalWeakOrCommonGlobal(
|
||||||
"Kotlin_ObjCExport_blockToFunctionConverters",
|
"Kotlin_ObjCExport_blockToFunctionConverters",
|
||||||
ptr,
|
ptr,
|
||||||
context.standardLlvmSymbolsOrigin
|
context.standardLlvmSymbolsOrigin
|
||||||
)
|
)
|
||||||
|
replaceExternalWeakOrCommonGlobal(
|
||||||
|
"Kotlin_ObjCExport_blockToFunctionConverters_size",
|
||||||
|
Int32(count),
|
||||||
|
context.standardLlvmSymbolsOrigin
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import kotlin.reflect.KClass
|
|||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
// Ensure loaded function IR classes aren't ordered by arity:
|
||||||
|
internal fun referenceFunction1(block: (Any?) -> Unit) {}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const val dbl: Double = 3.14
|
const val dbl: Double = 3.14
|
||||||
const val flt: Float = 2.73F
|
const val flt: Float = 2.73F
|
||||||
|
|||||||
@@ -392,6 +392,13 @@ func testLambda() throws {
|
|||||||
try assertTrue(ValuesKt.isFunction0(obj: blockMustBeFunction0))
|
try assertTrue(ValuesKt.isFunction0(obj: blockMustBeFunction0))
|
||||||
try assertFalse(ValuesKt.isFunction(obj: NSObject()))
|
try assertFalse(ValuesKt.isFunction(obj: NSObject()))
|
||||||
try assertFalse(ValuesKt.isFunction0(obj: NSObject()))
|
try assertFalse(ValuesKt.isFunction0(obj: NSObject()))
|
||||||
|
|
||||||
|
// Test no function class for dynamic conversion:
|
||||||
|
let blockAsMissingFunction: @convention(block) (AnyObject?, AnyObject?, AnyObject?, AnyObject?, AnyObject?) -> AnyObject?
|
||||||
|
= { return $0 ?? $1 ?? $2 ?? $3 ?? $4 }
|
||||||
|
|
||||||
|
try assertTrue(ValuesKt.isFunction(obj: blockAsMissingFunction))
|
||||||
|
try assertFalse(ValuesKt.isFunction0(obj: blockAsMissingFunction))
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- Tests for classes and interfaces -------
|
// -------- Tests for classes and interfaces -------
|
||||||
|
|||||||
@@ -440,6 +440,9 @@ static const char* getBlockEncoding(id block) {
|
|||||||
|
|
||||||
// Note: replaced by compiler in appropriate compilation modes.
|
// Note: replaced by compiler in appropriate compilation modes.
|
||||||
__attribute__((weak)) convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters = nullptr;
|
__attribute__((weak)) convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters = nullptr;
|
||||||
|
__attribute__((weak)) int Kotlin_ObjCExport_blockToFunctionConverters_size = 0;
|
||||||
|
|
||||||
|
extern "C" id objc_retainBlock(id self);
|
||||||
|
|
||||||
static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
|
static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
|
||||||
const char* encoding = getBlockEncoding(block);
|
const char* encoding = getBlockEncoding(block);
|
||||||
@@ -448,10 +451,6 @@ static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
|
|||||||
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
|
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
|
||||||
int parameterCount = signature.numberOfArguments - 1; // 1 for the block itself.
|
int parameterCount = signature.numberOfArguments - 1; // 1 for the block itself.
|
||||||
|
|
||||||
if (parameterCount > 22) {
|
|
||||||
[NSException raise:NSGenericException format:@"Blocks with %d (>22) parameters aren't supported", parameterCount];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i <= parameterCount; ++i) {
|
for (int i = 1; i <= parameterCount; ++i) {
|
||||||
const char* argEncoding = [signature getArgumentTypeAtIndex:i];
|
const char* argEncoding = [signature getArgumentTypeAtIndex:i];
|
||||||
if (argEncoding[0] != '@') {
|
if (argEncoding[0] != '@') {
|
||||||
@@ -466,7 +465,17 @@ static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
|
|||||||
format:@"Blocks with non-reference-typed return value aren't supported (%s)", returnTypeEncoding];
|
format:@"Blocks with non-reference-typed return value aren't supported (%s)", returnTypeEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_RESULT_OF(Kotlin_ObjCExport_blockToFunctionConverters[parameterCount], block);
|
auto converter = parameterCount < Kotlin_ObjCExport_blockToFunctionConverters_size
|
||||||
|
? Kotlin_ObjCExport_blockToFunctionConverters[parameterCount]
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
if (converter != nullptr) {
|
||||||
|
RETURN_RESULT_OF(converter, block);
|
||||||
|
} else {
|
||||||
|
// There is no function class for this arity, so resulting object will not be cast to FunctionN class,
|
||||||
|
// and it is enough to convert block to arbitrary object conforming Function.
|
||||||
|
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, theOpaqueFunctionTypeInfo, objc_retainBlock(block));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ extern const TypeInfo* theFloatArrayTypeInfo;
|
|||||||
extern const TypeInfo* theForeignObjCObjectTypeInfo;
|
extern const TypeInfo* theForeignObjCObjectTypeInfo;
|
||||||
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo;
|
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo;
|
||||||
extern const TypeInfo* theObjCObjectWrapperTypeInfo;
|
extern const TypeInfo* theObjCObjectWrapperTypeInfo;
|
||||||
|
extern const TypeInfo* theOpaqueFunctionTypeInfo;
|
||||||
extern const TypeInfo* theShortArrayTypeInfo;
|
extern const TypeInfo* theShortArrayTypeInfo;
|
||||||
extern const TypeInfo* theStringTypeInfo;
|
extern const TypeInfo* theStringTypeInfo;
|
||||||
extern const TypeInfo* theThrowableTypeInfo;
|
extern const TypeInfo* theThrowableTypeInfo;
|
||||||
|
|||||||
@@ -295,3 +295,7 @@ private fun Kotlin_Throwable_getMessage(throwable: Throwable): String? = throwab
|
|||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
private fun Kotlin_ObjCExport_getWrappedError(throwable: Throwable): Any? =
|
private fun Kotlin_ObjCExport_getWrappedError(throwable: Throwable): Any? =
|
||||||
(throwable as? ObjCErrorException)?.error
|
(throwable as? ObjCErrorException)?.error
|
||||||
|
|
||||||
|
@ExportTypeInfo("theOpaqueFunctionTypeInfo")
|
||||||
|
@PublishedApi
|
||||||
|
internal class OpaqueFunction : Function<Any?>
|
||||||
|
|||||||
Reference in New Issue
Block a user