Fix Objective-C block to kotlin.Function* dynamic conversion (#3499)

after migrating to synthetic function classes.
This commit is contained in:
SvyatoslavScherbina
2019-10-28 10:02:32 +03:00
committed by GitHub
parent fe5ef5bde9
commit aac2de48fc
6 changed files with 53 additions and 10 deletions
@@ -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 -------
+14 -5
View File
@@ -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);
+1
View File
@@ -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?>