[ObjCExport] Support naming of nested classes

^KT-65204 Fixed
This commit is contained in:
Sebastian Sellmair
2024-02-08 12:52:55 +01:00
committed by Space Team
parent f1fd84f539
commit 4b6624c920
9 changed files with 264 additions and 8 deletions
@@ -6,16 +6,109 @@
package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
context(KtAnalysisSession, KtObjCExportSession)
fun KtClassLikeSymbol.getObjCClassOrProtocolName(): ObjCExportClassOrProtocolName {
val resolvedObjCNameAnnotation = resolveObjCNameAnnotation()
return ObjCExportClassOrProtocolName(
objCName = resolvedObjCNameAnnotation?.objCName ?: nameOrAnonymous.asString(),
swiftName = resolvedObjCNameAnnotation?.swiftName ?: nameOrAnonymous.asString()
objCName = getObjCName(resolvedObjCNameAnnotation),
swiftName = getSwiftName(resolvedObjCNameAnnotation)
)
}
context(KtAnalysisSession, KtObjCExportSession)
private fun KtClassLikeSymbol.getObjCName(
resolvedObjCNameAnnotation: KtResolvedObjCNameAnnotation? = resolveObjCNameAnnotation(),
): String {
val objCName = (resolvedObjCNameAnnotation?.objCName ?: nameOrAnonymous.asString()).toValidObjCSwiftIdentifier()
if (resolvedObjCNameAnnotation != null && resolvedObjCNameAnnotation.isExact) {
return objCName
}
getContainingSymbol()?.let { it as? KtClassLikeSymbol }?.let { containingClass ->
return containingClass.getObjCName() + objCName.capitalizeAsciiOnly()
}
// KT-65670: Append module specific prefixes?
return configuration.frameworkName.orEmpty() + objCName
}
context(KtAnalysisSession, KtObjCExportSession)
private fun KtClassLikeSymbol.getSwiftName(
resolvedObjCNameAnnotation: KtResolvedObjCNameAnnotation? = resolveObjCNameAnnotation(),
): String {
val swiftName = (resolvedObjCNameAnnotation?.swiftName ?: nameOrAnonymous.asString()).toValidObjCSwiftIdentifier()
if (resolvedObjCNameAnnotation != null && resolvedObjCNameAnnotation.isExact) {
return swiftName
}
getContainingSymbol()?.let { it as? KtClassLikeSymbol }?.let { containingClass ->
val containingClassSwiftName = containingClass.getSwiftName()
return buildString {
if (canBeInnerSwift()) {
append(containingClassSwiftName)
if ("." !in this && containingClass.canBeOuterSwift()) {
// AB -> AB.C
append(".")
append(mangleSwiftNestedClassName(swiftName))
} else {
// AB -> ABC
// A.B -> A.BC
append(swiftName.capitalizeAsciiOnly())
}
} else {
append(containingClassSwiftName.replaceFirst(".", ""))
append(swiftName.capitalizeAsciiOnly())
}
}
}
// KT-65670: Append module specific prefixes?
return swiftName
}
context(KtAnalysisSession, KtObjCExportSession)
private fun KtClassLikeSymbol.canBeInnerSwift(): Boolean {
if (configuration.objcGenerics && this.typeParameters.isNotEmpty()) {
// Swift compiler doesn't seem to handle this case properly.
// See https://bugs.swift.org/browse/SR-14607.
// This behaviour of Kotlin is reported as https://youtrack.jetbrains.com/issue/KT-46518.
return false
}
if (this is KtClassOrObjectSymbol && this.classKind == KtClassKind.INTERFACE) {
// Swift doesn't support nested protocols.
return false
}
return true
}
context(KtAnalysisSession, KtObjCExportSession)
private fun KtClassLikeSymbol.canBeOuterSwift(): Boolean {
if (configuration.objcGenerics && this.typeParameters.isNotEmpty()) {
// Swift nested classes are static but capture outer's generics.
return false
}
if (this is KtClassOrObjectSymbol && this.classKind == KtClassKind.INTERFACE) {
// Swift doesn't support outer protocols.
return false
}
return true
}
private fun mangleSwiftNestedClassName(name: String): String = when (name) {
"Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167
else -> name
}
@@ -17,9 +17,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
val enumKind = this.classKind == KtClassKind.ENUM_CLASS
val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false
val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()
val name = getObjCClassOrProtocolName()
val attributes = (if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()) + name.toNameAttributes()
val comment: ObjCComment? = annotationsList.translateToObjCComment()
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()