Add minor improvements to ObjCExport

This commit is contained in:
Svyatoslav Scherbina
2019-05-15 17:09:59 +03:00
committed by SvyatoslavScherbina
parent faafeea719
commit c0ef560b07
6 changed files with 306 additions and 193 deletions
@@ -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<ObjCTopLevel<*>>
fun getClassIfExtension(receiverType: KotlinType): ClassDescriptor?
fun translateFile(file: SourceFile, declarations: List<CallableMemberDescriptor>): 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<ObjCTopLevel<*>> {
val stubs = mutableListOf<ObjCTopLevel<*>>()
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<ObjectType>"
))
// TODO: only if appears
stubs.add(objCInterface(
namer.mutableMapName,
generics = listOf("KeyType", "ObjectType"),
superClass = "NSMutableDictionary<KeyType, ObjectType>"
))
stubs.add(ObjCInterfaceImpl("NSError", categoryName = "NSErrorKotlinException", members = buildMembers {
+ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly"))
}))
genKotlinNumbers(stubs)
return stubs
}
private fun genKotlinNumbers(stubs: MutableList<ObjCTopLevel<*>>) {
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 {
return ObjCMethod(
null,
false,
ObjCInstanceType,
listOf(kind.factorySelector),
listOf(ObjCParameter("value", null, kind.objCType)),
attributes
)
}
private fun nsNumberFactory(kind: NSNumberKind, attributes: List<String> = emptyList()): ObjCMethod {
return ObjCMethod(
null,
true,
ObjCInstanceType,
listOf(kind.initSelector),
listOf(ObjCParameter("value", null, kind.objCType)),
attributes
)
}
override fun 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<String> {
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<Stub<*>> = buildMembers { translateInterfaceMembers(descriptor) }
val superProtocols: List<String> = 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<CallableMemberDescriptor>): 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<FunctionDescriptor, Set<RenderedStub<ObjCMethod>>>()
private val propertyToSignatures = mutableMapOf<PropertyDescriptor, Set<RenderedStub<ObjCProperty>>>()
private fun buildAsDeclaredOrInheritedMethods(
method: FunctionDescriptor
): Set<RenderedStub<ObjCMethod>> = methodToSignatures.getOrPut(method) {
): Set<RenderedStub<ObjCMethod>> {
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<RenderedStub<ObjCProperty>> = propertyToSignatures.getOrPut(property) {
): Set<RenderedStub<ObjCProperty>> {
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<FunctionDescriptor>()
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* <SomeInterface>`
// 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<ModuleDescriptor>,
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<String>()
private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
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<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()
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<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))
}
@@ -1062,7 +1091,7 @@ private fun objCInterface(
superProtocols: List<String> = emptyList(),
members: List<Stub<*>> = emptyList(),
attributes: List<String> = emptyList()
): ObjCInterface = ObjCInterface(
): ObjCInterface = ObjCInterfaceImpl(
name.objCName,
generics,
descriptor,
@@ -1080,7 +1109,7 @@ private fun objCProtocol(
superProtocols: List<String>,
members: List<Stub<*>>,
attributes: List<String> = 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<String> = listOfNotNull(
internal fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List<String> = 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()
private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? = descriptor.typeConstructor.supertypes.filter { !it.isInterface() }.firstOrNull()
internal const val OBJC_SUBCLASSING_RESTRICTED = "objc_subclassing_restricted"
@@ -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)
@@ -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) }
@@ -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<ModuleDescriptor>,
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<ModuleDescriptor>,
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 <T : Any> getPredefined(method: FunctionDescriptor, predefinedForAny: Map<Name, T>): 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>): N {
inline fun getOrPut(element: T, nameCandidates: () -> Sequence<N>): N {
getIfAssigned(element)?.let { return it }
nameCandidates().forEach {
@@ -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<out D : DeclarationDescriptor>(val name: String, val descriptor: D?)
abstract class Stub<out D : DeclarationDescriptor>(val name: String) {
abstract val descriptor: D?
}
abstract class ObjCTopLevel<out D : DeclarationDescriptor>(name: String) : Stub<D>(name)
abstract class ObjCClass<out D : DeclarationDescriptor>(name: String,
descriptor: D?,
val superProtocols: List<String>,
val members: List<Stub<*>>,
val attributes: List<String>) : Stub<D>(name, descriptor)
val attributes: List<String>) : ObjCTopLevel<D>(name) {
abstract val superProtocols: List<String>
abstract val members: List<Stub<*>>
}
class ObjCProtocol(name: String,
descriptor: ClassDescriptor,
superProtocols: List<String>,
members: List<Stub<*>>,
attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members, attributes)
abstract class ObjCProtocol(name: String,
attributes: List<String>) : ObjCClass<ClassDescriptor>(name, attributes)
class ObjCInterface(name: String,
val generics: List<String> = emptyList(),
descriptor: ClassDescriptor? = null,
val superClass: String? = null,
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList(),
superProtocols: List<String> = emptyList(),
val categoryName: String? = null,
members: List<Stub<*>> = emptyList(),
attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members, attributes)
class ObjCProtocolImpl(
name: String,
override val descriptor: ClassDescriptor,
override val superProtocols: List<String>,
override val members: List<Stub<*>>,
attributes: List<String> = emptyList()) : ObjCProtocol(name, attributes)
class ObjCMethod(descriptor: DeclarationDescriptor?,
abstract class ObjCInterface(name: String,
val generics: List<String>,
val categoryName: String?,
attributes: List<String>) : ObjCClass<ClassDescriptor>(name, attributes) {
abstract val superClass: String?
abstract val superClassGenerics: List<ObjCNonNullReferenceType>
}
class ObjCInterfaceImpl(
name: String,
generics: List<String> = emptyList(),
override val descriptor: ClassDescriptor? = null,
override val superClass: String? = null,
override val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList(),
override val superProtocols: List<String> = emptyList(),
categoryName: String? = null,
override val members: List<Stub<*>> = emptyList(),
attributes: List<String> = emptyList()
) : ObjCInterface(name, generics, categoryName, attributes)
class ObjCMethod(override val descriptor: DeclarationDescriptor?,
val isInstanceMethod: Boolean,
val returnType: ObjCType,
val selectors: List<String>,
val parameters: List<ObjCParameter>,
val attributes: List<String>) : Stub<DeclarationDescriptor>(buildMethodName(selectors, parameters), descriptor)
val attributes: List<String>) : Stub<DeclarationDescriptor>(buildMethodName(selectors, parameters))
class ObjCParameter(name: String,
descriptor: ParameterDescriptor?,
val type: ObjCType) : Stub<ParameterDescriptor>(name, descriptor)
override val descriptor: ParameterDescriptor?,
val type: ObjCType) : Stub<ParameterDescriptor>(name)
class ObjCProperty(name: String,
descriptor: PropertyDescriptor?,
override val descriptor: PropertyDescriptor?,
val type: ObjCType,
val propertyAttributes: List<String>,
val setterName: String? = null,
val getterName: String? = null,
val declarationAttributes: List<String> = emptyList()) : Stub<PropertyDescriptor>(name, descriptor) {
val declarationAttributes: List<String> = emptyList()) : Stub<PropertyDescriptor>(name) {
@Deprecated("", ReplaceWith("this.propertyAttributes"), DeprecationLevel.WARNING)
val attributes: List<String> get() = propertyAttributes
@@ -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<Any?>