Don't rely on stdlib when generating framework header

This commit is contained in:
Svyatoslav Scherbina
2019-01-14 15:11:31 +03:00
committed by SvyatoslavScherbina
parent 3632ed495d
commit fe7685eb03
3 changed files with 61 additions and 58 deletions
@@ -9,15 +9,17 @@ 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.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
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 mappedClassDescriptor: ClassDescriptor val mappedClassId: ClassId
fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType
class Simple( class Simple(
override val mappedClassDescriptor: ClassDescriptor, override val mappedClassId: ClassId,
private val objCClassName: String private val objCClassName: String
) : CustomTypeMapper { ) : CustomTypeMapper {
@@ -27,9 +29,12 @@ internal interface CustomTypeMapper {
class Collection( class Collection(
private val generator: ObjCExportHeaderGenerator, private val generator: ObjCExportHeaderGenerator,
override val mappedClassDescriptor: ClassDescriptor, mappedClassDescriptor: ClassDescriptor,
private val objCClassName: String private val objCClassName: String
) : CustomTypeMapper { ) : CustomTypeMapper {
override val mappedClassId = mappedClassDescriptor.classId!!
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType { override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
val typeArguments = mappedSuperType.arguments.map { val typeArguments = mappedSuperType.arguments.map {
val argument = it.type val argument = it.type
@@ -49,7 +54,7 @@ internal interface CustomTypeMapper {
private val generator: ObjCExportHeaderGenerator, private val generator: ObjCExportHeaderGenerator,
parameterCount: Int parameterCount: Int
) : CustomTypeMapper { ) : CustomTypeMapper {
override val mappedClassDescriptor = generator.builtIns.getFunction(parameterCount) override val mappedClassId: ClassId = generator.builtIns.getFunction(parameterCount).classId!!
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType { override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
val functionType = mappedSuperType val functionType = mappedSuperType
@@ -8,11 +8,10 @@ package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib
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
@@ -50,7 +49,7 @@ abstract class ObjCExportHeaderGenerator(
override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean { override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean {
// TODO: this method duplicates some of the [mapReferenceType] logic. // TODO: this method duplicates some of the [mapReferenceType] logic.
return descriptor == builtIns.any || return descriptor == builtIns.any ||
descriptor.getAllSuperClassifiers().any { it in customTypeMappers } descriptor.getAllSuperClassifiers().any { it.classId in customTypeMappers }
} }
} }
@@ -59,19 +58,12 @@ abstract class ObjCExportHeaderGenerator(
internal val generatedClasses = mutableSetOf<ClassDescriptor>() internal val generatedClasses = mutableSetOf<ClassDescriptor>()
internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>() internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
private val stdlibModule = moduleDescriptors.first().allDependencyModules.single { it.isKonanStdlib() } /**
* Custom type mappers.
private val mappedToNSNumber: List<ClassDescriptor> = with(builtIns) { *
val result = mutableListOf(boolean, byte, short, int, long, float, double) * Don't forget to update [hiddenTypes] after adding new one.
*/
UnsignedType.values().mapTo(result) { unsignedType -> private val customTypeMappers: Map<ClassId, CustomTypeMapper> = with(builtIns) {
stdlibModule.findClassAcrossModuleDependencies(unsignedType.classId)!!
}
result
}
private val customTypeMappers: Map<ClassDescriptor, CustomTypeMapper> = with(builtIns) {
val result = mutableListOf<CustomTypeMapper>() val result = mutableListOf<CustomTypeMapper>()
val generator = this@ObjCExportHeaderGenerator val generator = this@ObjCExportHeaderGenerator
@@ -85,32 +77,39 @@ abstract class ObjCExportHeaderGenerator(
NSNumberKind.values().forEach { NSNumberKind.values().forEach {
// TODO: NSNumber seem to have different equality semantics. // TODO: NSNumber seem to have different equality semantics.
if (it.mappedKotlinClassId != null) { val classId = it.mappedKotlinClassId
val descriptor = stdlibModule.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!! if (classId != null) {
result += CustomTypeMapper.Simple(descriptor, namer.numberBoxName(descriptor).objCName) result += CustomTypeMapper.Simple(classId, namer.numberBoxName(classId).objCName)
} }
} }
result += CustomTypeMapper.Simple(string, "NSString") result += CustomTypeMapper.Simple(string.classId!!, "NSString")
(0..mapper.maxFunctionTypeParameterCount).forEach { (0..mapper.maxFunctionTypeParameterCount).forEach {
result += CustomTypeMapper.Function(generator, it) result += CustomTypeMapper.Function(generator, it)
} }
result.associateBy { it.mappedClassDescriptor } result.associateBy { it.mappedClassId }
} }
private val hiddenTypes: Set<ClassDescriptor> = run { /**
val customMappedTypes = customTypeMappers.keys * Types to be "hidden" during mapping, i.e. represented as `id`.
*
customMappedTypes * Currently contains super types of classes handled by [customTypeMappers].
.asSequence() * Note: can be generated programmatically, but requires stdlib in this case.
.flatMap { it.getAllSuperClassifiers().asSequence() } */
.map { it as ClassDescriptor } private val hiddenTypes: Set<ClassId> = listOf(
.filter { !customMappedTypes.contains(it) } "kotlin.Any",
.toSet() "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
@@ -294,8 +293,7 @@ abstract class ObjCExportHeaderGenerator(
} }
private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface { private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface {
val descriptor = stdlibModule.findClassAcrossModuleDependencies(kotlinClassId)!! val name = namer.numberBoxName(kotlinClassId)
val name = namer.numberBoxName(descriptor)
val members = buildMembers { val members = buildMembers {
+nsNumberFactory(kind) +nsNumberFactory(kind)
@@ -834,26 +832,25 @@ abstract class ObjCExportHeaderGenerator(
} }
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType { internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType {
val typeToMapper = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type -> class TypeMappingMatch(val type: KotlinType, val descriptor: ClassDescriptor, val mapper: CustomTypeMapper)
val mapper = customTypeMappers[type.constructor.declarationDescriptor]
if (mapper != null) {
type to mapper
} else {
null
}
}.toMap()
val mostSpecificTypeToMapper = typeToMapper.filter { (_, mapper) -> val typeMappingMatches = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type ->
typeToMapper.values.all { (type.constructor.declarationDescriptor as? ClassDescriptor)?.let { descriptor ->
it.mappedClassDescriptor == mapper.mappedClassDescriptor || customTypeMappers[descriptor.classId]?.let { mapper ->
!it.mappedClassDescriptor.isSubclassOf(mapper.mappedClassDescriptor) TypeMappingMatch(type, descriptor, mapper)
}
} }
// E.g. if both List and MutableList are present, then retain only MutableList.
} }
if (mostSpecificTypeToMapper.size > 1) { val mostSpecificMatches = typeMappingMatches.filter { match ->
val types = mostSpecificTypeToMapper.keys.toList() 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 firstType = types[0]
val secondType = types[1] val secondType = types[1]
@@ -863,8 +860,8 @@ abstract class ObjCExportHeaderGenerator(
// TODO: the same warning for such classes. // TODO: the same warning for such classes.
} }
mostSpecificTypeToMapper.entries.firstOrNull()?.let { (type, mapper) -> mostSpecificMatches.firstOrNull()?.let {
return mapper.mapType(type) return it.mapper.mapType(it.type)
} }
val classDescriptor = kotlinType.getErasedTypeClass() val classDescriptor = kotlinType.getErasedTypeClass()
@@ -872,7 +869,7 @@ abstract class ObjCExportHeaderGenerator(
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>` // TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
// TODO: expose custom inline class boxes properly. // TODO: expose custom inline class boxes properly.
if (classDescriptor == builtIns.any || classDescriptor in hiddenTypes || classDescriptor.isInlined()) { if (classDescriptor == builtIns.any || classDescriptor.classId in hiddenTypes || classDescriptor.isInlined()) {
return ObjCIdType return ObjCIdType
} }
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib
import org.jetbrains.kotlin.incremental.components.NoLookupLocation 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.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.descriptorUtil.*
@@ -70,8 +71,8 @@ internal class ObjCExportNamerImpl(
val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName() val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName()
val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName() val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName()
fun numberBoxName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName = fun numberBoxName(classId: ClassId): ObjCExportNamer.ClassOrProtocolName =
descriptor.name.asString().toSpecialStandardClassOrProtocolName() classId.shortClassName.asString().toSpecialStandardClassOrProtocolName()
val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName() val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName()