diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt index 59e8860073b..54c69ff9760 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt @@ -108,10 +108,13 @@ internal class BuiltInFictitiousFunctionIrClassFactory( val builtClasses get() = builtClassesMap.values - val builtFunctionNClasses get() = - builtClassesMap.values - .filter { it -> (it.descriptor as FunctionClassDescriptor).functionKind == FunctionClassDescriptor.Kind.Function } - .map { FunctionalInterface(it, (it.descriptor as FunctionClassDescriptor).arity) } + val builtFunctionNClasses get() = builtClassesMap.values.mapNotNull { + with(it.descriptor as FunctionClassDescriptor) { + if (functionKind == FunctionClassDescriptor.Kind.Function) + FunctionalInterface(it, arity) + else null + } + } private fun createTypeParameter(descriptor: TypeParameterDescriptor) = symbolTable?.declareGlobalTypeParameter( @@ -343,4 +346,4 @@ internal class BuiltInFictitiousFunctionIrClassFactory( declarations += fakeOverrideDescriptors.map { createFakeOverride(it) } } -} \ No newline at end of file +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 329d04e1667..030b7786125 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -600,16 +600,13 @@ private fun ObjCExportCodeGenerator.generateContinuationToCompletionConverter( } } -private const val maxConvertorsInCache = 33 +private val ObjCExportBlockCodeGenerator.mappedFunctionNClasses get() = + context.ir.symbols.functionIrClassFactory.builtFunctionNClasses + .filter { it.irClass.descriptor.isMappedFunctionClass() } private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() { require(context.producedLlvmModuleContainsStdlib) - var count = context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.size - // TODO: ugly hack to avoid huge unneeded adaptors linked into every binary, needs rework. - if (context.config.produce.isCache) { - count = count.coerceAtMost(maxConvertorsInCache) - } - context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.take(count).forEach { functionClass -> + mappedFunctionNClasses.forEach { functionClass -> val converter = kotlinFunctionToBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false)) val writableTypeInfoValue = buildWritableTypeInfoValue(converter = constPointer(converter)) @@ -619,22 +616,15 @@ private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() { private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() { require(context.producedLlvmModuleContainsStdlib) - val functionClassesByArity = - context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.associateBy { it.arity } + val functionClassesByArity = mappedFunctionNClasses.associateBy { it.arity } - var count = ((functionClassesByArity.keys.maxOrNull() ?: -1) + 1) - // TODO: ugly hack to avoid huge unneeded adaptors linked into every binary, needs rework. - if (context.config.produce.isCache) { - count = count.coerceAtMost(maxConvertorsInCache) - } - val converters = (0 until count).map { arity -> - val functionClass = functionClassesByArity[arity] - if (functionClass != null) { - val bridge = BlockPointerBridge(numberOfParameters = functionClass.arity, returnsVoid = false) + val arityLimit = (functionClassesByArity.keys.maxOrNull() ?: -1) + 1 + + val converters = (0 until arityLimit).map { arity -> + functionClassesByArity[arity]?.let { + val bridge = BlockPointerBridge(numberOfParameters = arity, returnsVoid = false) constPointer(blockToKotlinFunctionConverter(bridge)) - } else { - NullPointer(objCToKotlinFunctionType) - } + } ?: NullPointer(objCToKotlinFunctionType) } val ptr = staticData.placeGlobalArray( @@ -645,7 +635,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() { // Note: defining globals declared in runtime. staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr, isExported = true) - staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters_size", Int32(count), isExported = true) + staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters_size", Int32(arityLimit), isExported = true) } private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt index f28e2d1f71b..bf9d349f235 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt @@ -15,6 +15,11 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +internal fun ClassDescriptor.isMappedFunctionClass() = + this.getFunctionalClassKind() == FunctionClassDescriptor.Kind.Function && + // Type parameters include return type. + declaredTypeParameters.size - 1 < CustomTypeMappers.functionTypeMappersArityLimit + internal interface CustomTypeMapper { val mappedClassId: ClassId fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType @@ -50,6 +55,8 @@ internal object CustomTypeMappers { result.associateBy { it.mappedClassId } } + internal val functionTypeMappersArityLimit = 33 // not including, i.e. [0..33) + fun hasMapper(descriptor: ClassDescriptor): Boolean { // Should be equivalent to `getMapper(descriptor) != null`. if (descriptor.classId in predefined) return true @@ -72,9 +79,6 @@ internal object CustomTypeMappers { return null } - private fun ClassDescriptor.isMappedFunctionClass() = - this.getFunctionalClassKind() == FunctionClassDescriptor.Kind.Function - /** * Types to be "hidden" during mapping, i.e. represented as `id`. * @@ -142,4 +146,4 @@ internal object CustomTypeMappers { return translator.mapFunctionTypeIgnoringNullability(mappedSuperType, objCExportScope, returnsVoid = false) } } -} \ No newline at end of file +} diff --git a/backend.native/tests/objcexport/expectedLazy.h b/backend.native/tests/objcexport/expectedLazy.h index ddf3668ac56..dc50e7fce28 100644 --- a/backend.native/tests/objcexport/expectedLazy.h +++ b/backend.native/tests/objcexport/expectedLazy.h @@ -308,6 +308,17 @@ __attribute__((swift_name("CoroutinesKt"))) + (void)throwCancellationExceptionWithCompletionHandler:(void (^)(KtKotlinUnit * _Nullable, NSError * _Nullable))completionHandler __attribute__((swift_name("throwCancellationException(completionHandler:)"))); @end; +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("FunctionalTypesKt"))) +@interface KtFunctionalTypesKt : KtBase ++ (void)callDynType2List:(NSArray *)list param:(id _Nullable)param __attribute__((swift_name("callDynType2(list:param:)"))); ++ (void)callStaticType2Fct:(id _Nullable (^)(id _Nullable, id _Nullable))fct param:(id _Nullable)param __attribute__((swift_name("callStaticType2(fct:param:)"))); ++ (void)callDynType32List:(NSArray *)list param:(id _Nullable)param __attribute__((swift_name("callDynType32(list:param:)"))); ++ (void)callStaticType32Fct:(id _Nullable (^)(id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable))fct param:(id _Nullable)param __attribute__((swift_name("callStaticType32(fct:param:)"))); ++ (void)callDynType33List:(NSArray> *)list param:(id _Nullable)param __attribute__((swift_name("callDynType33(list:param:)"))); ++ (void)callStaticType33Fct:(id _Nullable (^)(id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable, id _Nullable))fct param:(id _Nullable)param __attribute__((swift_name("callStaticType33(fct:param:)"))); +@end; + __attribute__((swift_name("GH4002ArgumentBase"))) @interface KtGH4002ArgumentBase : KtBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); diff --git a/backend.native/tests/objcexport/functionalTypes.kt b/backend.native/tests/objcexport/functionalTypes.kt new file mode 100644 index 00000000000..46bc855f41e --- /dev/null +++ b/backend.native/tests/objcexport/functionalTypes.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package functionalTypes + +import kotlin.test.* + +typealias AN = Any? + +typealias F2 = (AN, AN) -> AN +typealias F5 = (AN, AN, AN, AN, AN) -> AN +typealias F6 = (AN, AN, AN, AN, AN, AN,) -> AN +typealias F32 = (AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, + AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, + AN, AN, AN, AN, AN, AN, AN, AN) -> AN +typealias F33 = (AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, + AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, + AN, AN, AN, AN, AN, AN, AN, AN, AN) -> AN + +fun callDynType2(list: List, param: AN) { + val fct = list.first() + val ret = fct(param, null) + assertEquals(param, ret) +} + +fun callStaticType2(fct: F2, param: AN) { + val ret = fct(param, null) + assertEquals(param, ret) +} + +fun callDynType32(list: List, param: AN) { + val fct = list.first() + val ret = fct(param + , null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + ) + assertEquals(param, ret) +} + +fun callStaticType32(fct: F32, param: AN) { + val ret = fct(param + , null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + ) + assertEquals(param, ret) +} + +fun callDynType33(list: List, param: AN) { + val fct = list.first() + val ret = fct(param + , null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null, null + ) + assertEquals(param, ret) +} + +fun callStaticType33(fct: F33, param: AN) { + val ret = fct(param + , null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null + , null, null, null, null, null, null, null, null, null + ) + assertEquals(param, ret) +} diff --git a/backend.native/tests/objcexport/functionalTypes.swift b/backend.native/tests/objcexport/functionalTypes.swift new file mode 100644 index 00000000000..8dce6099217 --- /dev/null +++ b/backend.native/tests/objcexport/functionalTypes.swift @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +import Kt + +private func test1() { + FunctionalTypesKt.callStaticType2(fct: foo2, param: "from swift") + FunctionalTypesKt.callDynType2(list: [ foo2 ], param: "from swift") + + FunctionalTypesKt.callStaticType2(fct : {a1, _ in return a1 }, param: "from swift block") + FunctionalTypesKt.callDynType2(list: [ {a1, _ in return a1 } ], param: "from swift block") + + // 32 params is mapped as regular; block is OK + FunctionalTypesKt.callStaticType32(fct : { + a1, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _ + in return a1 }, param: "from swift block") + + FunctionalTypesKt.callDynType32(list : [{ + a1, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _ + in return a1 }], param: "from swift block") + + // 33 params requires explicit implementation of KotlinFunction33 + FunctionalTypesKt.callStaticType33(fct: foo33, param: "from swift") + FunctionalTypesKt.callDynType33(list: [ Foo33() ], param: "from swift") +} + +class FunctionalTypesTests : SimpleTestProvider { + override init() { + super.init() + + test("Test1", test1) + } +} + +private func foo2(a1: Any?, _: Any?) -> Any? { + return a1 +} + +private func foo33(a1: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, + _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, + _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, + _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any?, _: Any? +) -> Any? { + return a1 +} + +private class Foo33 : KotlinFunction33 { + func invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, + p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, + p20: Any?, p21: Any?, p22: Any?, p23: Any?, p24: Any?, p25: Any?, p26: Any?, p27: Any?, p28: Any?, p29: Any?, + p30: Any?, p31: Any?, p32: Any?, p33: Any? + ) -> Any? { + return foo33(a1: p1 + , nil, nil, nil, nil, nil, nil, nil, nil + , nil, nil, nil, nil, nil, nil, nil, nil + , nil, nil, nil, nil, nil, nil, nil, nil + , nil, nil, nil, nil, nil, nil, nil, nil) + } +}