diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index b96d1e6c028..1494f03da53 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -56,6 +56,7 @@ import java.lang.System.out import kotlin.LazyThreadSafetyMode.PUBLICATION import kotlin.reflect.KProperty import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl /** @@ -207,6 +208,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { lateinit var moduleDescriptor: ModuleDescriptor + lateinit var objCExport: ObjCExport + override val builtIns: KonanBuiltIns by lazy(PUBLICATION) { moduleDescriptor.builtIns as KonanBuiltIns } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index db6e31e73e9..64f57c164b9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.serialization.* import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.config.CommonConfigurationKeys @@ -47,6 +48,14 @@ internal val frontendPhase = konanUnitPhase( description = "Frontend builds AST" ) +internal val objCExportPhase = konanUnitPhase( + op = { + objCExport = ObjCExport(this) + }, + name = "ObjCExport", + description = "Objective-C header generation" +) + internal val psiToIrPhase = konanUnitPhase( op = { // Translate AST to high level IR. @@ -273,6 +282,7 @@ internal val toplevelPhase = namedUnitPhase( name = "Compiler", description = "The whole compilation process", lower = frontendPhase then + objCExportPhase then psiToIrPhase then irGeneratorPluginsPhase then copyDefaultValuesToActualPhase then diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 73ffa812cea..709d76a32cc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -314,7 +314,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map, + val categoryMembers: Map>, + val topLevel: Map>, + val headerLines: List, + val namer: ObjCExportNamer, + val mapper: ObjCExportMapper +) +internal class ObjCExport(val context: Context) { private val target get() = context.config.target - internal fun produce() { + private val exportedInterface = produceInterface() + + private fun produceInterface(): ObjCExportedInterface? { + if (target.family != Family.IOS && target.family != Family.OSX) return null + + if (!context.config.produce.isNativeBinary) return null // TODO: emit RTTI to the same modules as classes belong to. + + val produceFramework = context.config.produce == CompilerOutputKind.FRAMEWORK + + return if (produceFramework) { + val mapper = ObjCExportMapper() + val moduleDescriptors = listOf(context.moduleDescriptor) + context.getExportedDependencies() + val namer = ObjCExportNamerImpl( + moduleDescriptors.toSet(), + context.moduleDescriptor.builtIns, + mapper, + context.moduleDescriptor.namePrefix, + local = false + ) + val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer) + headerGenerator.translateModule() + headerGenerator.buildInterface() + } else { + null + } + } + + internal fun generate(codegen: CodeGenerator) { if (target.family != Family.IOS && target.family != Family.OSX) return if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to. - val produceFramework = context.config.produce == CompilerOutputKind.FRAMEWORK - - val mapper = ObjCExportMapper() - val exportedDependencies = if (produceFramework) context.getExportedDependencies() else emptyList() - val moduleDescriptors = listOf(context.moduleDescriptor) + exportedDependencies - val namer = ObjCExportNamerImpl( - moduleDescriptors.toSet(), + val mapper = exportedInterface?.mapper ?: ObjCExportMapper() + val namer = exportedInterface?.namer ?: ObjCExportNamerImpl( + setOf(codegen.context.moduleDescriptor), context.moduleDescriptor.builtIns, mapper, context.moduleDescriptor.namePrefix, @@ -45,23 +79,20 @@ internal class ObjCExport(val codegen: CodeGenerator) { val objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper) - if (produceFramework) { - val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer) - produceFrameworkSpecific(headerGenerator) + if (exportedInterface != null) { + produceFrameworkSpecific(exportedInterface.headerLines) objCCodeGenerator.generate( - generatedClasses = headerGenerator.generatedClasses, - categoryMembers = headerGenerator.extensions, - topLevel = headerGenerator.topLevel + generatedClasses = exportedInterface.generatedClasses, + categoryMembers = exportedInterface.categoryMembers, + topLevel = exportedInterface.topLevel ) } objCCodeGenerator.emitRtti() } - private fun produceFrameworkSpecific(headerGenerator: ObjCExportHeaderGenerator) { - headerGenerator.translateModule() - + private fun produceFrameworkSpecific(headerLines: List) { val framework = File(context.config.outputFile) val frameworkContents = when(target.family) { Family.IOS -> framework @@ -75,7 +106,7 @@ internal class ObjCExport(val codegen: CodeGenerator) { val headerName = frameworkName + ".h" val header = headers.child(headerName) headers.mkdirs() - header.writeLines(headerGenerator.build()) + header.writeLines(headerLines) val modules = frameworkContents.child("Modules") modules.mkdirs() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 67a3a5485b5..7f364067146 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -703,9 +703,9 @@ abstract class ObjCExportHeaderGenerator internal constructor( this@ObjCExportHeaderGenerator.reportWarning(method, text) }) - internal val generatedClasses = mutableSetOf() - internal val extensions = mutableMapOf>() - internal val topLevel = mutableMapOf>() + private val generatedClasses = mutableSetOf() + private val extensions = mutableMapOf>() + private val topLevel = mutableMapOf>() fun build(): List = mutableListOf().apply { add("#import ") @@ -732,6 +732,11 @@ abstract class ObjCExportHeaderGenerator internal constructor( add("NS_ASSUME_NONNULL_END") } + internal fun buildInterface(): ObjCExportedInterface { + val headerLines = build() + return ObjCExportedInterface(generatedClasses, extensions, topLevel, headerLines, namer, mapper) + } + protected abstract fun reportWarning(text: String) protected abstract fun reportWarning(method: FunctionDescriptor, text: String)