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 0f9adb5020f..8733072dcca 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 @@ -7,11 +7,14 @@ 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.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isAny +import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.* @@ -19,12 +22,15 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.types.typeUtil.isInterface import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.utils.addIfNotNull interface ObjCExportTranslator { + fun generateBaseDeclarations(): List> + fun getClassIfExtension(receiverType: KotlinType): ClassDescriptor? fun translateFile(file: SourceFile, declarations: List): ObjCInterface fun translateClass(descriptor: ClassDescriptor): ObjCInterface fun translateInterface(descriptor: ClassDescriptor): ObjCProtocol @@ -43,7 +49,6 @@ interface ObjCExportWarningCollector { internal class ObjCExportTranslatorImpl( private val generator: ObjCExportHeaderGenerator?, - val builtIns: KotlinBuiltIns, val mapper: ObjCExportMapper, val namer: ObjCExportNamer, val warningCollector: ObjCExportWarningCollector, @@ -52,13 +57,129 @@ internal class ObjCExportTranslatorImpl( private val kotlinAnyName = namer.kotlinAnyName - internal fun translateKotlinObjCClassAsUnavailableStub(descriptor: ClassDescriptor): ObjCInterface = objCInterface( + override fun generateBaseDeclarations(): List> { + val stubs = mutableListOf>() + + 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(ObjCInterfaceImpl( + 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" + )) + + // TODO: only if appears + stubs.add(objCInterface( + namer.mutableMapName, + generics = listOf("KeyType", "ObjectType"), + superClass = "NSMutableDictionary" + )) + + stubs.add(ObjCInterfaceImpl("NSError", categoryName = "NSErrorKotlinException", members = buildMembers { + +ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly")) + })) + + genKotlinNumbers(stubs) + + return stubs + } + + private fun genKotlinNumbers(stubs: MutableList>) { + 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 = emptyList()): ObjCMethod { + return ObjCMethod( + null, + false, + ObjCInstanceType, + listOf(kind.factorySelector), + listOf(ObjCParameter("value", null, kind.objCType)), + attributes + ) + } + + private fun nsNumberFactory(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { + return ObjCMethod( + null, + true, + ObjCInstanceType, + listOf(kind.initSelector), + listOf(ObjCParameter("value", null, kind.objCType)), + attributes + ) + } + + override fun getClassIfExtension(receiverType: KotlinType): ClassDescriptor? = + mapper.getClassIfCategory(receiverType) + + internal fun translateUnexposedClassAsUnavailableStub(descriptor: ClassDescriptor): ObjCInterface = objCInterface( namer.getClassOrProtocolName(descriptor), descriptor = descriptor, superClass = "NSObject", - attributes = listOf("unavailable(\"Kotlin subclass of Objective-C class can't be imported\")") + attributes = attributesForUnexposed(descriptor) ) + internal fun translateUnexposedInterfaceAsUnavailableStub(descriptor: ClassDescriptor): ObjCProtocol = objCProtocol( + namer.getClassOrProtocolName(descriptor), + descriptor = descriptor, + superProtocols = emptyList(), + members = emptyList(), + attributes = attributesForUnexposed(descriptor) + ) + + private fun attributesForUnexposed(descriptor: ClassDescriptor): List { + val message = when { + descriptor.isKotlinObjCClass() -> "Kotlin subclass of Objective-C class " + else -> "" + } + "can't be imported" + return listOf("unavailable(\"$message\")") + } + private fun genericExportScope(classDescriptor: DeclarationDescriptor): ObjCExportScope { return if(objcGenerics && classDescriptor is ClassDescriptor && !classDescriptor.isInterface) { ObjCClassExportScope(classDescriptor, namer) @@ -105,6 +226,9 @@ internal class ObjCExportTranslatorImpl( } override fun translateInterface(descriptor: ClassDescriptor): ObjCProtocol { + require(mapper.shouldBeExposed(descriptor)) + require(descriptor.isInterface) + val name = translateClassOrInterfaceName(descriptor) val members: List> = buildMembers { translateInterfaceMembers(descriptor) } val superProtocols: List = descriptor.superProtocols @@ -133,7 +257,7 @@ internal class ObjCExportTranslatorImpl( val members = buildMembers { translatePlainMembers(declarations, ObjCNoneExportScope) } - return ObjCInterface(name, categoryName = "Extensions", members = members) + return ObjCInterfaceImpl(name, categoryName = "Extensions", members = members) } override fun translateFile(file: SourceFile, declarations: List): ObjCInterface { @@ -147,11 +271,13 @@ internal class ObjCExportTranslatorImpl( name, superClass = namer.kotlinAnyName.objCName, members = members, - attributes = listOf("objc_subclassing_restricted") + attributes = listOf(OBJC_SUBCLASSING_RESTRICTED) ) } override fun translateClass(descriptor: ClassDescriptor): ObjCInterface { + require(mapper.shouldBeExposed(descriptor)) + require(!descriptor.isInterface) val genericExportScope = genericExportScope(descriptor) @@ -187,6 +313,7 @@ internal class ObjCExportTranslatorImpl( if (!descriptor.isArray) presentConstructors += selector +buildMethod(it, it, genericExportScope) + exportThrown(it) if (selector == "init") { +ObjCMethod(it, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("availability(swift, unavailable, message=\"use object initializers instead\")")) @@ -244,7 +371,7 @@ internal class ObjCExportTranslatorImpl( translateClassMembers(descriptor) } - val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList() + val attributes = if (descriptor.isFinalOrEnum) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList() val name = translateClassOrInterfaceName(descriptor) @@ -334,6 +461,8 @@ internal class ObjCExportTranslatorImpl( members.toObjCMembers(methods, properties) + methods.forEach { exportThrown(it) } + collectMethodsOrProperties(methods) { it -> buildAsDeclaredOrInheritedMethods(it.original) } collectMethodsOrProperties(properties) { it -> buildAsDeclaredOrInheritedProperties(it.original) } } @@ -346,6 +475,8 @@ internal class ObjCExportTranslatorImpl( members.toObjCMembers(methods, properties) + methods.forEach { exportThrown(it) } + methods.retainAll { mapper.isBaseMethod(it) } properties.retainAll { @@ -366,6 +497,8 @@ internal class ObjCExportTranslatorImpl( members.toObjCMembers(methods, properties) + methods.forEach { exportThrown(it) } + translatePlainMembers(methods, properties, objCExportScope) } @@ -398,32 +531,29 @@ internal class ObjCExportTranslatorImpl( } } - private val methodToSignatures = mutableMapOf>>() - private val propertyToSignatures = mutableMapOf>>() - private fun buildAsDeclaredOrInheritedMethods( method: FunctionDescriptor - ): Set> = methodToSignatures.getOrPut(method) { + ): Set> { val isInterface = (method.containingDeclaration as ClassDescriptor).isInterface - mapper.getBaseMethods(method) + return mapper.getBaseMethods(method) .asSequence() .distinctBy { namer.getSelector(it) } .map { base -> buildMethod((if (isInterface) base else method), base, genericExportScope(method.containingDeclaration)) } - .map { method -> RenderedStub(method) } + .map { RenderedStub(it) } .toSet() } private fun buildAsDeclaredOrInheritedProperties( property: PropertyDescriptor - ): Set> = propertyToSignatures.getOrPut(property) { + ): Set> { val isInterface = (property.containingDeclaration as ClassDescriptor).isInterface - mapper.getBaseProperties(property) + return mapper.getBaseProperties(property) .asSequence() .distinctBy { namer.getPropertyName(it) } .map { base -> buildProperty((if (isInterface) base else property), base, genericExportScope(property.containingDeclaration)) } - .map { property -> RenderedStub(property) } + .map { RenderedStub(it) } .toSet() } @@ -514,8 +644,6 @@ internal class ObjCExportTranslatorImpl( val baseMethodBridge = mapper.bridgeMethod(baseMethod) - exportThrownFromThisAndOverridden(method) - val isInstanceMethod: Boolean = baseMethodBridge.isInstance val returnType: ObjCType = mapReturnType(baseMethodBridge.returnBridge, method, objCExportScope) val parameters = collectParameters(baseMethodBridge, method) @@ -541,12 +669,8 @@ internal class ObjCExportTranslatorImpl( } } - private val methodsWithThrowAnnotationConsidered = mutableSetOf() - - private val uncheckedExceptionClasses = listOf("Error", "RuntimeException").map { - builtIns.builtInsPackageScope - .getContributedClassifier(Name.identifier(it), NoLookupLocation.FROM_BACKEND) as ClassDescriptor - } + private val uncheckedExceptionClasses = + listOf("kotlin.Error", "kotlin.RuntimeException").map { ClassId.topLevel(FqName(it)) } private fun exportThrown(method: FunctionDescriptor) { if (!method.kind.isReal) return @@ -557,14 +681,11 @@ internal class ObjCExportTranslatorImpl( "@${KonanFqNames.throws.shortName()} annotation should also be added to a base method") } - if (method in methodsWithThrowAnnotationConsidered) return - methodsWithThrowAnnotationConsidered += method - val arguments = (throwsAnnotation.allValueArguments.values.single() as ArrayValue).value for (argument in arguments) { val classDescriptor = TypeUtils.getClassDescriptor((argument as KClassValue).getArgumentType(method.module)) ?: continue - uncheckedExceptionClasses.firstOrNull { classDescriptor.isSubclassOf(it) }?.let { + classDescriptor.getAllSuperClassifiers().firstOrNull { it.classId in uncheckedExceptionClasses }?.let { warningCollector.reportWarning(method, "Method is declared to throw ${classDescriptor.fqNameSafe}, " + "but instances of ${it.fqNameSafe} and its subclasses aren't propagated " + @@ -575,10 +696,6 @@ internal class ObjCExportTranslatorImpl( } } - private fun exportThrownFromThisAndOverridden(method: FunctionDescriptor) { - method.allOverriddenDescriptors.forEach { exportThrown(it) } - } - private fun mapReturnType(returnBridge: MethodBridge.ReturnValue, method: FunctionDescriptor, objCExportScope: ObjCExportScope): ObjCType = when (returnBridge) { MethodBridge.ReturnValue.Void -> ObjCVoidType MethodBridge.ReturnValue.HashCode -> ObjCPrimitiveType("NSUInteger") @@ -650,7 +767,7 @@ internal class ObjCExportTranslatorImpl( // TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* ` // TODO: expose custom inline class boxes properly. - if (classDescriptor == builtIns.any || classDescriptor.classId in mapper.hiddenTypes || classDescriptor.isInlined()) { + if (isAny(classDescriptor) || classDescriptor.classId in mapper.hiddenTypes || classDescriptor.isInlined()) { return ObjCIdType } @@ -719,7 +836,7 @@ internal class ObjCExportTranslatorImpl( objCExportScope: ObjCExportScope, typeBridge: BlockPointerBridge ): ObjCReferenceType { - val expectedDescriptor = builtIns.getFunction(typeBridge.numberOfParameters) + val expectedDescriptor = kotlinType.builtIns.getFunction(typeBridge.numberOfParameters) // Somewhat similar to mapType: val functionType = if (TypeUtils.getClassDescriptor(kotlinType) == expectedDescriptor) { @@ -759,7 +876,6 @@ internal class ObjCExportTranslatorImpl( abstract class ObjCExportHeaderGenerator internal constructor( val moduleDescriptors: List, - val builtIns: KotlinBuiltIns, internal val mapper: ObjCExportMapper, val namer: ObjCExportNamer, val objcGenerics:Boolean = false @@ -778,7 +894,6 @@ abstract class ObjCExportHeaderGenerator internal constructor( mapper: ObjCExportMapper ) : this( moduleDescriptors, - builtIns, mapper, ObjCExportNamerImpl(moduleDescriptors.toSet(), builtIns, mapper, topLevelNamePrefix, local = false) ) @@ -802,7 +917,7 @@ abstract class ObjCExportHeaderGenerator internal constructor( private val protocolForwardDeclarations = linkedSetOf() private val extraClassesToTranslate = mutableSetOf() - private val translator = ObjCExportTranslatorImpl(this, builtIns, mapper, namer, + private val translator = ObjCExportTranslatorImpl(this, mapper, namer, object : ObjCExportWarningCollector { override fun reportWarning(text: String) = this@ObjCExportHeaderGenerator.reportWarning(text) @@ -866,38 +981,7 @@ abstract class ObjCExportHeaderGenerator internal constructor( // TODO: make the translation order stable // 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" - )) - - // TODO: only if appears - stubs.add(objCInterface( - namer.mutableMapName, - generics = listOf("KeyType", "ObjectType"), - superClass = "NSMutableDictionary" - )) - - stubs.add(ObjCInterface("NSError", categoryName = "NSErrorKotlinException", members = buildMembers { - +ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly")) - })) - - genKotlinNumbers() + stubs += translator.generateBaseDeclarations() val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() } @@ -930,9 +1014,12 @@ abstract class ObjCExportHeaderGenerator internal constructor( } it.unsubstitutedMemberScope.translateClasses() - } else if (it.isKotlinObjCClass() && mapper.shouldBeVisible(it)) { - assert(!it.isInterface) - stubs += translator.translateKotlinObjCClassAsUnavailableStub(it) + } else if (mapper.shouldBeVisible(it)) { + stubs += if (it.isInterface) { + translator.translateUnexposedInterfaceAsUnavailableStub(it) + } else { + translator.translateUnexposedClassAsUnavailableStub(it) + } } } } @@ -962,64 +1049,6 @@ abstract class ObjCExportHeaderGenerator internal constructor( return stubs } - 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 = emptyList()): ObjCMethod { - return ObjCMethod( - null, - false, - ObjCInstanceType, - listOf(kind.factorySelector), - listOf(ObjCParameter("value", null, kind.objCType)), - attributes - ) - } - - private fun nsNumberFactory(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { - return ObjCMethod( - null, - true, - ObjCInstanceType, - listOf(kind.initSelector), - listOf(ObjCParameter("value", null, kind.objCType)), - attributes - ) - } - private fun generateFile(sourceFile: SourceFile, declarations: List) { stubs.add(translator.translateFile(sourceFile, declarations)) } @@ -1062,7 +1091,7 @@ private fun objCInterface( superProtocols: List = emptyList(), members: List> = emptyList(), attributes: List = emptyList() -): ObjCInterface = ObjCInterface( +): ObjCInterface = ObjCInterfaceImpl( name.objCName, generics, descriptor, @@ -1080,7 +1109,7 @@ private fun objCProtocol( superProtocols: List, members: List>, attributes: List = emptyList() -): ObjCProtocol = ObjCProtocol( +): ObjCProtocol = ObjCProtocolImpl( name.objCName, descriptor, superProtocols, @@ -1088,7 +1117,7 @@ private fun objCProtocol( attributes + name.toNameAttributes() ) -private fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List = listOfNotNull( +internal fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List = listOfNotNull( binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) }, swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) } ) @@ -1131,4 +1160,6 @@ internal fun Variance.objcDeclaration():String = when(this){ else -> "" } -private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? = descriptor.typeConstructor.supertypes.filter { !it.isInterface() }.firstOrNull() \ No newline at end of file +private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? = descriptor.typeConstructor.supertypes.filter { !it.isInterface() }.firstOrNull() + +internal const val OBJC_SUBCLASSING_RESTRICTED = "objc_subclassing_restricted" diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt index 2747b2aaff4..11b05b3f0ca 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGeneratorImpl.kt @@ -22,7 +22,7 @@ internal class ObjCExportHeaderGeneratorImpl( mapper: ObjCExportMapper, namer: ObjCExportNamer, objcGenerics: Boolean -) : ObjCExportHeaderGenerator(moduleDescriptors, context.builtIns, mapper, namer, objcGenerics) { +) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics) { override fun reportWarning(text: String) { context.reportCompilationWarning(text) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt index 40906fb3714..a9c11b4ebc4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt @@ -46,6 +46,10 @@ internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescr val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null + return getClassIfCategory(extensionReceiverType) +} + +internal fun ObjCExportMapper.getClassIfCategory(extensionReceiverType: KotlinType): ClassDescriptor? { // FIXME: this code must rely on type mapping instead of copying its logic. if (extensionReceiverType.isObjCObjectType()) return null @@ -63,13 +67,13 @@ internal fun ObjCExportMapper.shouldBeExposed(descriptor: CallableMemberDescript descriptor.isEffectivelyPublicApi && !descriptor.isSuspend && !descriptor.isExpect internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Boolean = - shouldBeVisible(descriptor) && !descriptor.defaultType.isObjCObjectType() + shouldBeVisible(descriptor) && !isSpecialMapped(descriptor) && !descriptor.defaultType.isObjCObjectType() internal fun ObjCExportMapper.shouldBeVisible(descriptor: ClassDescriptor): Boolean = descriptor.isEffectivelyPublicApi && when (descriptor.kind) { ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false - } && !descriptor.isExpect && !isSpecialMapped(descriptor) && !descriptor.isInlined() + } && !descriptor.isExpect && !descriptor.isInlined() private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean = descriptor.overriddenDescriptors.all { !shouldBeExposed(it) } 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 9e7c82bde09..a4a9705c6b4 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 @@ -23,6 +23,12 @@ import org.jetbrains.kotlin.resolve.source.PsiSourceFile interface ObjCExportNamer { data class ClassOrProtocolName(val swiftName: String, val objCName: String, val binaryName: String = objCName) + interface Configuration { + val topLevelNamePrefix: String + fun getAdditionalPrefix(module: ModuleDescriptor): String? + val objcGenerics: Boolean + } + fun getFileClassName(file: SourceFile): ClassOrProtocolName fun getClassOrProtocolName(descriptor: ClassDescriptor): ClassOrProtocolName fun getSelector(method: FunctionDescriptor): String @@ -57,16 +63,42 @@ fun createNamer( ) internal class ObjCExportNamerImpl( - private val moduleDescriptors: Set, + private val configuration: ObjCExportNamer.Configuration, builtIns: KotlinBuiltIns, private val mapper: ObjCExportMapper, - private val topLevelNamePrefix: String, - private val local: Boolean, - private val objcGenerics: Boolean = false + private val local: Boolean ) : ObjCExportNamer { + + constructor( + moduleDescriptors: Set, + builtIns: KotlinBuiltIns, + mapper: ObjCExportMapper, + topLevelNamePrefix: String, + local: Boolean, + objcGenerics: Boolean = false + ) : this( + object : ObjCExportNamer.Configuration { + override val topLevelNamePrefix: String + get() = topLevelNamePrefix + + override fun getAdditionalPrefix(module: ModuleDescriptor): String? = + if (module in moduleDescriptors) null else module.namePrefix + + override val objcGenerics: Boolean + get() = objcGenerics + + }, + builtIns, + mapper, + local + ) + private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName = ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = this) + private val objcGenerics get() = configuration.objcGenerics + private val topLevelNamePrefix get() = configuration.topLevelNamePrefix + private fun String.toSpecialStandardClassOrProtocolName() = ObjCExportNamer.ClassOrProtocolName( swiftName = "Kotlin$this", objCName = "${topLevelNamePrefix}$this", @@ -174,18 +206,11 @@ internal class ObjCExportNamerImpl( return ObjCExportNamer.ClassOrProtocolName(swiftName = swiftName, objCName = objCName) } - private val predefinedClassNames = mapOf( - builtIns.any to kotlinAnyName, - builtIns.mutableSet to mutableSetName, - builtIns.mutableMap to mutableMapName - ) - override fun getClassOrProtocolName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName = - predefinedClassNames[descriptor] - ?: ObjCExportNamer.ClassOrProtocolName( - swiftName = getClassOrProtocolSwiftName(descriptor), - objCName = getClassOrProtocolObjCName(descriptor) - ) + ObjCExportNamer.ClassOrProtocolName( + swiftName = getClassOrProtocolSwiftName(descriptor), + objCName = getClassOrProtocolObjCName(descriptor) + ) private fun getClassOrProtocolSwiftName( descriptor: ClassDescriptor @@ -260,8 +285,8 @@ internal class ObjCExportNamerImpl( } private fun StringBuilder.appendTopLevelClassBaseName(descriptor: ClassDescriptor) = apply { - if (descriptor.module !in moduleDescriptors) { - append(descriptor.module.namePrefix) + configuration.getAdditionalPrefix(descriptor.module)?.let { + append(it) } append(descriptor.name.asString()) } @@ -269,6 +294,8 @@ internal class ObjCExportNamerImpl( override fun getSelector(method: FunctionDescriptor): String = methodSelectors.getOrPut(method) { assert(mapper.isBaseMethod(method)) + getPredefined(method, Predefined.anyMethodSelectors)?.let { return it } + val parameters = mapper.bridgeMethod(method).valueParametersAssociated(method) StringBuilder().apply { @@ -317,6 +344,8 @@ internal class ObjCExportNamerImpl( override fun getSwiftName(method: FunctionDescriptor): String = methodSwiftNames.getOrPut(method) { assert(mapper.isBaseMethod(method)) + getPredefined(method, Predefined.anyMethodSwiftNames)?.let { return it } + val parameters = mapper.bridgeMethod(method).valueParametersAssociated(method) StringBuilder().apply { @@ -349,6 +378,14 @@ internal class ObjCExportNamerImpl( } } + private fun getPredefined(method: FunctionDescriptor, predefinedForAny: Map): T? { + return if (method.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) }) { + predefinedForAny.getValue(method.name) + } else { + null + } + } + override fun getPropertyName(property: PropertyDescriptor): String = propertyNames.getOrPut(property) { assert(mapper.isBaseProperty(property)) assert(mapper.isObjCProperty(property)) @@ -395,31 +432,52 @@ internal class ObjCExportNamerImpl( } init { + if (!local) { + forceAssignPredefined(builtIns) + } + } + + private fun forceAssignPredefined(builtIns: KotlinBuiltIns) { val any = builtIns.any + val predefinedClassNames = mapOf( + builtIns.any to kotlinAnyName, + builtIns.mutableSet to mutableSetName, + builtIns.mutableMap to mutableMapName + ) + predefinedClassNames.forEach { descriptor, name -> objCClassNames.forceAssign(descriptor, name.objCName) swiftClassAndProtocolNames.forceAssign(descriptor, name.swiftName) } - fun ClassDescriptor.method(name: String) = + fun ClassDescriptor.method(name: Name) = this.unsubstitutedMemberScope.getContributedFunctions( - Name.identifier(name), + name, NoLookupLocation.FROM_BACKEND ).single() - val hashCode = any.method("hashCode") - val toString = any.method("toString") - val equals = any.method("equals") + Predefined.anyMethodSelectors.forEach { name, selector -> + methodSelectors.forceAssign(any.method(name), selector) + } - methodSelectors.forceAssign(hashCode, "hash") - methodSwiftNames.forceAssign(hashCode, "hash()") + Predefined.anyMethodSwiftNames.forEach { name, swiftName -> + methodSwiftNames.forceAssign(any.method(name), swiftName) + } + } - methodSelectors.forceAssign(toString, "description") - methodSwiftNames.forceAssign(toString, "description()") + private object Predefined { + val anyMethodSelectors = mapOf( + "hashCode" to "hash", + "toString" to "description", + "equals" to "isEqual:" + ).mapKeys { Name.identifier(it.key) } - methodSelectors.forceAssign(equals, "isEqual:") - methodSwiftNames.forceAssign(equals, "isEqual(_:)") + val anyMethodSwiftNames = mapOf( + "hashCode" to "hash()", + "toString" to "description()", + "equals" to "isEqual(_:)" + ).mapKeys { Name.identifier(it.key) } } private fun FunctionDescriptor.getMangledName(forSwift: Boolean): String { @@ -519,7 +577,7 @@ internal class ObjCExportNamerImpl( abstract fun conflict(first: T, second: T): Boolean open fun reserved(name: N) = false - fun getOrPut(element: T, nameCandidates: () -> Sequence): N { + inline fun getOrPut(element: T, nameCandidates: () -> Sequence): N { getIfAssigned(element)?.let { return it } nameCandidates().forEach { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/stubs.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/stubs.kt index f99ec1aba88..761bbdfbae0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/stubs.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/stubs.kt @@ -10,48 +10,66 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -open class Stub(val name: String, val descriptor: D?) +abstract class Stub(val name: String) { + abstract val descriptor: D? +} + +abstract class ObjCTopLevel(name: String) : Stub(name) abstract class ObjCClass(name: String, - descriptor: D?, - val superProtocols: List, - val members: List>, - val attributes: List) : Stub(name, descriptor) + val attributes: List) : ObjCTopLevel(name) { + abstract val superProtocols: List + abstract val members: List> +} -class ObjCProtocol(name: String, - descriptor: ClassDescriptor, - superProtocols: List, - members: List>, - attributes: List = emptyList()) : ObjCClass(name, descriptor, superProtocols, members, attributes) +abstract class ObjCProtocol(name: String, + attributes: List) : ObjCClass(name, attributes) -class ObjCInterface(name: String, - val generics: List = emptyList(), - descriptor: ClassDescriptor? = null, - val superClass: String? = null, - val superClassGenerics: List = emptyList(), - superProtocols: List = emptyList(), - val categoryName: String? = null, - members: List> = emptyList(), - attributes: List = emptyList()) : ObjCClass(name, descriptor, superProtocols, members, attributes) +class ObjCProtocolImpl( + name: String, + override val descriptor: ClassDescriptor, + override val superProtocols: List, + override val members: List>, + attributes: List = emptyList()) : ObjCProtocol(name, attributes) -class ObjCMethod(descriptor: DeclarationDescriptor?, +abstract class ObjCInterface(name: String, + val generics: List, + val categoryName: String?, + attributes: List) : ObjCClass(name, attributes) { + abstract val superClass: String? + abstract val superClassGenerics: List +} + +class ObjCInterfaceImpl( + name: String, + generics: List = emptyList(), + override val descriptor: ClassDescriptor? = null, + override val superClass: String? = null, + override val superClassGenerics: List = emptyList(), + override val superProtocols: List = emptyList(), + categoryName: String? = null, + override val members: List> = emptyList(), + attributes: List = emptyList() +) : ObjCInterface(name, generics, categoryName, attributes) + +class ObjCMethod(override val descriptor: DeclarationDescriptor?, val isInstanceMethod: Boolean, val returnType: ObjCType, val selectors: List, val parameters: List, - val attributes: List) : Stub(buildMethodName(selectors, parameters), descriptor) + val attributes: List) : Stub(buildMethodName(selectors, parameters)) class ObjCParameter(name: String, - descriptor: ParameterDescriptor?, - val type: ObjCType) : Stub(name, descriptor) + override val descriptor: ParameterDescriptor?, + val type: ObjCType) : Stub(name) class ObjCProperty(name: String, - descriptor: PropertyDescriptor?, + override val descriptor: PropertyDescriptor?, val type: ObjCType, val propertyAttributes: List, val setterName: String? = null, val getterName: String? = null, - val declarationAttributes: List = emptyList()) : Stub(name, descriptor) { + val declarationAttributes: List = emptyList()) : Stub(name) { @Deprecated("", ReplaceWith("this.propertyAttributes"), DeprecationLevel.WARNING) val attributes: List get() = propertyAttributes diff --git a/backend.native/tests/framework/values/values.kt b/backend.native/tests/framework/values/values.kt index da44d302596..2c529046af9 100644 --- a/backend.native/tests/framework/values/values.kt +++ b/backend.native/tests/framework/values/values.kt @@ -421,3 +421,5 @@ object UnitBlockCoercionImpl : UnitBlockCoercion<() -> Unit> { override fun coerce(block: () -> Unit): () -> Unit = block override fun uncoerce(block: () -> Unit): () -> Unit = block } + +abstract class MyAbstractList : List