diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index ca15cedd475..72ed02ddcee 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -44,6 +44,8 @@ internal class LinkStage(val context: Context) { private val bitcodeLibraries = context.llvm.bitcodeToLink private val nativeDependencies = context.llvm.nativeDependenciesToLink + private val additionalBitcodeFilesToLink = context.llvm.additionalProducedBitcodeFiles + private fun MutableList.addNonEmpty(elements: List) { addAll(elements.filter { !it.isEmpty() }) } @@ -223,7 +225,7 @@ internal class LinkStage(val context: Context) { fun makeObjectFiles() { - val bitcodeFiles = listOf(emitted) + + val bitcodeFiles = listOf(emitted) + additionalBitcodeFilesToLink + bitcodeLibraries.map { it.bitcodePaths }.flatten().filter { it.isBitcode } objectFiles.add(when (platform.configurables) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 63def798176..af1689a4949 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -380,6 +380,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { .filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) } } + val additionalProducedBitcodeFiles = mutableListOf() + val staticData = StaticData(context) private val target = context.config.target diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index cfa32b0c550..22453ea2ffc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments +import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.getExportedDependencies import org.jetbrains.kotlin.backend.konan.isNativeBinary import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator @@ -15,11 +16,13 @@ import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.SourceFile +import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.konan.target.KonanTarget -import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.file.createTempFile import org.jetbrains.kotlin.konan.target.AppleConfigurables +import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.isSubpackageOf @@ -87,6 +90,8 @@ internal class ObjCExport(val context: Context) { categoryMembers = exportedInterface.categoryMembers, topLevel = exportedInterface.topLevel ) + + exportedInterface.generateWorkaroundForSwiftSR10177() } objCCodeGenerator.emitRtti() @@ -226,6 +231,48 @@ internal class ObjCExport(val context: Context) { file.writeBytes(contents.toString().toByteArray()) } + + // See https://bugs.swift.org/browse/SR-10177 + private fun ObjCExportedInterface.generateWorkaroundForSwiftSR10177() { + // Code for all protocols from the header should get into the binary. + // Objective-C protocols ABI is complicated (consider e.g. undocumented extended type encoding), + // so the easiest way to achieve this (quickly) is to compile a stub by clang. + + val protocolsStub = listOf( + "__attribute__((used)) static void __workaroundSwiftSR10177() {", + buildString { + append(" ") + generatedClasses.forEach { + if (it.isInterface) { + val protocolName = namer.getClassOrProtocolName(it).objCName + append("@protocol($protocolName); ") + } + } + }, + "}" + ) + + val source = createTempFile("protocols", ".m").deleteOnExit() + source.writeLines(headerLines + protocolsStub) + + val bitcode = createTempFile("protocols", ".bc").deleteOnExit() + + val clangCommand = context.config.clang.clangC( + source.absolutePath, + "-O2", + "-emit-llvm", + "-c", "-o", bitcode.absolutePath + ) + + val result = Command(clangCommand).getResult(withErrors = true) + + if (result.exitCode == 0) { + context.llvm.additionalProducedBitcodeFiles += bitcode.absolutePath + } else { + // Note: ignoring compile errors intentionally. + // In this case resulting framework will likely be unusable due to compile errors when importing it. + } + } } internal fun ModuleDescriptor.guessMainPackage(): FqName {