[ObjCExport] Extract generation of ObjC base declarations into shared code

KT-64226
This commit is contained in:
Sebastian Sellmair
2023-12-13 18:21:24 +01:00
committed by Space Team
parent aab9a606ce
commit 72f135396c
12 changed files with 659 additions and 153 deletions
@@ -436,44 +436,3 @@ internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor)
return bridgeType(descriptor.type)
}
@InternalKotlinNativeApi
enum class NSNumberKind(val mappedKotlinClassId: ClassId?, val objCType: ObjCType) {
CHAR(PrimitiveType.BYTE, ObjCPrimitiveType.char),
UNSIGNED_CHAR(UnsignedType.UBYTE, ObjCPrimitiveType.unsigned_char),
SHORT(PrimitiveType.SHORT, ObjCPrimitiveType.short),
UNSIGNED_SHORT(UnsignedType.USHORT, ObjCPrimitiveType.unsigned_short),
INT(PrimitiveType.INT, ObjCPrimitiveType.int),
UNSIGNED_INT(UnsignedType.UINT, ObjCPrimitiveType.unsigned_int),
LONG(ObjCPrimitiveType.long),
UNSIGNED_LONG(ObjCPrimitiveType.unsigned_long),
LONG_LONG(PrimitiveType.LONG, ObjCPrimitiveType.long_long),
UNSIGNED_LONG_LONG(UnsignedType.ULONG, ObjCPrimitiveType.unsigned_long_long),
FLOAT(PrimitiveType.FLOAT, ObjCPrimitiveType.float),
DOUBLE(PrimitiveType.DOUBLE, ObjCPrimitiveType.double),
BOOL(PrimitiveType.BOOLEAN, ObjCPrimitiveType.BOOL),
INTEGER(ObjCPrimitiveType.NSInteger),
UNSIGNED_INTEGER(ObjCPrimitiveType.NSUInteger)
;
// UNSIGNED_SHORT -> unsignedShort
private val kindName = this.name.split('_')
.joinToString("") { it.lowercase().replaceFirstChar(Char::uppercaseChar) }.replaceFirstChar(Char::lowercaseChar)
val valueSelector = kindName // unsignedShort
val initSelector = "initWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // initWithUnsignedShort:
val factorySelector = "numberWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // numberWithUnsignedShort:
constructor(
primitiveType: PrimitiveType,
objCPrimitiveType: ObjCPrimitiveType,
) : this(ClassId.topLevel(primitiveType.typeFqName), objCPrimitiveType)
constructor(
unsignedType: UnsignedType,
objCPrimitiveType: ObjCPrimitiveType,
) : this(unsignedType.classId, objCPrimitiveType)
constructor(objCPrimitiveType: ObjCPrimitiveType) : this(null, objCPrimitiveType)
}
@@ -52,115 +52,14 @@ class ObjCExportTranslatorImpl(
private val kotlinAnyName = namer.kotlinAnyName
override fun generateBaseDeclarations(): List<ObjCTopLevel> = buildTopLevel {
add {
objCInterface(namer.kotlinAnyName, superClass = "NSObject", members = buildMembers {
add { ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable")) }
add { ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) }
add { ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super")) }
})
}
// TODO: add comment to the header.
add {
ObjCInterfaceImpl(
namer.kotlinAnyName.objCName,
superProtocols = listOf("NSCopying"),
categoryName = "${namer.kotlinAnyName.objCName}Copying"
)
}
// TODO: only if appears
add {
val generics = listOf("ObjectType")
objCInterface(
namer.mutableSetName,
generics = generics,
superClass = "NSMutableSet",
superClassGenerics = generics
)
}
// TODO: only if appears
add {
val generics = listOf("KeyType", "ObjectType")
objCInterface(
namer.mutableMapName,
generics = generics,
superClass = "NSMutableDictionary",
superClassGenerics = generics
)
}
val nsErrorCategoryName = "NSError${namer.topLevelNamePrefix}KotlinException"
add {
ObjCInterfaceImpl("NSError", categoryName = nsErrorCategoryName, members = buildMembers {
add { ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly")) }
})
}
genKotlinNumbers()
}
private fun StubBuilder<ObjCTopLevel>.genKotlinNumbers() {
val members = buildMembers {
NSNumberKind.entries.forEach {
add { nsNumberFactory(it, listOf("unavailable")) }
}
NSNumberKind.entries.forEach {
add { nsNumberInit(it, listOf("unavailable")) }
}
}
add {
objCInterface(
namer.kotlinNumberName,
superClass = "NSNumber",
members = members
)
}
NSNumberKind.entries.forEach {
if (it.mappedKotlinClassId != null) add {
genKotlinNumber(it.mappedKotlinClassId, it)
}
}
}
private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface {
val name = namer.numberBoxName(kotlinClassId)
val members = buildMembers {
add { nsNumberFactory(kind) }
add { nsNumberInit(kind) }
}
return objCInterface(
name,
superClass = namer.kotlinNumberName.objCName,
members = members
)
}
private fun nsNumberInit(kind: NSNumberKind, attributes: List<String> = emptyList()): ObjCMethod {
return ObjCMethod(
null,
false,
ObjCInstanceType,
listOf(kind.factorySelector),
listOf(ObjCParameter("value", null, kind.objCType)),
attributes
)
}
private fun nsNumberFactory(kind: NSNumberKind, attributes: List<String> = emptyList()): ObjCMethod {
return ObjCMethod(
null,
true,
ObjCInstanceType,
listOf(kind.initSelector),
listOf(ObjCParameter("value", null, kind.objCType)),
attributes
)
}
override fun generateBaseDeclarations(): List<ObjCTopLevel> = objCBaseDeclarations(
topLevelNamePrefix = namer.topLevelNamePrefix,
objCNameOfAny = namer.kotlinAnyName,
objCNameOfNumber = namer.kotlinNumberName,
objCNameOfMutableMap = namer.mutableMapName,
objCNameOfMutableSet = namer.mutableSetName,
objCNameForNumberBox = { classId -> namer.numberBoxName(classId) }
)
override fun getClassIfExtension(receiverType: KotlinType): ClassDescriptor? =
mapper.getClassIfCategory(receiverType)
@@ -0,0 +1,39 @@
/*
* 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.testUtils
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCTopLevel
import org.jetbrains.kotlin.backend.konan.tests.ObjCExportBaseDeclarationsTest
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.ParameterContext
import org.junit.jupiter.api.extension.ParameterResolver
class Fe10BaseDeclarationsGeneratorExtension : ParameterResolver {
override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean {
return parameterContext.parameter.type == ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator::class.java
}
override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any {
return Fe10BaseDeclarationsGenerator
}
}
private object Fe10BaseDeclarationsGenerator : ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator {
override fun invoke(topLevelPrefix: String): List<ObjCTopLevel> {
val translator = ObjCExportTranslatorImpl(
generator = null,
mapper = createObjCExportMapper(),
namer = createObjCExportNamer(configuration = createObjCExportNamerConfiguration(topLevelNamePrefix = topLevelPrefix)),
problemCollector = ObjCExportProblemCollector.SILENT,
objcGenerics = true
)
return translator.generateBaseDeclarations()
}
}
@@ -2,4 +2,5 @@
# 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.
#
org.jetbrains.kotlin.backend.konan.testUtils.Fe10HeaderGeneratorExtension
org.jetbrains.kotlin.backend.konan.testUtils.Fe10HeaderGeneratorExtension
org.jetbrains.kotlin.backend.konan.testUtils.Fe10BaseDeclarationsGeneratorExtension