From fe7685eb03846a30037490a8516e62fd0b1c6965 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 14 Jan 2019 15:11:31 +0300 Subject: [PATCH] Don't rely on stdlib when generating framework header --- .../konan/objcexport/CustomTypeMapper.kt | 13 ++- .../objcexport/ObjCExportHeaderGenerator.kt | 101 +++++++++--------- .../konan/objcexport/ObjCExportNamer.kt | 5 +- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt index ccdba06fdd0..6695d1e4cab 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/CustomTypeMapper.kt @@ -9,15 +9,17 @@ import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType 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.TypeUtils internal interface CustomTypeMapper { - val mappedClassDescriptor: ClassDescriptor + val mappedClassId: ClassId fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType class Simple( - override val mappedClassDescriptor: ClassDescriptor, + override val mappedClassId: ClassId, private val objCClassName: String ) : CustomTypeMapper { @@ -27,9 +29,12 @@ internal interface CustomTypeMapper { class Collection( private val generator: ObjCExportHeaderGenerator, - override val mappedClassDescriptor: ClassDescriptor, + mappedClassDescriptor: ClassDescriptor, private val objCClassName: String ) : CustomTypeMapper { + + override val mappedClassId = mappedClassDescriptor.classId!! + override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType { val typeArguments = mappedSuperType.arguments.map { val argument = it.type @@ -49,7 +54,7 @@ internal interface CustomTypeMapper { private val generator: ObjCExportHeaderGenerator, parameterCount: Int ) : CustomTypeMapper { - override val mappedClassDescriptor = generator.builtIns.getFunction(parameterCount) + override val mappedClassId: ClassId = generator.builtIns.getFunction(parameterCount).classId!! override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType { val functionType = mappedSuperType diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 241266bce2f..85c72ae5653 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -8,11 +8,10 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.KClassValue @@ -50,7 +49,7 @@ abstract class ObjCExportHeaderGenerator( override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean { // TODO: this method duplicates some of the [mapReferenceType] logic. 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() internal val topLevel = mutableMapOf>() - private val stdlibModule = moduleDescriptors.first().allDependencyModules.single { it.isKonanStdlib() } - - private val mappedToNSNumber: List = with(builtIns) { - val result = mutableListOf(boolean, byte, short, int, long, float, double) - - UnsignedType.values().mapTo(result) { unsignedType -> - stdlibModule.findClassAcrossModuleDependencies(unsignedType.classId)!! - } - - result - } - - private val customTypeMappers: Map = with(builtIns) { + /** + * Custom type mappers. + * + * Don't forget to update [hiddenTypes] after adding new one. + */ + private val customTypeMappers: Map = with(builtIns) { val result = mutableListOf() val generator = this@ObjCExportHeaderGenerator @@ -85,32 +77,39 @@ abstract class ObjCExportHeaderGenerator( NSNumberKind.values().forEach { // TODO: NSNumber seem to have different equality semantics. - if (it.mappedKotlinClassId != null) { - val descriptor = stdlibModule.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!! - result += CustomTypeMapper.Simple(descriptor, namer.numberBoxName(descriptor).objCName) + val classId = it.mappedKotlinClassId + if (classId != null) { + result += CustomTypeMapper.Simple(classId, namer.numberBoxName(classId).objCName) } } - result += CustomTypeMapper.Simple(string, "NSString") + result += CustomTypeMapper.Simple(string.classId!!, "NSString") (0..mapper.maxFunctionTypeParameterCount).forEach { result += CustomTypeMapper.Function(generator, it) } - result.associateBy { it.mappedClassDescriptor } + result.associateBy { it.mappedClassId } } - private val hiddenTypes: Set = run { - val customMappedTypes = customTypeMappers.keys - - customMappedTypes - .asSequence() - .flatMap { it.getAllSuperClassifiers().asSequence() } - .map { it as ClassDescriptor } - .filter { !customMappedTypes.contains(it) } - .toSet() - } + /** + * 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 = 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 @@ -294,8 +293,7 @@ abstract class ObjCExportHeaderGenerator( } private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface { - val descriptor = stdlibModule.findClassAcrossModuleDependencies(kotlinClassId)!! - val name = namer.numberBoxName(descriptor) + val name = namer.numberBoxName(kotlinClassId) val members = buildMembers { +nsNumberFactory(kind) @@ -834,26 +832,25 @@ abstract class ObjCExportHeaderGenerator( } internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType { - val typeToMapper = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type -> - val mapper = customTypeMappers[type.constructor.declarationDescriptor] - if (mapper != null) { - type to mapper - } else { - null - } - }.toMap() + class TypeMappingMatch(val type: KotlinType, val descriptor: ClassDescriptor, val mapper: CustomTypeMapper) - val mostSpecificTypeToMapper = typeToMapper.filter { (_, mapper) -> - typeToMapper.values.all { - it.mappedClassDescriptor == mapper.mappedClassDescriptor || - !it.mappedClassDescriptor.isSubclassOf(mapper.mappedClassDescriptor) + val typeMappingMatches = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type -> + (type.constructor.declarationDescriptor as? ClassDescriptor)?.let { descriptor -> + customTypeMappers[descriptor.classId]?.let { mapper -> + TypeMappingMatch(type, descriptor, mapper) + } } - - // E.g. if both List and MutableList are present, then retain only MutableList. } - if (mostSpecificTypeToMapper.size > 1) { - val types = mostSpecificTypeToMapper.keys.toList() + 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] @@ -863,8 +860,8 @@ abstract class ObjCExportHeaderGenerator( // TODO: the same warning for such classes. } - mostSpecificTypeToMapper.entries.firstOrNull()?.let { (type, mapper) -> - return mapper.mapType(type) + mostSpecificMatches.firstOrNull()?.let { + return it.mapper.mapType(it.type) } val classDescriptor = kotlinType.getErasedTypeClass() @@ -872,7 +869,7 @@ abstract class ObjCExportHeaderGenerator( // TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* ` // 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 } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt index bb74a22bbbd..3afaa340e52 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.descriptorUtil.* @@ -70,8 +71,8 @@ internal class ObjCExportNamerImpl( val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName() val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName() - fun numberBoxName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName = - descriptor.name.asString().toSpecialStandardClassOrProtocolName() + fun numberBoxName(classId: ClassId): ObjCExportNamer.ClassOrProtocolName = + classId.shortClassName.asString().toSpecialStandardClassOrProtocolName() val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName()