[K/N] Modularise :kotlin-native:native.backend to extract objc header generation (2/2)

^KT-63905 Fixed
This commit is contained in:
Sebastian Sellmair
2023-11-29 11:41:56 +01:00
committed by Space Team
parent ae9f3d66c2
commit 0713866c1e
37 changed files with 601 additions and 458 deletions
+4 -35
View File
@@ -29,12 +29,6 @@ sourceSets {
cli_bc {
kotlin.srcDir 'cli.bc/src'
}
test {
kotlin {
srcDir 'functionalTest/src'
}
}
}
compileCompilerKotlin {
@@ -43,6 +37,7 @@ compileCompilerKotlin {
'-opt-in=kotlin.RequiresOptIn',
"-opt-in=kotlinx.cinterop.BetaInteropApi",
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-opt-in=org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi",
'-Xskip-prerelease-check']
}
@@ -159,6 +154,8 @@ dependencies {
kotlin_script_runtime_jar project(":kotlin-script-runtime")
compilerApi project(":kotlin-native:utilities:basic-utils")
compilerApi project(":native:objcexport-header-generator")
compilerApi project(":native:base")
compilerImplementation project(":kotlin-compiler")
compilerApi project(":native:kotlin-native-utils")
@@ -187,11 +184,6 @@ dependencies {
cli_bcApi sourceSets.compiler.output
cli_bcApiElements sourceSets.cli_bc.output
testImplementation(project(path: ":compiler:tests-common", configuration: "tests-jar"))
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
testRuntimeOnly(libs.junit.jupiter.engine)
}
classes.dependsOn 'compilerClasses', 'cli_bcClasses'
@@ -251,27 +243,4 @@ RepoArtifacts.sourcesJar(project) {
it.from(sourceSets["compiler"].allSource)
}
RepoArtifacts.javadocJar(project)
/*
Configure 'test' task
*/
TasksKt.projectTest(project, JUnitMode.JUnit5)
tasks {
test {
useJUnitPlatform()
dependsOn ':kotlin-native:dist'
systemProperty("org.jetbrains.kotlin.native.home", distDir.canonicalPath)
workingDir(rootProject.projectDir)
}
}
kotlin {
target {
compilations {
test.associateWith(compiler)
}
}
}
RepoArtifacts.javadocJar(project)
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.atMostOne
fun IrType.getInlinedClassNative(): IrClass? = IrTypeInlineClassesSupport.getInlinedClass(this)
fun IrType.isInlinedNative(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isInlined(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isNativePrimitiveType() = IrTypeInlineClassesSupport.isTopLevelClass(this) &&
KonanPrimitiveType.byFqNameParts[packageFqName]?.get(name) != null
fun IrType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun IrType.computeBinaryType(): BinaryType<IrClass> = IrTypeInlineClassesSupport.computeBinaryType(this)
fun IrClass.inlinedClassIsNullable(): Boolean = this.defaultType.makeNullable().getInlinedClassNative() == this // TODO: optimize
fun IrClass.isUsedAsBoxClass(): Boolean = IrTypeInlineClassesSupport.isUsedAsBoxClass(this)
fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
internal inline fun <R> IrType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: IrClass, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: IrType) -> R
): R = IrTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
internal object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.isNullable()
override fun makeNullable(type: IrType): IrType = type.makeNullable()
override tailrec fun erase(type: IrType): IrClass {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}
override fun computeFullErasure(type: IrType): Sequence<IrClass> = when (val classifier = type.classifierOrFail) {
is IrClassSymbol -> sequenceOf(classifier.owner)
is IrTypeParameterSymbol -> classifier.owner.superTypes.asSequence().flatMap { computeFullErasure(it) }
is IrScriptSymbol -> classifier.unexpectedSymbolKind<IrClassifierSymbol>()
}
override fun hasInlineModifier(clazz: IrClass): Boolean = clazz.isSingleFieldValueClass
override fun getNativePointedSuperclass(clazz: IrClass): IrClass? {
var superClass: IrClass? = clazz
while (superClass != null && InteropFqNames.nativePointed.toSafe() != superClass.fqNameWhenAvailable)
superClass = superClass.getSuperClassNotAny()
return superClass
}
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
?: clazz.declarations.filterIsInstance<IrProperty>().atMostOne { it.backingField?.takeUnless { it.isStatic } != null }?.backingField?.type
?: clazz.inlineClassRepresentation!!.underlyingType
override fun getPackageFqName(clazz: IrClass) =
clazz.packageFqName
override fun getName(clazz: IrClass): Name? =
clazz.name
override fun isTopLevelClass(clazz: IrClass): Boolean = clazz.isTopLevel
}
@@ -41,31 +41,6 @@ internal val IrFunction.isTypedIntrinsic: Boolean
internal val IrConstructor.isConstantConstructorIntrinsic: Boolean
get() = annotations.hasAnnotation(KonanFqNames.constantConstructorIntrinsic)
internal val arrayTypes = setOf(
"kotlin.Array",
"kotlin.ByteArray",
"kotlin.CharArray",
"kotlin.ShortArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray",
"kotlin.native.ImmutableBlob",
"kotlin.native.internal.NativePtrArray"
)
internal val arraysWithFixedSizeItems = setOf(
"kotlin.ByteArray",
"kotlin.CharArray",
"kotlin.ShortArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray"
)
internal val IrClass.isArray: Boolean
get() = this.fqNameForIrSerialization.asString() in arrayTypes
@@ -1075,10 +1075,10 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
is MethodBridge.ReturnValue.Mapped -> if (LLVMTypeOf(targetResult!!) == llvm.voidType) {
returnBridge.bridge.makeNothing(llvm)
} else {
when (returnBridge.bridge) {
when (val bridge = returnBridge.bridge) {
is ReferenceBridge -> return autoreleaseAndRet(kotlinReferenceToRetainedObjC(targetResult))
is BlockPointerBridge -> return autoreleaseAndRet(kotlinFunctionToRetainedObjCBlockPointer(returnBridge.bridge, targetResult))
is ValueTypeBridge -> kotlinToObjC(targetResult, returnBridge.bridge.objCValueType)
is BlockPointerBridge -> return autoreleaseAndRet(kotlinFunctionToRetainedObjCBlockPointer(bridge, targetResult))
is ValueTypeBridge -> kotlinToObjC(targetResult, bridge.objCValueType)
}
}
MethodBridge.ReturnValue.WithError.Success -> llvm.int8(1) // true
@@ -1198,11 +1198,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
if (LLVMTypeOf(kotlinValue) == llvm.voidType) {
bridge.bridge.makeNothing(llvm)
} else {
when (bridge.bridge) {
when (val typeBridge = bridge.bridge) {
is ReferenceBridge -> kotlinReferenceToRetainedObjC(kotlinValue).also { objCReferenceArgsToRelease += it }
is BlockPointerBridge -> kotlinFunctionToRetainedObjCBlockPointer(bridge.bridge, kotlinValue) // TODO: use stack-allocated block here.
is BlockPointerBridge -> kotlinFunctionToRetainedObjCBlockPointer(typeBridge, kotlinValue) // TODO: use stack-allocated block here.
.also { objCReferenceArgsToRelease += it }
is ValueTypeBridge -> kotlinToObjC(kotlinValue, bridge.bridge.objCValueType)
is ValueTypeBridge -> kotlinToObjC(kotlinValue, typeBridge.objCValueType)
}
}
}
@@ -2006,3 +2006,26 @@ private fun NativeGenerationState.is64BitNSInteger(): Boolean {
}
return llvm.nsIntegerTypeWidth == 64L
}
private fun MethodBridge.parametersAssociated(
irFunction: IrFunction
): List<Pair<MethodBridgeParameter, IrValueParameter?>> {
val kotlinParameters = irFunction.allParameters.iterator()
return this.paramBridges.map {
when (it) {
is MethodBridgeValueParameter.Mapped,
MethodBridgeReceiver.Instance,
is MethodBridgeValueParameter.SuspendCompletion ->
it to kotlinParameters.next()
MethodBridgeReceiver.Static, MethodBridgeSelector, MethodBridgeValueParameter.ErrorOutParameter ->
it to null
MethodBridgeReceiver.Factory -> {
kotlinParameters.next()
it to null
}
}
}.also { assert(!kotlinParameters.hasNext()) }
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.llvm.objcexport
import org.jetbrains.kotlin.backend.konan.llvm.LlvmParameterAttribute
import org.jetbrains.kotlin.backend.konan.objcexport.*
internal val ObjCValueType.defaultParameterAttributes: List<LlvmParameterAttribute>
get() = when (this) {
ObjCValueType.BOOL -> listOf(LlvmParameterAttribute.SignExt)
ObjCValueType.UNICHAR -> listOf(LlvmParameterAttribute.ZeroExt)
ObjCValueType.CHAR -> listOf(LlvmParameterAttribute.SignExt)
ObjCValueType.SHORT -> listOf(LlvmParameterAttribute.SignExt)
ObjCValueType.UNSIGNED_CHAR -> listOf(LlvmParameterAttribute.ZeroExt)
ObjCValueType.UNSIGNED_SHORT -> listOf(LlvmParameterAttribute.ZeroExt)
else -> emptyList()
}
@@ -11,14 +11,15 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportBlockCodeGenerator
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
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.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.file.createTempFile
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.source.getPsi
internal class ObjCExportedInterface(
val generatedClasses: Set<ClassDescriptor>,
@@ -52,7 +53,7 @@ internal fun produceObjCExportInterface(
val ignoreInterfaceMethodCollisions = config.configuration.getBoolean(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions)
val reportNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportReportNameCollisions)
val problemCollector = ObjCExportHeaderGeneratorImpl.ProblemCollector(context)
val problemCollector = ObjCExportCompilerProblemCollector(context)
val namer = ObjCExportNamerImpl(
moduleDescriptors.toSet(),
@@ -66,11 +67,36 @@ internal fun produceObjCExportInterface(
ignoreInterfaceMethodCollisions = ignoreInterfaceMethodCollisions,
reportNameCollisions = reportNameCollisions,
)
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer, problemCollector, objcGenerics)
val shouldExportKDoc = context.shouldExportKDoc()
val additionalImports = context.config.configuration.getNotNull(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS)
val headerGenerator = ObjCExportHeaderGenerator.createInstance(
moduleDescriptors, mapper, namer, problemCollector, objcGenerics, shouldExportKDoc = shouldExportKDoc,
additionalImports = additionalImports)
headerGenerator.translateModule()
return headerGenerator.buildInterface()
}
private class ObjCExportCompilerProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
override fun reportWarning(text: String) {
context.reportCompilationWarning(text)
}
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {
val psi = (declaration as? DeclarationDescriptorWithSource)?.source?.getPsi()
?: return reportWarning(
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
)
val location = MessageUtil.psiElementToMessageLocation(psi)
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
}
override fun reportException(throwable: Throwable) {
throw throwable
}
}
/**
* Populate framework directory with headers, module and info.plist.
*/
@@ -189,3 +215,6 @@ private fun ObjCExportedInterface.generateWorkaroundForSwiftSR10177(generationSt
// In this case resulting framework will likely be unusable due to compile errors when importing it.
}
}
internal val PhaseContext.objCExportTopLevelNamePrefix: String
get() = abbreviate(config.fullExportedNamePrefix)