Refactor ObjCExportHeaderGenerator to allow per declaration translation
This commit is contained in:
committed by
SvyatoslavScherbina
parent
cead418d50
commit
2b8dcfc533
+14
-10
@@ -33,7 +33,7 @@ internal fun TypeBridge.makeNothing() = when (this) {
|
|||||||
|
|
||||||
internal class ObjCExportCodeGenerator(
|
internal class ObjCExportCodeGenerator(
|
||||||
codegen: CodeGenerator,
|
codegen: CodeGenerator,
|
||||||
val namer: ObjCExportNamerImpl,
|
val namer: ObjCExportNamer,
|
||||||
val mapper: ObjCExportMapper
|
val mapper: ObjCExportMapper
|
||||||
) : ObjCCodeGenerator(codegen) {
|
) : ObjCCodeGenerator(codegen) {
|
||||||
|
|
||||||
@@ -155,14 +155,15 @@ internal class ObjCExportCodeGenerator(
|
|||||||
return callFromBridge(conversion.owner.llvmFunction, listOf(value), resultLifetime)
|
return callFromBridge(conversion.owner.llvmFunction, listOf(value), resultLifetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun emitRtti(
|
private val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>()
|
||||||
|
|
||||||
|
internal fun generate(
|
||||||
generatedClasses: Collection<ClassDescriptor>,
|
generatedClasses: Collection<ClassDescriptor>,
|
||||||
|
categoryMembers: Map<ClassDescriptor, List<CallableMemberDescriptor>>,
|
||||||
topLevel: Map<SourceFile, List<CallableMemberDescriptor>>
|
topLevel: Map<SourceFile, List<CallableMemberDescriptor>>
|
||||||
) {
|
) {
|
||||||
val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>()
|
|
||||||
|
|
||||||
generatedClasses.forEach {
|
generatedClasses.forEach {
|
||||||
objCTypeAdapters += createTypeAdapter(it)
|
objCTypeAdapters += createTypeAdapter(it, categoryMembers[it].orEmpty())
|
||||||
|
|
||||||
if (!it.isInterface) {
|
if (!it.isInterface) {
|
||||||
val className = namer.getClassOrProtocolName(it).binaryName
|
val className = namer.getClassOrProtocolName(it).binaryName
|
||||||
@@ -180,7 +181,9 @@ internal class ObjCExportCodeGenerator(
|
|||||||
val name = namer.getFileClassName(sourceFile).binaryName
|
val name = namer.getFileClassName(sourceFile).binaryName
|
||||||
dataGenerator.emitEmptyClass(name, namer.kotlinAnyName.binaryName)
|
dataGenerator.emitEmptyClass(name, namer.kotlinAnyName.binaryName)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun emitRtti() {
|
||||||
NSNumberKind.values().mapNotNull { it.mappedKotlinClassId }.forEach {
|
NSNumberKind.values().mapNotNull { it.mappedKotlinClassId }.forEach {
|
||||||
dataGenerator.exportClass("Kotlin${it.shortClassName}")
|
dataGenerator.exportClass("Kotlin${it.shortClassName}")
|
||||||
}
|
}
|
||||||
@@ -189,7 +192,7 @@ internal class ObjCExportCodeGenerator(
|
|||||||
|
|
||||||
emitSpecialClassesConvertions()
|
emitSpecialClassesConvertions()
|
||||||
|
|
||||||
objCTypeAdapters += createTypeAdapter(context.builtIns.any)
|
objCTypeAdapters += createTypeAdapter(context.builtIns.any, categoryMembers = emptyList())
|
||||||
|
|
||||||
val placedClassAdapters = mutableMapOf<String, ConstPointer>()
|
val placedClassAdapters = mutableMapOf<String, ConstPointer>()
|
||||||
val placedInterfaceAdapters = mutableMapOf<String, ConstPointer>()
|
val placedInterfaceAdapters = mutableMapOf<String, ConstPointer>()
|
||||||
@@ -397,7 +400,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
|
|||||||
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
||||||
val generator = BlockAdapterToFunctionGenerator(this)
|
val generator = BlockAdapterToFunctionGenerator(this)
|
||||||
|
|
||||||
(0 .. mapper.maxFunctionTypeParameterCount).forEach { numberOfParameters ->
|
(0 .. ObjCExportMapper.maxFunctionTypeParameterCount).forEach { numberOfParameters ->
|
||||||
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
|
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
|
||||||
setObjCExportTypeInfo(context.builtIns.getFunction(numberOfParameters), constPointer(converter))
|
setObjCExportTypeInfo(context.builtIns.getFunction(numberOfParameters), constPointer(converter))
|
||||||
}
|
}
|
||||||
@@ -421,7 +424,7 @@ private fun ObjCExportCodeGenerator.emitKotlinFunctionAdaptersToBlock() {
|
|||||||
val ptr = staticData.placeGlobalArray(
|
val ptr = staticData.placeGlobalArray(
|
||||||
"",
|
"",
|
||||||
pointerType(runtime.typeInfoType),
|
pointerType(runtime.typeInfoType),
|
||||||
(0 .. mapper.maxFunctionTypeParameterCount).map {
|
(0 .. ObjCExportMapper.maxFunctionTypeParameterCount).map {
|
||||||
generateKotlinFunctionAdapterToBlock(it)
|
generateKotlinFunctionAdapterToBlock(it)
|
||||||
}
|
}
|
||||||
).pointer.getElementPtr(0)
|
).pointer.getElementPtr(0)
|
||||||
@@ -866,7 +869,8 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjCExportCodeGenerator.createTypeAdapter(
|
private fun ObjCExportCodeGenerator.createTypeAdapter(
|
||||||
descriptor: ClassDescriptor
|
descriptor: ClassDescriptor,
|
||||||
|
categoryMembers: List<CallableMemberDescriptor>
|
||||||
): ObjCExportCodeGenerator.ObjCTypeAdapter {
|
): ObjCExportCodeGenerator.ObjCTypeAdapter {
|
||||||
val adapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
|
val adapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
|
||||||
val classAdapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
|
val classAdapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
|
||||||
@@ -883,7 +887,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val categoryMethods = mapper.getCategoryMembersFor(descriptor).toMethods()
|
val categoryMethods = categoryMembers.toMethods()
|
||||||
|
|
||||||
val exposedMethods = descriptor.contributedMethods.filter { mapper.shouldBeExposed(it) } + categoryMethods
|
val exposedMethods = descriptor.contributedMethods.filter { mapper.shouldBeExposed(it) } + categoryMethods
|
||||||
|
|
||||||
|
|||||||
+86
-27
@@ -5,58 +5,117 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
internal interface CustomTypeMapper {
|
internal interface CustomTypeMapper {
|
||||||
val mappedClassId: ClassId
|
val mappedClassId: ClassId
|
||||||
fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType
|
fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType
|
||||||
|
|
||||||
class Simple(
|
|
||||||
override val mappedClassId: ClassId,
|
|
||||||
private val objCClassName: String
|
|
||||||
) : CustomTypeMapper {
|
|
||||||
|
|
||||||
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType =
|
|
||||||
ObjCClassType(objCClassName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Collection(
|
internal object CustomTypeMappers {
|
||||||
private val generator: ObjCExportHeaderGenerator,
|
/**
|
||||||
mappedClassDescriptor: ClassDescriptor,
|
* Custom type mappers.
|
||||||
private val objCClassName: String
|
*
|
||||||
|
* Don't forget to update [hiddenTypes] after adding new one.
|
||||||
|
*/
|
||||||
|
val byClassId: Map<ClassId, CustomTypeMapper> = with(KotlinBuiltIns.FQ_NAMES) {
|
||||||
|
val result = mutableListOf<CustomTypeMapper>()
|
||||||
|
|
||||||
|
result += Collection(list, "NSArray")
|
||||||
|
result += Collection(mutableList, "NSMutableArray")
|
||||||
|
result += Collection(set, "NSSet")
|
||||||
|
result += Collection(mutableSet, { namer.mutableSetName.objCName })
|
||||||
|
result += Collection(map, "NSDictionary")
|
||||||
|
result += Collection(mutableMap, { namer.mutableMapName.objCName })
|
||||||
|
|
||||||
|
NSNumberKind.values().forEach {
|
||||||
|
// TODO: NSNumber seem to have different equality semantics.
|
||||||
|
val classId = it.mappedKotlinClassId
|
||||||
|
if (classId != null) {
|
||||||
|
result += Simple(classId, { namer.numberBoxName(classId).objCName })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
result += Simple(ClassId.topLevel(string.toSafe()), "NSString")
|
||||||
|
|
||||||
|
(0..ObjCExportMapper.maxFunctionTypeParameterCount).forEach {
|
||||||
|
result += Function(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.associateBy { it.mappedClassId }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types to be "hidden" during mapping, i.e. represented as `id`.
|
||||||
|
*
|
||||||
|
* Currently contains super types of classes handled by custom type mappers.
|
||||||
|
* Note: can be generated programmatically, but requires stdlib in this case.
|
||||||
|
*/
|
||||||
|
val hiddenTypes: Set<ClassId> = listOf(
|
||||||
|
"kotlin.Any",
|
||||||
|
"kotlin.CharSequence",
|
||||||
|
"kotlin.Comparable",
|
||||||
|
"kotlin.Function",
|
||||||
|
"kotlin.Number",
|
||||||
|
"kotlin.collections.Collection",
|
||||||
|
"kotlin.collections.Iterable",
|
||||||
|
"kotlin.collections.MutableCollection",
|
||||||
|
"kotlin.collections.MutableIterable"
|
||||||
|
).map { ClassId.topLevel(FqName(it)) }.toSet()
|
||||||
|
|
||||||
|
private class Simple(
|
||||||
|
override val mappedClassId: ClassId,
|
||||||
|
private val getObjCClassName: ObjCExportTranslatorImpl.() -> String
|
||||||
) : CustomTypeMapper {
|
) : CustomTypeMapper {
|
||||||
|
|
||||||
override val mappedClassId = mappedClassDescriptor.classId!!
|
constructor(
|
||||||
|
mappedClassId: ClassId,
|
||||||
|
objCClassName: String
|
||||||
|
) : this(mappedClassId, { objCClassName })
|
||||||
|
|
||||||
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
|
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType =
|
||||||
|
ObjCClassType(translator.getObjCClassName())
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Collection(
|
||||||
|
mappedClassFqName: FqName,
|
||||||
|
private val getObjCClassName: ObjCExportTranslatorImpl.() -> String
|
||||||
|
) : CustomTypeMapper {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
mappedClassFqName: FqName,
|
||||||
|
objCClassName: String
|
||||||
|
) : this(mappedClassFqName, { objCClassName })
|
||||||
|
|
||||||
|
override val mappedClassId = ClassId.topLevel(mappedClassFqName)
|
||||||
|
|
||||||
|
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType {
|
||||||
val typeArguments = mappedSuperType.arguments.map {
|
val typeArguments = mappedSuperType.arguments.map {
|
||||||
val argument = it.type
|
val argument = it.type
|
||||||
if (TypeUtils.isNullableType(argument)) {
|
if (TypeUtils.isNullableType(argument)) {
|
||||||
// Kotlin `null` keys and values are represented as `NSNull` singleton.
|
// Kotlin `null` keys and values are represented as `NSNull` singleton.
|
||||||
ObjCIdType
|
ObjCIdType
|
||||||
} else {
|
} else {
|
||||||
generator.mapReferenceTypeIgnoringNullability(argument)
|
translator.mapReferenceTypeIgnoringNullability(argument)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ObjCClassType(objCClassName, typeArguments)
|
return ObjCClassType(translator.getObjCClassName(), typeArguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Function(
|
private class Function(parameterCount: Int) : CustomTypeMapper {
|
||||||
private val generator: ObjCExportHeaderGenerator,
|
override val mappedClassId: ClassId = KotlinBuiltIns.getFunctionClassId(parameterCount)
|
||||||
parameterCount: Int
|
|
||||||
) : CustomTypeMapper {
|
|
||||||
override val mappedClassId: ClassId = generator.builtIns.getFunction(parameterCount).classId!!
|
|
||||||
|
|
||||||
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
|
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType {
|
||||||
val functionType = mappedSuperType
|
val functionType = mappedSuperType
|
||||||
|
|
||||||
val returnType = functionType.getReturnTypeFromFunctionType()
|
val returnType = functionType.getReturnTypeFromFunctionType()
|
||||||
@@ -64,8 +123,8 @@ internal interface CustomTypeMapper {
|
|||||||
functionType.getValueParameterTypesFromFunctionType().map { it.type }
|
functionType.getValueParameterTypesFromFunctionType().map { it.type }
|
||||||
|
|
||||||
return ObjCBlockPointerType(
|
return ObjCBlockPointerType(
|
||||||
generator.mapReferenceType(returnType),
|
translator.mapReferenceType(returnType),
|
||||||
parameterTypes.map { generator.mapReferenceType(it) }
|
parameterTypes.map { translator.mapReferenceType(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-28
@@ -6,13 +6,11 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||||
|
import org.jetbrains.kotlin.backend.konan.getExportedDependencies
|
||||||
import org.jetbrains.kotlin.backend.konan.isNativeBinary
|
import org.jetbrains.kotlin.backend.konan.isNativeBinary
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
|
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.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SourceFile
|
|
||||||
import org.jetbrains.kotlin.konan.file.File
|
import org.jetbrains.kotlin.konan.file.File
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
@@ -32,36 +30,33 @@ internal class ObjCExport(val codegen: CodeGenerator) {
|
|||||||
|
|
||||||
if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to.
|
if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to.
|
||||||
|
|
||||||
val objCCodeGenerator: ObjCExportCodeGenerator
|
val produceFramework = context.config.produce == CompilerOutputKind.FRAMEWORK
|
||||||
val generatedClasses: Set<ClassDescriptor>
|
|
||||||
val topLevelDeclarations: Map<SourceFile, List<CallableMemberDescriptor>>
|
|
||||||
|
|
||||||
if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
|
val mapper = ObjCExportMapper()
|
||||||
val headerGenerator = ObjCExportHeaderGeneratorImpl(context)
|
val exportedDependencies = if (produceFramework) context.getExportedDependencies() else emptyList()
|
||||||
|
val moduleDescriptors = listOf(context.moduleDescriptor) + exportedDependencies
|
||||||
|
val namer = ObjCExportNamerImpl(
|
||||||
|
moduleDescriptors.toSet(),
|
||||||
|
context.moduleDescriptor.builtIns,
|
||||||
|
mapper,
|
||||||
|
context.moduleDescriptor.namePrefix,
|
||||||
|
local = false
|
||||||
|
)
|
||||||
|
|
||||||
|
val objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper)
|
||||||
|
|
||||||
|
if (produceFramework) {
|
||||||
|
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer)
|
||||||
produceFrameworkSpecific(headerGenerator)
|
produceFrameworkSpecific(headerGenerator)
|
||||||
|
|
||||||
generatedClasses = headerGenerator.generatedClasses
|
objCCodeGenerator.generate(
|
||||||
topLevelDeclarations = headerGenerator.topLevel
|
generatedClasses = headerGenerator.generatedClasses,
|
||||||
objCCodeGenerator = ObjCExportCodeGenerator(codegen, headerGenerator.namer, headerGenerator.mapper)
|
categoryMembers = headerGenerator.extensions,
|
||||||
} else {
|
topLevel = headerGenerator.topLevel
|
||||||
// TODO: refactor ObjCExport* to handle this case on a general basis.
|
)
|
||||||
val mapper = object : ObjCExportMapper() {
|
|
||||||
override fun getCategoryMembersFor(descriptor: ClassDescriptor): List<CallableMemberDescriptor> =
|
|
||||||
emptyList()
|
|
||||||
|
|
||||||
override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean =
|
|
||||||
error("shouldn't reach here")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val namer = ObjCExportNamerImpl(emptySet(), context.builtIns, mapper, context.moduleDescriptor.namePrefix)
|
objCCodeGenerator.emitRtti()
|
||||||
objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper)
|
|
||||||
|
|
||||||
generatedClasses = emptySet()
|
|
||||||
topLevelDeclarations = emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
objCCodeGenerator.emitRtti(generatedClasses = generatedClasses, topLevel = topLevelDeclarations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun produceFrameworkSpecific(headerGenerator: ObjCExportHeaderGenerator) {
|
private fun produceFrameworkSpecific(headerGenerator: ObjCExportHeaderGenerator) {
|
||||||
|
|||||||
+442
-419
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||||
@@ -19,332 +18,75 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.isNullable
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
abstract class ObjCExportHeaderGenerator(
|
interface ObjCExportTranslator {
|
||||||
val moduleDescriptors: List<ModuleDescriptor>,
|
fun translateFile(file: SourceFile, declarations: List<CallableMemberDescriptor>): ObjCInterface
|
||||||
|
fun translateClass(descriptor: ClassDescriptor): ObjCInterface
|
||||||
|
fun translateInterface(descriptor: ClassDescriptor): ObjCProtocol
|
||||||
|
fun translateExtensions(classDescriptor: ClassDescriptor, declarations: List<CallableMemberDescriptor>): ObjCInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObjCExportWarningCollector {
|
||||||
|
fun reportWarning(text: String)
|
||||||
|
fun reportWarning(method: FunctionDescriptor, text: String)
|
||||||
|
|
||||||
|
object SILENT : ObjCExportWarningCollector {
|
||||||
|
override fun reportWarning(text: String) {}
|
||||||
|
override fun reportWarning(method: FunctionDescriptor, text: String) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCExportTranslatorImpl(
|
||||||
|
private val generator: ObjCExportHeaderGenerator?,
|
||||||
val builtIns: KotlinBuiltIns,
|
val builtIns: KotlinBuiltIns,
|
||||||
topLevelNamePrefix: String
|
val mapper: ObjCExportMapper,
|
||||||
) {
|
val namer: ObjCExportNamer,
|
||||||
|
val warningCollector: ObjCExportWarningCollector
|
||||||
constructor(
|
) : ObjCExportTranslator {
|
||||||
moduleDescriptor: ModuleDescriptor,
|
|
||||||
builtIns: KotlinBuiltIns,
|
|
||||||
topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
|
||||||
) : this(moduleDescriptor, emptyList(), builtIns, topLevelNamePrefix)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
moduleDescriptor: ModuleDescriptor,
|
|
||||||
exportedDependencies: List<ModuleDescriptor>,
|
|
||||||
builtIns: KotlinBuiltIns,
|
|
||||||
topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
|
||||||
) : this(listOf(moduleDescriptor) + exportedDependencies, builtIns, topLevelNamePrefix)
|
|
||||||
|
|
||||||
internal val mapper: ObjCExportMapper = object : ObjCExportMapper() {
|
|
||||||
override fun getCategoryMembersFor(descriptor: ClassDescriptor) =
|
|
||||||
extensions[descriptor].orEmpty()
|
|
||||||
|
|
||||||
override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean {
|
|
||||||
// TODO: this method duplicates some of the [mapReferenceType] logic.
|
|
||||||
return descriptor == builtIns.any ||
|
|
||||||
descriptor.getAllSuperClassifiers().any { it.classId in customTypeMappers }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val namer = ObjCExportNamerImpl(moduleDescriptors.toSet(), builtIns, mapper, topLevelNamePrefix)
|
|
||||||
|
|
||||||
internal val generatedClasses = mutableSetOf<ClassDescriptor>()
|
|
||||||
internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom type mappers.
|
|
||||||
*
|
|
||||||
* Don't forget to update [hiddenTypes] after adding new one.
|
|
||||||
*/
|
|
||||||
private val customTypeMappers: Map<ClassId, CustomTypeMapper> = with(builtIns) {
|
|
||||||
val result = mutableListOf<CustomTypeMapper>()
|
|
||||||
|
|
||||||
val generator = this@ObjCExportHeaderGenerator
|
|
||||||
|
|
||||||
result += CustomTypeMapper.Collection(generator, list, "NSArray")
|
|
||||||
result += CustomTypeMapper.Collection(generator, mutableList, "NSMutableArray")
|
|
||||||
result += CustomTypeMapper.Collection(generator, set, "NSSet")
|
|
||||||
result += CustomTypeMapper.Collection(generator, mutableSet, namer.mutableSetName.objCName)
|
|
||||||
result += CustomTypeMapper.Collection(generator, map, "NSDictionary")
|
|
||||||
result += CustomTypeMapper.Collection(generator, mutableMap, namer.mutableMapName.objCName)
|
|
||||||
|
|
||||||
NSNumberKind.values().forEach {
|
|
||||||
// TODO: NSNumber seem to have different equality semantics.
|
|
||||||
val classId = it.mappedKotlinClassId
|
|
||||||
if (classId != null) {
|
|
||||||
result += CustomTypeMapper.Simple(classId, namer.numberBoxName(classId).objCName)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
result += CustomTypeMapper.Simple(string.classId!!, "NSString")
|
|
||||||
|
|
||||||
(0..mapper.maxFunctionTypeParameterCount).forEach {
|
|
||||||
result += CustomTypeMapper.Function(generator, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.associateBy { it.mappedClassId }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Types to be "hidden" during mapping, i.e. represented as `id`.
|
|
||||||
*
|
|
||||||
* Currently contains super types of classes handled by [customTypeMappers].
|
|
||||||
* Note: can be generated programmatically, but requires stdlib in this case.
|
|
||||||
*/
|
|
||||||
private val hiddenTypes: Set<ClassId> = listOf(
|
|
||||||
"kotlin.Any",
|
|
||||||
"kotlin.CharSequence",
|
|
||||||
"kotlin.Comparable",
|
|
||||||
"kotlin.Function",
|
|
||||||
"kotlin.Number",
|
|
||||||
"kotlin.collections.Collection",
|
|
||||||
"kotlin.collections.Iterable",
|
|
||||||
"kotlin.collections.MutableCollection",
|
|
||||||
"kotlin.collections.MutableIterable"
|
|
||||||
).map { ClassId.topLevel(FqName(it)) }.toSet()
|
|
||||||
|
|
||||||
private val kotlinAnyName = namer.kotlinAnyName
|
private val kotlinAnyName = namer.kotlinAnyName
|
||||||
|
|
||||||
private val stubs = mutableListOf<Stub<*>>()
|
internal fun translateKotlinObjCClassAsUnavailableStub(descriptor: ClassDescriptor): ObjCInterface = objCInterface(
|
||||||
private val classOrInterfaceToName = mutableMapOf<ClassDescriptor, ObjCExportNamer.ClassOrProtocolName>()
|
|
||||||
|
|
||||||
private val classForwardDeclarations = mutableSetOf<String>()
|
|
||||||
private val protocolForwardDeclarations = mutableSetOf<String>()
|
|
||||||
|
|
||||||
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
|
|
||||||
private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
|
|
||||||
|
|
||||||
private fun objCInterface(
|
|
||||||
name: ObjCExportNamer.ClassOrProtocolName,
|
|
||||||
generics: List<String> = emptyList(),
|
|
||||||
descriptor: ClassDescriptor? = null,
|
|
||||||
superClass: String? = null,
|
|
||||||
superProtocols: List<String> = emptyList(),
|
|
||||||
members: List<Stub<*>> = emptyList(),
|
|
||||||
attributes: List<String> = emptyList()
|
|
||||||
): ObjCInterface = ObjCInterface(
|
|
||||||
name.objCName,
|
|
||||||
generics,
|
|
||||||
descriptor,
|
|
||||||
superClass,
|
|
||||||
superProtocols,
|
|
||||||
null,
|
|
||||||
members,
|
|
||||||
attributes + name.toNameAttributes()
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun objCProtocol(
|
|
||||||
name: ObjCExportNamer.ClassOrProtocolName,
|
|
||||||
descriptor: ClassDescriptor,
|
|
||||||
superProtocols: List<String>,
|
|
||||||
members: List<Stub<*>>,
|
|
||||||
attributes: List<String> = emptyList()
|
|
||||||
): ObjCProtocol = ObjCProtocol(
|
|
||||||
name.objCName,
|
|
||||||
descriptor,
|
|
||||||
superProtocols,
|
|
||||||
members,
|
|
||||||
attributes + name.toNameAttributes()
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
|
|
||||||
binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) },
|
|
||||||
swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun translateModule(): List<Stub<*>> {
|
|
||||||
// TODO: make the translation order stable
|
|
||||||
// to stabilize name mangling.
|
|
||||||
|
|
||||||
stubs.add(objCInterface(kotlinAnyName, superClass = "NSObject", members = buildMembers {
|
|
||||||
+ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable"))
|
|
||||||
+ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable"))
|
|
||||||
+ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super"))
|
|
||||||
}))
|
|
||||||
|
|
||||||
// TODO: add comment to the header.
|
|
||||||
stubs.add(ObjCInterface(
|
|
||||||
kotlinAnyName.objCName,
|
|
||||||
superProtocols = listOf("NSCopying"),
|
|
||||||
categoryName = "${kotlinAnyName.objCName}Copying"
|
|
||||||
))
|
|
||||||
|
|
||||||
// TODO: only if appears
|
|
||||||
stubs.add(objCInterface(
|
|
||||||
namer.mutableSetName,
|
|
||||||
generics = listOf("ObjectType"),
|
|
||||||
superClass = "NSMutableSet<ObjectType>"
|
|
||||||
))
|
|
||||||
|
|
||||||
// TODO: only if appears
|
|
||||||
stubs.add(objCInterface(
|
|
||||||
namer.mutableMapName,
|
|
||||||
generics = listOf("KeyType", "ObjectType"),
|
|
||||||
superClass = "NSMutableDictionary<KeyType, ObjectType>"
|
|
||||||
))
|
|
||||||
|
|
||||||
stubs.add(ObjCInterface("NSError", categoryName = "NSErrorKotlinException", members = buildMembers {
|
|
||||||
+ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly"))
|
|
||||||
}))
|
|
||||||
|
|
||||||
genKotlinNumbers()
|
|
||||||
|
|
||||||
val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() }
|
|
||||||
|
|
||||||
packageFragments.forEach { packageFragment ->
|
|
||||||
packageFragment.getMemberScope().getContributedDescriptors()
|
|
||||||
.asSequence()
|
|
||||||
.filterIsInstance<CallableMemberDescriptor>()
|
|
||||||
.filter { mapper.shouldBeExposed(it) }
|
|
||||||
.forEach {
|
|
||||||
val classDescriptor = mapper.getClassIfCategory(it)
|
|
||||||
if (classDescriptor != null) {
|
|
||||||
extensions.getOrPut(classDescriptor, { mutableListOf() }) += it
|
|
||||||
} else {
|
|
||||||
topLevel.getOrPut(it.findSourceFile(), { mutableListOf() }) += it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MemberScope.translateClasses() {
|
|
||||||
getContributedDescriptors()
|
|
||||||
.asSequence()
|
|
||||||
.filterIsInstance<ClassDescriptor>()
|
|
||||||
.forEach {
|
|
||||||
if (mapper.shouldBeExposed(it)) {
|
|
||||||
if (it.isInterface) {
|
|
||||||
translateInterface(it)
|
|
||||||
} else {
|
|
||||||
translateClass(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.unsubstitutedMemberScope.translateClasses()
|
|
||||||
} else if (it.isKotlinObjCClass() && mapper.shouldBeVisible(it)) {
|
|
||||||
assert(!it.isInterface)
|
|
||||||
translateKotlinObjCClassAsUnavailableStub(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packageFragments.forEach { packageFragment ->
|
|
||||||
packageFragment.getMemberScope().translateClasses()
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions.forEach { classDescriptor, declarations ->
|
|
||||||
translateExtensions(classDescriptor, declarations)
|
|
||||||
}
|
|
||||||
|
|
||||||
topLevel.forEach { sourceFile, declarations ->
|
|
||||||
translateTopLevel(sourceFile, declarations)
|
|
||||||
}
|
|
||||||
|
|
||||||
while (extraClassesToTranslate.isNotEmpty()) {
|
|
||||||
val descriptor = extraClassesToTranslate.first()
|
|
||||||
extraClassesToTranslate -= descriptor
|
|
||||||
if (descriptor.isInterface) {
|
|
||||||
translateInterface(descriptor)
|
|
||||||
} else {
|
|
||||||
translateClass(descriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stubs
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateKotlinObjCClassAsUnavailableStub(descriptor: ClassDescriptor) {
|
|
||||||
stubs.add(objCInterface(
|
|
||||||
namer.getClassOrProtocolName(descriptor),
|
namer.getClassOrProtocolName(descriptor),
|
||||||
descriptor = descriptor,
|
descriptor = descriptor,
|
||||||
superClass = "NSObject",
|
superClass = "NSObject",
|
||||||
attributes = listOf("unavailable(\"Kotlin subclass of Objective-C class can't be imported\")")
|
attributes = listOf("unavailable(\"Kotlin subclass of Objective-C class can't be imported\")")
|
||||||
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun genKotlinNumbers() {
|
|
||||||
val members = buildMembers {
|
|
||||||
NSNumberKind.values().forEach {
|
|
||||||
+nsNumberFactory(it, listOf("unavailable"))
|
|
||||||
}
|
|
||||||
NSNumberKind.values().forEach {
|
|
||||||
+nsNumberInit(it, listOf("unavailable"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stubs.add(objCInterface(
|
|
||||||
namer.kotlinNumberName,
|
|
||||||
superClass = "NSNumber",
|
|
||||||
members = members
|
|
||||||
))
|
|
||||||
|
|
||||||
NSNumberKind.values().forEach {
|
|
||||||
if (it.mappedKotlinClassId != null) {
|
|
||||||
stubs += genKotlinNumber(it.mappedKotlinClassId, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface {
|
|
||||||
val name = namer.numberBoxName(kotlinClassId)
|
|
||||||
|
|
||||||
val members = buildMembers {
|
|
||||||
+nsNumberFactory(kind)
|
|
||||||
+nsNumberInit(kind)
|
|
||||||
}
|
|
||||||
return objCInterface(
|
|
||||||
name,
|
|
||||||
superClass = namer.kotlinNumberName.objCName,
|
|
||||||
members = members
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
private fun nsNumberInit(kind: NSNumberKind, attributes: List<String> = emptyList()): ObjCMethod {
|
private fun referenceClass(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName {
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateClassName(descriptor: ClassDescriptor) = classOrInterfaceToName.getOrPut(descriptor) {
|
|
||||||
assert(mapper.shouldBeExposed(descriptor))
|
assert(mapper.shouldBeExposed(descriptor))
|
||||||
val forwardDeclarations = if (descriptor.isInterface) protocolForwardDeclarations else classForwardDeclarations
|
assert(!descriptor.isInterface)
|
||||||
|
generator?.requireClassOrInterface(descriptor)
|
||||||
|
|
||||||
namer.getClassOrProtocolName(descriptor).also { forwardDeclarations += it.objCName }
|
return translateClassOrInterfaceName(descriptor).also {
|
||||||
|
generator?.referenceClass(it.objCName, descriptor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateInterface(descriptor: ClassDescriptor) {
|
private fun referenceProtocol(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName {
|
||||||
if (!generatedClasses.add(descriptor)) return
|
assert(mapper.shouldBeExposed(descriptor))
|
||||||
|
assert(descriptor.isInterface)
|
||||||
|
generator?.requireClassOrInterface(descriptor)
|
||||||
|
|
||||||
val name = translateClassName(descriptor)
|
return translateClassOrInterfaceName(descriptor).also {
|
||||||
|
generator?.referenceProtocol(it.objCName, descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateClassOrInterfaceName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName {
|
||||||
|
assert(mapper.shouldBeExposed(descriptor))
|
||||||
|
|
||||||
|
return namer.getClassOrProtocolName(descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun translateInterface(descriptor: ClassDescriptor): ObjCProtocol {
|
||||||
|
val name = translateClassOrInterfaceName(descriptor)
|
||||||
val members: List<Stub<*>> = buildMembers { translateInterfaceMembers(descriptor) }
|
val members: List<Stub<*>> = buildMembers { translateInterfaceMembers(descriptor) }
|
||||||
val superProtocols: List<String> = descriptor.superProtocols
|
val superProtocols: List<String> = descriptor.superProtocols
|
||||||
|
|
||||||
val protocolStub = objCProtocol(name, descriptor, superProtocols, members)
|
return objCProtocol(name, descriptor, superProtocols, members)
|
||||||
|
|
||||||
stubs.add(protocolStub)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ClassDescriptor.superProtocols: List<String>
|
private val ClassDescriptor.superProtocols: List<String>
|
||||||
@@ -353,47 +95,48 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { mapper.shouldBeExposed(it) }
|
.filter { mapper.shouldBeExposed(it) }
|
||||||
.map {
|
.map {
|
||||||
translateInterface(it)
|
generator?.generateInterface(it)
|
||||||
translateClassName(it).objCName
|
referenceProtocol(it).objCName
|
||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
private fun translateExtensions(classDescriptor: ClassDescriptor, declarations: List<CallableMemberDescriptor>) {
|
override fun translateExtensions(
|
||||||
translateClass(classDescriptor)
|
classDescriptor: ClassDescriptor,
|
||||||
|
declarations: List<CallableMemberDescriptor>
|
||||||
|
): ObjCInterface {
|
||||||
|
generator?.generateClass(classDescriptor)
|
||||||
|
|
||||||
val name = translateClassName(classDescriptor).objCName
|
val name = referenceClass(classDescriptor).objCName
|
||||||
val members = buildMembers {
|
val members = buildMembers {
|
||||||
translatePlainMembers(declarations)
|
translatePlainMembers(declarations)
|
||||||
}
|
}
|
||||||
stubs.add(ObjCInterface(name, categoryName = "Extensions", members = members))
|
return ObjCInterface(name, categoryName = "Extensions", members = members)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateTopLevel(sourceFile: SourceFile, declarations: List<CallableMemberDescriptor>) {
|
override fun translateFile(file: SourceFile, declarations: List<CallableMemberDescriptor>): ObjCInterface {
|
||||||
val name = namer.getFileClassName(sourceFile)
|
val name = namer.getFileClassName(file)
|
||||||
|
|
||||||
// TODO: stop inheriting KotlinBase.
|
// TODO: stop inheriting KotlinBase.
|
||||||
val members = buildMembers {
|
val members = buildMembers {
|
||||||
translatePlainMembers(declarations)
|
translatePlainMembers(declarations)
|
||||||
}
|
}
|
||||||
stubs.add(objCInterface(
|
return objCInterface(
|
||||||
name,
|
name,
|
||||||
superClass = namer.kotlinAnyName.objCName,
|
superClass = namer.kotlinAnyName.objCName,
|
||||||
members = members,
|
members = members,
|
||||||
attributes = listOf("objc_subclassing_restricted")
|
attributes = listOf("objc_subclassing_restricted")
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateClass(descriptor: ClassDescriptor) {
|
override fun translateClass(descriptor: ClassDescriptor): ObjCInterface {
|
||||||
if (!generatedClasses.add(descriptor)) return
|
val name = translateClassOrInterfaceName(descriptor)
|
||||||
|
|
||||||
val name = translateClassName(descriptor)
|
|
||||||
val superClass = descriptor.getSuperClassNotAny()
|
val superClass = descriptor.getSuperClassNotAny()
|
||||||
|
|
||||||
val superName = if (superClass == null) {
|
val superName = if (superClass == null) {
|
||||||
kotlinAnyName
|
kotlinAnyName
|
||||||
} else {
|
} else {
|
||||||
translateClass(superClass)
|
generator?.generateClass(superClass)
|
||||||
translateClassName(superClass)
|
referenceClass(superClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
val superProtocols: List<String> = descriptor.superProtocols
|
val superProtocols: List<String> = descriptor.superProtocols
|
||||||
@@ -467,7 +210,7 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
|
|
||||||
val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList()
|
val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList()
|
||||||
|
|
||||||
val interfaceStub = objCInterface(
|
return objCInterface(
|
||||||
name,
|
name,
|
||||||
descriptor = descriptor,
|
descriptor = descriptor,
|
||||||
superClass = superName.objCName,
|
superClass = superName.objCName,
|
||||||
@@ -475,7 +218,6 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
members = members,
|
members = members,
|
||||||
attributes = attributes
|
attributes = attributes
|
||||||
)
|
)
|
||||||
stubs.add(interfaceStub)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassDescriptor.getExposedMembers(): List<CallableMemberDescriptor> =
|
private fun ClassDescriptor.getExposedMembers(): List<CallableMemberDescriptor> =
|
||||||
@@ -744,9 +486,6 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun swiftNameAttribute(swiftName: String) = "swift_name(\"$swiftName\")"
|
|
||||||
private fun objcRuntimeNameAttribute(name: String) = "objc_runtime_name(\"$name\")"
|
|
||||||
|
|
||||||
private val methodsWithThrowAnnotationConsidered = mutableSetOf<FunctionDescriptor>()
|
private val methodsWithThrowAnnotationConsidered = mutableSetOf<FunctionDescriptor>()
|
||||||
|
|
||||||
private val uncheckedExceptionClasses = listOf("Error", "RuntimeException").map {
|
private val uncheckedExceptionClasses = listOf("Error", "RuntimeException").map {
|
||||||
@@ -759,7 +498,8 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
val throwsAnnotation = method.annotations.findAnnotation(KonanFqNames.throws) ?: return
|
val throwsAnnotation = method.annotations.findAnnotation(KonanFqNames.throws) ?: return
|
||||||
|
|
||||||
if (!mapper.doesThrow(method)) {
|
if (!mapper.doesThrow(method)) {
|
||||||
reportWarning(method, "@${KonanFqNames.throws.shortName()} annotation should also be added to a base method")
|
warningCollector.reportWarning(method,
|
||||||
|
"@${KonanFqNames.throws.shortName()} annotation should also be added to a base method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method in methodsWithThrowAnnotationConsidered) return
|
if (method in methodsWithThrowAnnotationConsidered) return
|
||||||
@@ -770,13 +510,13 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
val classDescriptor = TypeUtils.getClassDescriptor((argument as KClassValue).getArgumentType(method.module)) ?: continue
|
val classDescriptor = TypeUtils.getClassDescriptor((argument as KClassValue).getArgumentType(method.module)) ?: continue
|
||||||
|
|
||||||
uncheckedExceptionClasses.firstOrNull { classDescriptor.isSubclassOf(it) }?.let {
|
uncheckedExceptionClasses.firstOrNull { classDescriptor.isSubclassOf(it) }?.let {
|
||||||
reportWarning(method,
|
warningCollector.reportWarning(method,
|
||||||
"Method is declared to throw ${classDescriptor.fqNameSafe}, " +
|
"Method is declared to throw ${classDescriptor.fqNameSafe}, " +
|
||||||
"but instances of ${it.fqNameSafe} and its subclasses aren't propagated " +
|
"but instances of ${it.fqNameSafe} and its subclasses aren't propagated " +
|
||||||
"from Kotlin to Objective-C/Swift")
|
"from Kotlin to Objective-C/Swift")
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleClassToBeGenerated(classDescriptor)
|
generator?.requireClassOrInterface(classDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,6 +540,173 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
MethodBridge.ReturnValue.Instance.FactoryResult -> ObjCInstanceType
|
MethodBridge.ReturnValue.Instance.FactoryResult -> ObjCInstanceType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun mapReferenceType(kotlinType: KotlinType): ObjCReferenceType =
|
||||||
|
mapReferenceTypeIgnoringNullability(kotlinType).let {
|
||||||
|
if (kotlinType.binaryRepresentationIsNullable()) {
|
||||||
|
ObjCNullableReferenceType(it)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType {
|
||||||
|
class TypeMappingMatch(val type: KotlinType, val descriptor: ClassDescriptor, val mapper: CustomTypeMapper)
|
||||||
|
|
||||||
|
val typeMappingMatches = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type ->
|
||||||
|
(type.constructor.declarationDescriptor as? ClassDescriptor)?.let { descriptor ->
|
||||||
|
mapper.customTypeMappers[descriptor.classId]?.let { mapper ->
|
||||||
|
TypeMappingMatch(type, descriptor, mapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mostSpecificMatches = typeMappingMatches.filter { match ->
|
||||||
|
typeMappingMatches.all { otherMatch ->
|
||||||
|
otherMatch.descriptor == match.descriptor ||
|
||||||
|
!otherMatch.descriptor.isSubclassOf(match.descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mostSpecificMatches.size > 1) {
|
||||||
|
val types = mostSpecificMatches.map { it.type }
|
||||||
|
val firstType = types[0]
|
||||||
|
val secondType = types[1]
|
||||||
|
|
||||||
|
warningCollector.reportWarning(
|
||||||
|
"Exposed type '$kotlinType' is '$firstType' and '$secondType' at the same time. " +
|
||||||
|
"This most likely wouldn't work as expected.")
|
||||||
|
|
||||||
|
// TODO: the same warning for such classes.
|
||||||
|
}
|
||||||
|
|
||||||
|
mostSpecificMatches.firstOrNull()?.let {
|
||||||
|
return it.mapper.mapType(it.type, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val classDescriptor = kotlinType.getErasedTypeClass()
|
||||||
|
|
||||||
|
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
|
||||||
|
|
||||||
|
// TODO: expose custom inline class boxes properly.
|
||||||
|
if (classDescriptor == builtIns.any || classDescriptor.classId in mapper.hiddenTypes || classDescriptor.isInlined()) {
|
||||||
|
return ObjCIdType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classDescriptor.defaultType.isObjCObjectType()) {
|
||||||
|
return mapObjCObjectReferenceTypeIgnoringNullability(classDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (classDescriptor.isInterface) {
|
||||||
|
ObjCProtocolType(referenceProtocol(classDescriptor).objCName)
|
||||||
|
} else {
|
||||||
|
ObjCClassType(referenceClass(classDescriptor).objCName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private tailrec fun mapObjCObjectReferenceTypeIgnoringNullability(descriptor: ClassDescriptor): ObjCNonNullReferenceType {
|
||||||
|
// TODO: more precise types can be used.
|
||||||
|
|
||||||
|
if (descriptor.isObjCMetaClass()) return ObjCIdType
|
||||||
|
|
||||||
|
if (descriptor.isExternalObjCClass()) {
|
||||||
|
return if (descriptor.isInterface) {
|
||||||
|
val name = descriptor.name.asString().removeSuffix("Protocol")
|
||||||
|
generator?.referenceProtocol(name)
|
||||||
|
ObjCProtocolType(name)
|
||||||
|
} else {
|
||||||
|
val name = descriptor.name.asString()
|
||||||
|
generator?.referenceClass(name)
|
||||||
|
ObjCClassType(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.isKotlinObjCClass()) {
|
||||||
|
return mapObjCObjectReferenceTypeIgnoringNullability(descriptor.getSuperClassOrAny())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjCIdType
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge): ObjCType = when (typeBridge) {
|
||||||
|
ReferenceBridge -> mapReferenceType(kotlinType)
|
||||||
|
is ValueTypeBridge -> {
|
||||||
|
when (typeBridge.objCValueType) {
|
||||||
|
ObjCValueType.BOOL -> ObjCPrimitiveType("BOOL")
|
||||||
|
ObjCValueType.UNICHAR -> ObjCPrimitiveType("unichar")
|
||||||
|
ObjCValueType.CHAR -> ObjCPrimitiveType("int8_t")
|
||||||
|
ObjCValueType.SHORT -> ObjCPrimitiveType("int16_t")
|
||||||
|
ObjCValueType.INT -> ObjCPrimitiveType("int32_t")
|
||||||
|
ObjCValueType.LONG_LONG -> ObjCPrimitiveType("int64_t")
|
||||||
|
ObjCValueType.UNSIGNED_CHAR -> ObjCPrimitiveType("uint8_t")
|
||||||
|
ObjCValueType.UNSIGNED_SHORT -> ObjCPrimitiveType("uint16_t")
|
||||||
|
ObjCValueType.UNSIGNED_INT -> ObjCPrimitiveType("uint32_t")
|
||||||
|
ObjCValueType.UNSIGNED_LONG_LONG -> ObjCPrimitiveType("uint64_t")
|
||||||
|
ObjCValueType.FLOAT -> ObjCPrimitiveType("float")
|
||||||
|
ObjCValueType.DOUBLE -> ObjCPrimitiveType("double")
|
||||||
|
ObjCValueType.POINTER -> ObjCPointerType(ObjCVoidType)
|
||||||
|
}
|
||||||
|
// TODO: consider other namings.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ObjCExportHeaderGenerator internal constructor(
|
||||||
|
val moduleDescriptors: List<ModuleDescriptor>,
|
||||||
|
val builtIns: KotlinBuiltIns,
|
||||||
|
internal val mapper: ObjCExportMapper,
|
||||||
|
val namer: ObjCExportNamer
|
||||||
|
) {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
moduleDescriptors: List<ModuleDescriptor>,
|
||||||
|
builtIns: KotlinBuiltIns,
|
||||||
|
topLevelNamePrefix: String
|
||||||
|
) : this(moduleDescriptors, builtIns, topLevelNamePrefix, ObjCExportMapper())
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
moduleDescriptors: List<ModuleDescriptor>,
|
||||||
|
builtIns: KotlinBuiltIns,
|
||||||
|
topLevelNamePrefix: String,
|
||||||
|
mapper: ObjCExportMapper
|
||||||
|
) : this(
|
||||||
|
moduleDescriptors,
|
||||||
|
builtIns,
|
||||||
|
mapper,
|
||||||
|
ObjCExportNamerImpl(moduleDescriptors.toSet(), builtIns, mapper, topLevelNamePrefix, local = false)
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
moduleDescriptor: ModuleDescriptor,
|
||||||
|
builtIns: KotlinBuiltIns,
|
||||||
|
topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
||||||
|
) : this(moduleDescriptor, emptyList(), builtIns, topLevelNamePrefix)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
moduleDescriptor: ModuleDescriptor,
|
||||||
|
exportedDependencies: List<ModuleDescriptor>,
|
||||||
|
builtIns: KotlinBuiltIns,
|
||||||
|
topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
||||||
|
) : this(listOf(moduleDescriptor) + exportedDependencies, builtIns, topLevelNamePrefix)
|
||||||
|
|
||||||
|
private val stubs = mutableListOf<Stub<*>>()
|
||||||
|
|
||||||
|
private val classForwardDeclarations = linkedSetOf<String>()
|
||||||
|
private val protocolForwardDeclarations = linkedSetOf<String>()
|
||||||
|
private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
|
||||||
|
|
||||||
|
private val translator = ObjCExportTranslatorImpl(this, builtIns, mapper, namer,
|
||||||
|
object : ObjCExportWarningCollector {
|
||||||
|
override fun reportWarning(text: String) =
|
||||||
|
this@ObjCExportHeaderGenerator.reportWarning(text)
|
||||||
|
|
||||||
|
override fun reportWarning(method: FunctionDescriptor, text: String) =
|
||||||
|
this@ObjCExportHeaderGenerator.reportWarning(method, text)
|
||||||
|
})
|
||||||
|
|
||||||
|
internal val generatedClasses = mutableSetOf<ClassDescriptor>()
|
||||||
|
internal val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
|
||||||
|
internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
|
||||||
|
|
||||||
fun build(): List<String> = mutableListOf<String>().apply {
|
fun build(): List<String> = mutableListOf<String>().apply {
|
||||||
add("#import <Foundation/Foundation.h>")
|
add("#import <Foundation/Foundation.h>")
|
||||||
add("")
|
add("")
|
||||||
@@ -829,119 +736,235 @@ abstract class ObjCExportHeaderGenerator(
|
|||||||
|
|
||||||
protected abstract fun reportWarning(method: FunctionDescriptor, text: String)
|
protected abstract fun reportWarning(method: FunctionDescriptor, text: String)
|
||||||
|
|
||||||
internal fun mapReferenceType(kotlinType: KotlinType): ObjCReferenceType =
|
|
||||||
mapReferenceTypeIgnoringNullability(kotlinType).let {
|
fun translateModule(): List<Stub<*>> {
|
||||||
if (kotlinType.binaryRepresentationIsNullable()) {
|
// TODO: make the translation order stable
|
||||||
ObjCNullableReferenceType(it)
|
// to stabilize name mangling.
|
||||||
|
|
||||||
|
stubs.add(objCInterface(namer.kotlinAnyName, superClass = "NSObject", members = buildMembers {
|
||||||
|
+ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable"))
|
||||||
|
+ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable"))
|
||||||
|
+ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super"))
|
||||||
|
}))
|
||||||
|
|
||||||
|
// TODO: add comment to the header.
|
||||||
|
stubs.add(ObjCInterface(
|
||||||
|
namer.kotlinAnyName.objCName,
|
||||||
|
superProtocols = listOf("NSCopying"),
|
||||||
|
categoryName = "${namer.kotlinAnyName.objCName}Copying"
|
||||||
|
))
|
||||||
|
|
||||||
|
// TODO: only if appears
|
||||||
|
stubs.add(objCInterface(
|
||||||
|
namer.mutableSetName,
|
||||||
|
generics = listOf("ObjectType"),
|
||||||
|
superClass = "NSMutableSet<ObjectType>"
|
||||||
|
))
|
||||||
|
|
||||||
|
// TODO: only if appears
|
||||||
|
stubs.add(objCInterface(
|
||||||
|
namer.mutableMapName,
|
||||||
|
generics = listOf("KeyType", "ObjectType"),
|
||||||
|
superClass = "NSMutableDictionary<KeyType, ObjectType>"
|
||||||
|
))
|
||||||
|
|
||||||
|
stubs.add(ObjCInterface("NSError", categoryName = "NSErrorKotlinException", members = buildMembers {
|
||||||
|
+ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly"))
|
||||||
|
}))
|
||||||
|
|
||||||
|
genKotlinNumbers()
|
||||||
|
|
||||||
|
val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() }
|
||||||
|
|
||||||
|
packageFragments.forEach { packageFragment ->
|
||||||
|
packageFragment.getMemberScope().getContributedDescriptors()
|
||||||
|
.asSequence()
|
||||||
|
.filterIsInstance<CallableMemberDescriptor>()
|
||||||
|
.filter { mapper.shouldBeExposed(it) }
|
||||||
|
.forEach {
|
||||||
|
val classDescriptor = mapper.getClassIfCategory(it)
|
||||||
|
if (classDescriptor != null) {
|
||||||
|
extensions.getOrPut(classDescriptor, { mutableListOf() }) += it
|
||||||
} else {
|
} else {
|
||||||
it
|
topLevel.getOrPut(it.findSourceFile(), { mutableListOf() }) += it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType {
|
|
||||||
class TypeMappingMatch(val type: KotlinType, val descriptor: ClassDescriptor, val mapper: CustomTypeMapper)
|
|
||||||
|
|
||||||
val typeMappingMatches = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type ->
|
|
||||||
(type.constructor.declarationDescriptor as? ClassDescriptor)?.let { descriptor ->
|
|
||||||
customTypeMappers[descriptor.classId]?.let { mapper ->
|
|
||||||
TypeMappingMatch(type, descriptor, mapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val mostSpecificMatches = typeMappingMatches.filter { match ->
|
fun MemberScope.translateClasses() {
|
||||||
typeMappingMatches.all { otherMatch ->
|
getContributedDescriptors()
|
||||||
otherMatch.descriptor == match.descriptor ||
|
.asSequence()
|
||||||
!otherMatch.descriptor.isSubclassOf(match.descriptor)
|
.filterIsInstance<ClassDescriptor>()
|
||||||
}
|
.forEach {
|
||||||
}
|
if (mapper.shouldBeExposed(it)) {
|
||||||
|
if (it.isInterface) {
|
||||||
if (mostSpecificMatches.size > 1) {
|
generateInterface(it)
|
||||||
val types = mostSpecificMatches.map { it.type }
|
|
||||||
val firstType = types[0]
|
|
||||||
val secondType = types[1]
|
|
||||||
|
|
||||||
reportWarning("Exposed type '$kotlinType' is '$firstType' and '$secondType' at the same time. " +
|
|
||||||
"This most likely wouldn't work as expected.")
|
|
||||||
|
|
||||||
// TODO: the same warning for such classes.
|
|
||||||
}
|
|
||||||
|
|
||||||
mostSpecificMatches.firstOrNull()?.let {
|
|
||||||
return it.mapper.mapType(it.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
val classDescriptor = kotlinType.getErasedTypeClass()
|
|
||||||
|
|
||||||
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
|
|
||||||
|
|
||||||
// TODO: expose custom inline class boxes properly.
|
|
||||||
if (classDescriptor == builtIns.any || classDescriptor.classId in hiddenTypes || classDescriptor.isInlined()) {
|
|
||||||
return ObjCIdType
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classDescriptor.defaultType.isObjCObjectType()) {
|
|
||||||
return mapObjCObjectReferenceTypeIgnoringNullability(classDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleClassToBeGenerated(classDescriptor)
|
|
||||||
|
|
||||||
return if (classDescriptor.isInterface) {
|
|
||||||
ObjCProtocolType(translateClassName(classDescriptor).objCName)
|
|
||||||
} else {
|
} else {
|
||||||
ObjCClassType(translateClassName(classDescriptor).objCName)
|
generateClass(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.unsubstitutedMemberScope.translateClasses()
|
||||||
|
} else if (it.isKotlinObjCClass() && mapper.shouldBeVisible(it)) {
|
||||||
|
assert(!it.isInterface)
|
||||||
|
stubs += translator.translateKotlinObjCClassAsUnavailableStub(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private tailrec fun mapObjCObjectReferenceTypeIgnoringNullability(descriptor: ClassDescriptor): ObjCNonNullReferenceType {
|
packageFragments.forEach { packageFragment ->
|
||||||
// TODO: more precise types can be used.
|
packageFragment.getMemberScope().translateClasses()
|
||||||
|
}
|
||||||
|
|
||||||
if (descriptor.isObjCMetaClass()) return ObjCIdType
|
extensions.forEach { classDescriptor, declarations ->
|
||||||
|
generateExtensions(classDescriptor, declarations)
|
||||||
|
}
|
||||||
|
|
||||||
if (descriptor.isExternalObjCClass()) {
|
topLevel.forEach { sourceFile, declarations ->
|
||||||
return if (descriptor.isInterface) {
|
generateFile(sourceFile, declarations)
|
||||||
val name = descriptor.name.asString().removeSuffix("Protocol")
|
}
|
||||||
protocolForwardDeclarations += name
|
|
||||||
ObjCProtocolType(name)
|
while (extraClassesToTranslate.isNotEmpty()) {
|
||||||
|
val descriptor = extraClassesToTranslate.first()
|
||||||
|
extraClassesToTranslate -= descriptor
|
||||||
|
if (descriptor.isInterface) {
|
||||||
|
generateInterface(descriptor)
|
||||||
} else {
|
} else {
|
||||||
val name = descriptor.name.asString()
|
generateClass(descriptor)
|
||||||
classForwardDeclarations += name
|
|
||||||
ObjCClassType(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor.isKotlinObjCClass()) {
|
return stubs
|
||||||
return mapObjCObjectReferenceTypeIgnoringNullability(descriptor.getSuperClassOrAny())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ObjCIdType
|
private fun genKotlinNumbers() {
|
||||||
|
val members = buildMembers {
|
||||||
|
NSNumberKind.values().forEach {
|
||||||
|
+nsNumberFactory(it, listOf("unavailable"))
|
||||||
}
|
}
|
||||||
|
NSNumberKind.values().forEach {
|
||||||
|
+nsNumberInit(it, listOf("unavailable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stubs.add(objCInterface(
|
||||||
|
namer.kotlinNumberName,
|
||||||
|
superClass = "NSNumber",
|
||||||
|
members = members
|
||||||
|
))
|
||||||
|
|
||||||
private fun scheduleClassToBeGenerated(classDescriptor: ClassDescriptor) {
|
NSNumberKind.values().forEach {
|
||||||
if (classDescriptor !in generatedClasses) {
|
if (it.mappedKotlinClassId != null) {
|
||||||
extraClassesToTranslate += classDescriptor
|
stubs += genKotlinNumber(it.mappedKotlinClassId, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge): ObjCType = when (typeBridge) {
|
private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface {
|
||||||
ReferenceBridge -> mapReferenceType(kotlinType)
|
val name = namer.numberBoxName(kotlinClassId)
|
||||||
is ValueTypeBridge -> {
|
|
||||||
when (typeBridge.objCValueType) {
|
val members = buildMembers {
|
||||||
ObjCValueType.BOOL -> ObjCPrimitiveType("BOOL")
|
+nsNumberFactory(kind)
|
||||||
ObjCValueType.UNICHAR -> ObjCPrimitiveType("unichar")
|
+nsNumberInit(kind)
|
||||||
ObjCValueType.CHAR -> ObjCPrimitiveType("int8_t")
|
|
||||||
ObjCValueType.SHORT -> ObjCPrimitiveType("int16_t")
|
|
||||||
ObjCValueType.INT -> ObjCPrimitiveType("int32_t")
|
|
||||||
ObjCValueType.LONG_LONG -> ObjCPrimitiveType("int64_t")
|
|
||||||
ObjCValueType.UNSIGNED_CHAR -> ObjCPrimitiveType("uint8_t")
|
|
||||||
ObjCValueType.UNSIGNED_SHORT -> ObjCPrimitiveType("uint16_t")
|
|
||||||
ObjCValueType.UNSIGNED_INT -> ObjCPrimitiveType("uint32_t")
|
|
||||||
ObjCValueType.UNSIGNED_LONG_LONG -> ObjCPrimitiveType("uint64_t")
|
|
||||||
ObjCValueType.FLOAT -> ObjCPrimitiveType("float")
|
|
||||||
ObjCValueType.DOUBLE -> ObjCPrimitiveType("double")
|
|
||||||
ObjCValueType.POINTER -> ObjCPointerType(ObjCVoidType)
|
|
||||||
}
|
}
|
||||||
// TODO: consider other namings.
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateFile(sourceFile: SourceFile, declarations: List<CallableMemberDescriptor>) {
|
||||||
|
stubs.add(translator.translateFile(sourceFile, declarations))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateExtensions(classDescriptor: ClassDescriptor, declarations: List<CallableMemberDescriptor>) {
|
||||||
|
stubs.add(translator.translateExtensions(classDescriptor, declarations))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun generateClass(descriptor: ClassDescriptor) {
|
||||||
|
if (!generatedClasses.add(descriptor)) return
|
||||||
|
stubs.add(translator.translateClass(descriptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun generateInterface(descriptor: ClassDescriptor) {
|
||||||
|
if (!generatedClasses.add(descriptor)) return
|
||||||
|
stubs.add(translator.translateInterface(descriptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun requireClassOrInterface(descriptor: ClassDescriptor) {
|
||||||
|
if (descriptor !in generatedClasses) {
|
||||||
|
extraClassesToTranslate += descriptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun referenceClass(objCName: String, descriptor: ClassDescriptor? = null) {
|
||||||
|
if (descriptor !in generatedClasses) classForwardDeclarations += objCName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun referenceProtocol(objCName: String, descriptor: ClassDescriptor? = null) {
|
||||||
|
if (descriptor !in generatedClasses) protocolForwardDeclarations += objCName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun objCInterface(
|
||||||
|
name: ObjCExportNamer.ClassOrProtocolName,
|
||||||
|
generics: List<String> = emptyList(),
|
||||||
|
descriptor: ClassDescriptor? = null,
|
||||||
|
superClass: String? = null,
|
||||||
|
superProtocols: List<String> = emptyList(),
|
||||||
|
members: List<Stub<*>> = emptyList(),
|
||||||
|
attributes: List<String> = emptyList()
|
||||||
|
): ObjCInterface = ObjCInterface(
|
||||||
|
name.objCName,
|
||||||
|
generics,
|
||||||
|
descriptor,
|
||||||
|
superClass,
|
||||||
|
superProtocols,
|
||||||
|
null,
|
||||||
|
members,
|
||||||
|
attributes + name.toNameAttributes()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun objCProtocol(
|
||||||
|
name: ObjCExportNamer.ClassOrProtocolName,
|
||||||
|
descriptor: ClassDescriptor,
|
||||||
|
superProtocols: List<String>,
|
||||||
|
members: List<Stub<*>>,
|
||||||
|
attributes: List<String> = emptyList()
|
||||||
|
): ObjCProtocol = ObjCProtocol(
|
||||||
|
name.objCName,
|
||||||
|
descriptor,
|
||||||
|
superProtocols,
|
||||||
|
members,
|
||||||
|
attributes + name.toNameAttributes()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
|
||||||
|
binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) },
|
||||||
|
swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun swiftNameAttribute(swiftName: String) = "swift_name(\"$swiftName\")"
|
||||||
|
private fun objcRuntimeNameAttribute(name: String) = "objc_runtime_name(\"$name\")"
|
||||||
|
|||||||
+7
-6
@@ -6,16 +6,17 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.getExportedDependencies
|
|
||||||
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.ir.util.report
|
import org.jetbrains.kotlin.ir.util.report
|
||||||
|
|
||||||
internal class ObjCExportHeaderGeneratorImpl(val context: Context) : ObjCExportHeaderGenerator(
|
internal class ObjCExportHeaderGeneratorImpl(
|
||||||
context.moduleDescriptor,
|
val context: Context,
|
||||||
context.getExportedDependencies(),
|
moduleDescriptors: List<ModuleDescriptor>,
|
||||||
context.builtIns
|
mapper: ObjCExportMapper,
|
||||||
) {
|
namer: ObjCExportNamer
|
||||||
|
) : ObjCExportHeaderGenerator(moduleDescriptors, context.builtIns, mapper, namer) {
|
||||||
|
|
||||||
override fun reportWarning(text: String) {
|
override fun reportWarning(text: String) {
|
||||||
context.reportCompilationWarning(text)
|
context.reportCompilationWarning(text)
|
||||||
|
|||||||
+13
-3
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
|
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
@@ -21,10 +22,19 @@ import org.jetbrains.kotlin.types.TypeUtils
|
|||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
|
||||||
internal abstract class ObjCExportMapper {
|
internal class ObjCExportMapper {
|
||||||
abstract fun getCategoryMembersFor(descriptor: ClassDescriptor): List<CallableMemberDescriptor>
|
companion object {
|
||||||
val maxFunctionTypeParameterCount get() = KONAN_FUNCTION_INTERFACES_MAX_PARAMETERS
|
val maxFunctionTypeParameterCount get() = KONAN_FUNCTION_INTERFACES_MAX_PARAMETERS
|
||||||
abstract fun isSpecialMapped(descriptor: ClassDescriptor): Boolean
|
}
|
||||||
|
|
||||||
|
val customTypeMappers: Map<ClassId, CustomTypeMapper> get() = CustomTypeMappers.byClassId
|
||||||
|
val hiddenTypes: Set<ClassId> get() = CustomTypeMappers.hiddenTypes
|
||||||
|
|
||||||
|
fun isSpecialMapped(descriptor: ClassDescriptor): Boolean {
|
||||||
|
// TODO: this method duplicates some of the [ObjCExportTranslatorImpl.mapReferenceType] logic.
|
||||||
|
return KotlinBuiltIns.isAny(descriptor) ||
|
||||||
|
descriptor.getAllSuperClassifiers().any { it.classId in customTypeMappers }
|
||||||
|
}
|
||||||
|
|
||||||
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
|
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
|
||||||
|
|
||||||
|
|||||||
+41
-30
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
@@ -14,11 +15,11 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|||||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
|
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
|
||||||
|
|
||||||
|
|
||||||
interface ObjCExportNamer {
|
interface ObjCExportNamer {
|
||||||
data class ClassOrProtocolName(val swiftName: String, val objCName: String, val binaryName: String = objCName)
|
data class ClassOrProtocolName(val swiftName: String, val objCName: String, val binaryName: String = objCName)
|
||||||
|
|
||||||
@@ -29,32 +30,37 @@ interface ObjCExportNamer {
|
|||||||
fun getPropertyName(property: PropertyDescriptor): String
|
fun getPropertyName(property: PropertyDescriptor): String
|
||||||
fun getObjectInstanceSelector(descriptor: ClassDescriptor): String
|
fun getObjectInstanceSelector(descriptor: ClassDescriptor): String
|
||||||
fun getEnumEntrySelector(descriptor: ClassDescriptor): String
|
fun getEnumEntrySelector(descriptor: ClassDescriptor): String
|
||||||
|
|
||||||
|
fun numberBoxName(classId: ClassId): ClassOrProtocolName
|
||||||
|
|
||||||
|
val kotlinAnyName: ClassOrProtocolName
|
||||||
|
val mutableSetName: ClassOrProtocolName
|
||||||
|
val mutableMapName: ClassOrProtocolName
|
||||||
|
val kotlinNumberName: ClassOrProtocolName
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNamer(moduleDescriptor: ModuleDescriptor,
|
fun createNamer(moduleDescriptor: ModuleDescriptor,
|
||||||
topLevelNamePrefix: String = moduleDescriptor.namePrefix): ObjCExportNamer =
|
topLevelNamePrefix: String = moduleDescriptor.namePrefix): ObjCExportNamer =
|
||||||
createNamer(moduleDescriptor, emptyList(), topLevelNamePrefix)
|
createNamer(moduleDescriptor, emptyList(), topLevelNamePrefix)
|
||||||
|
|
||||||
fun createNamer(moduleDescriptor: ModuleDescriptor,
|
fun createNamer(
|
||||||
|
moduleDescriptor: ModuleDescriptor,
|
||||||
exportedDependencies: List<ModuleDescriptor>,
|
exportedDependencies: List<ModuleDescriptor>,
|
||||||
topLevelNamePrefix: String = moduleDescriptor.namePrefix): ObjCExportNamer {
|
topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
||||||
val generator = object : ObjCExportHeaderGenerator(
|
): ObjCExportNamer = ObjCExportNamerImpl(
|
||||||
moduleDescriptor,
|
(exportedDependencies + moduleDescriptor).toSet(),
|
||||||
exportedDependencies,
|
|
||||||
moduleDescriptor.builtIns,
|
moduleDescriptor.builtIns,
|
||||||
topLevelNamePrefix
|
ObjCExportMapper(),
|
||||||
) {
|
topLevelNamePrefix,
|
||||||
override fun reportWarning(text: String) {}
|
local = true
|
||||||
override fun reportWarning(method: FunctionDescriptor, text: String) {}
|
)
|
||||||
}
|
|
||||||
return generator.namer
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class ObjCExportNamerImpl(
|
internal class ObjCExportNamerImpl(
|
||||||
val moduleDescriptors: Set<ModuleDescriptor>,
|
private val moduleDescriptors: Set<ModuleDescriptor>,
|
||||||
builtIns: KotlinBuiltIns,
|
builtIns: KotlinBuiltIns,
|
||||||
val mapper: ObjCExportMapper,
|
private val mapper: ObjCExportMapper,
|
||||||
private val topLevelNamePrefix: String
|
private val topLevelNamePrefix: String,
|
||||||
|
private val local: Boolean
|
||||||
) : ObjCExportNamer {
|
) : ObjCExportNamer {
|
||||||
|
|
||||||
private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName =
|
private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName =
|
||||||
@@ -66,15 +72,15 @@ internal class ObjCExportNamerImpl(
|
|||||||
binaryName = "Kotlin$this"
|
binaryName = "Kotlin$this"
|
||||||
)
|
)
|
||||||
|
|
||||||
val kotlinAnyName = "KotlinBase".toUnmangledClassOrProtocolName()
|
override val kotlinAnyName = "KotlinBase".toUnmangledClassOrProtocolName()
|
||||||
|
|
||||||
val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName()
|
override val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName()
|
||||||
val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName()
|
override val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName()
|
||||||
|
|
||||||
fun numberBoxName(classId: ClassId): ObjCExportNamer.ClassOrProtocolName =
|
override fun numberBoxName(classId: ClassId): ObjCExportNamer.ClassOrProtocolName =
|
||||||
classId.shortClassName.asString().toSpecialStandardClassOrProtocolName()
|
classId.shortClassName.asString().toSpecialStandardClassOrProtocolName()
|
||||||
|
|
||||||
val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName()
|
override val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName()
|
||||||
|
|
||||||
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
|
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
|
||||||
|
|
||||||
@@ -204,8 +210,10 @@ internal class ObjCExportNamerImpl(
|
|||||||
} else {
|
} else {
|
||||||
append(descriptor.name.asString().capitalize())
|
append(descriptor.name.asString().capitalize())
|
||||||
}
|
}
|
||||||
} else {
|
} else if (containingDeclaration is PackageFragmentDescriptor) {
|
||||||
appendTopLevelClassBaseName(descriptor)
|
appendTopLevelClassBaseName(descriptor)
|
||||||
|
} else {
|
||||||
|
error("unexpected class parent: $containingDeclaration")
|
||||||
}
|
}
|
||||||
}.mangledBySuffixUnderscores()
|
}.mangledBySuffixUnderscores()
|
||||||
}
|
}
|
||||||
@@ -219,8 +227,10 @@ internal class ObjCExportNamerImpl(
|
|||||||
append(getClassOrProtocolObjCName(containingDeclaration))
|
append(getClassOrProtocolObjCName(containingDeclaration))
|
||||||
.append(descriptor.name.asString().capitalize())
|
.append(descriptor.name.asString().capitalize())
|
||||||
|
|
||||||
} else {
|
} else if (containingDeclaration is PackageFragmentDescriptor) {
|
||||||
append(topLevelNamePrefix).appendTopLevelClassBaseName(descriptor)
|
append(topLevelNamePrefix).appendTopLevelClassBaseName(descriptor)
|
||||||
|
} else {
|
||||||
|
error("unexpected class parent: $containingDeclaration")
|
||||||
}
|
}
|
||||||
}.mangledBySuffixUnderscores()
|
}.mangledBySuffixUnderscores()
|
||||||
}
|
}
|
||||||
@@ -430,21 +440,22 @@ internal class ObjCExportNamerImpl(
|
|||||||
error("name candidates run out")
|
error("name candidates run out")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIfAssigned(element: T): N? = elementToName[element]
|
private fun getIfAssigned(element: T): N? = elementToName[element]
|
||||||
|
|
||||||
fun tryAssign(element: T, name: N): Boolean {
|
private fun tryAssign(element: T, name: N): Boolean {
|
||||||
if (element in elementToName) error(element)
|
if (element in elementToName) error(element)
|
||||||
|
|
||||||
if (reserved(name)) return false
|
if (reserved(name)) return false
|
||||||
|
|
||||||
val elements = nameToElements.getOrPut(name) { mutableListOf() }
|
if (nameToElements[name].orEmpty().any { conflict(element, it) }) {
|
||||||
if (elements.any { conflict(element, it) }) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
elements += element
|
if (!local) {
|
||||||
|
nameToElements.getOrPut(name) { mutableListOf() } += element
|
||||||
|
|
||||||
elementToName[element] = name
|
elementToName[element] = name
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user