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
|
||||
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
|
||||
|
||||
private val ObjCExportCodeGenerator.objCToKotlinFunctionType: LLVMTypeRef
|
||||
get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr)
|
||||
|
||||
private fun ObjCExportCodeGenerator.emitBoxConverters() {
|
||||
val irBuiltIns = context.irBuiltIns
|
||||
|
||||
@@ -537,22 +540,38 @@ private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() {
|
||||
val converters = context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.map {
|
||||
val bridge = BlockPointerBridge(numberOfParameters = it.arity, returnsVoid = false)
|
||||
constPointer(blockToKotlinFunctionConverter(bridge))
|
||||
val functionClassesByArity =
|
||||
context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.associateBy { it.arity }
|
||||
|
||||
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(
|
||||
"",
|
||||
converters.first().llvmType,
|
||||
pointerType(objCToKotlinFunctionType),
|
||||
converters
|
||||
).pointer.getElementPtr(0)
|
||||
|
||||
// Note: this global replaces the weak global defined in runtime.
|
||||
// Note: replacing weak globals defined in runtime.
|
||||
replaceExternalWeakOrCommonGlobal(
|
||||
"Kotlin_ObjCExport_blockToFunctionConverters",
|
||||
ptr,
|
||||
context.standardLlvmSymbolsOrigin
|
||||
)
|
||||
replaceExternalWeakOrCommonGlobal(
|
||||
"Kotlin_ObjCExport_blockToFunctionConverters_size",
|
||||
Int32(count),
|
||||
context.standardLlvmSymbolsOrigin
|
||||
)
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
||||
|
||||
@@ -16,6 +16,9 @@ import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
// Ensure loaded function IR classes aren't ordered by arity:
|
||||
internal fun referenceFunction1(block: (Any?) -> Unit) {}
|
||||
|
||||
// Constants
|
||||
const val dbl: Double = 3.14
|
||||
const val flt: Float = 2.73F
|
||||
|
||||
@@ -392,6 +392,13 @@ func testLambda() throws {
|
||||
try assertTrue(ValuesKt.isFunction0(obj: blockMustBeFunction0))
|
||||
try assertFalse(ValuesKt.isFunction(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 -------
|
||||
|
||||
@@ -440,6 +440,9 @@ static const char* getBlockEncoding(id block) {
|
||||
|
||||
// Note: replaced by compiler in appropriate compilation modes.
|
||||
__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) {
|
||||
const char* encoding = getBlockEncoding(block);
|
||||
@@ -448,10 +451,6 @@ static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
|
||||
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
|
||||
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) {
|
||||
const char* argEncoding = [signature getArgumentTypeAtIndex:i];
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -96,6 +96,7 @@ extern const TypeInfo* theFloatArrayTypeInfo;
|
||||
extern const TypeInfo* theForeignObjCObjectTypeInfo;
|
||||
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo;
|
||||
extern const TypeInfo* theObjCObjectWrapperTypeInfo;
|
||||
extern const TypeInfo* theOpaqueFunctionTypeInfo;
|
||||
extern const TypeInfo* theShortArrayTypeInfo;
|
||||
extern const TypeInfo* theStringTypeInfo;
|
||||
extern const TypeInfo* theThrowableTypeInfo;
|
||||
|
||||
@@ -295,3 +295,7 @@ private fun Kotlin_Throwable_getMessage(throwable: Throwable): String? = throwab
|
||||
@ExportForCppRuntime
|
||||
private fun Kotlin_ObjCExport_getWrappedError(throwable: Throwable): Any? =
|
||||
(throwable as? ObjCErrorException)?.error
|
||||
|
||||
@ExportTypeInfo("theOpaqueFunctionTypeInfo")
|
||||
@PublishedApi
|
||||
internal class OpaqueFunction : Function<Any?>
|
||||
|
||||
Reference in New Issue
Block a user