From 1791ed56432556bab1014fa34ff478e1b50a8fe4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 12 Sep 2018 17:45:17 +0300 Subject: [PATCH] Improve inner/nested class names in framework Use 'Outer.Inner' instead of 'OuterInner' for Swift Also do some refactoring and add other minor improvements. --- .../konan/objcexport/ObjCExportNamer.kt | 103 ++++++++++++------ .../tests/framework/values/values.swift | 2 +- 2 files changed, 73 insertions(+), 32 deletions(-) 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 e7f0bf3365f..43715804bd7 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 @@ -46,9 +46,6 @@ internal class ObjCExportNamerImpl( private val topLevelNamePrefix: String = moduleDescriptor.namePrefix ) : ObjCExportNamer { - private fun String.mangleClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName = - ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = "$topLevelNamePrefix${this}") - private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName = ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = this) @@ -95,13 +92,15 @@ internal class ObjCExportNamerImpl( !mapper.canHaveSameName(first, second) } - private val classNames = object : Mapping() { - override fun conflict(first: Any, second: Any): Boolean = true + private inner open class GlobalNameMapping : Mapping() { + final override fun conflict(first: T, second: T): Boolean = true } - private val protocolNames = object : Mapping() { - override fun conflict(first: Any, second: Any): Boolean = true - } + private val objCClassNames = GlobalNameMapping() + private val objCProtocolNames = GlobalNameMapping() + + // Classes and protocols share the same namespace in Swift. + private val swiftClassAndProtocolNames = GlobalNameMapping() private abstract inner class ClassPropertyNameMapping : Mapping() { @@ -127,12 +126,26 @@ internal class ObjCExportNamerImpl( first.containingDeclaration == second.containingDeclaration } - override fun getFileClassName(file: SourceFile): ObjCExportNamer.ClassOrProtocolName = classNames.getOrPut(file) { - val psiSourceFile = file as? PsiSourceFile ?: error("SourceFile '$file' is not PsiSourceFile") - val psiFile = psiSourceFile.psiFile - val ktFile = psiFile as? KtFile ?: error("PsiFile '$psiFile' is not KtFile") - StringBuilder(PackagePartClassUtils.getFilePartShortName(ktFile.name)).mangledSequence { append("_") } - }.mangleClassOrProtocolName() + override fun getFileClassName(file: SourceFile): ObjCExportNamer.ClassOrProtocolName { + val baseName by lazy { + val psiSourceFile = file as? PsiSourceFile ?: error("SourceFile '$file' is not PsiSourceFile") + val psiFile = psiSourceFile.psiFile + val ktFile = psiFile as? KtFile ?: error("PsiFile '$psiFile' is not KtFile") + PackagePartClassUtils.getFilePartShortName(ktFile.name) + } + + val objCName = objCClassNames.getOrPut(file) { + StringBuilder(topLevelNamePrefix).append(baseName) + .mangledBySuffixUnderscores() + } + + val swiftName = swiftClassAndProtocolNames.getOrPut(file) { + StringBuilder(baseName) + .mangledBySuffixUnderscores() + } + + return ObjCExportNamer.ClassOrProtocolName(swiftName = swiftName, objCName = objCName) + } private val predefinedClassNames = mapOf( builtIns.any to kotlinAnyName, @@ -140,22 +153,48 @@ internal class ObjCExportNamerImpl( builtIns.mutableMap to mutableMapName ) - override fun getClassOrProtocolName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName { - predefinedClassNames[descriptor]?.let { return it } + override fun getClassOrProtocolName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName = + predefinedClassNames[descriptor] + ?: ObjCExportNamer.ClassOrProtocolName( + swiftName = getClassOrProtocolSwiftName(descriptor), + objCName = getClassOrProtocolObjCName(descriptor) + ) - val mapping = if (descriptor.isInterface) protocolNames else classNames + private fun getClassOrProtocolSwiftName( + descriptor: ClassDescriptor + ): String = swiftClassAndProtocolNames.getOrPut(descriptor) { + StringBuilder().apply { + val containingDeclaration = descriptor.containingDeclaration + if (containingDeclaration is ClassDescriptor) { + append(getClassOrProtocolSwiftName(containingDeclaration)) + .append(".").append(descriptor.name.asString()) + } else { + appendTopLevelClassBaseName(descriptor) + } + }.mangledBySuffixUnderscores() + } - return mapping.getOrPut(descriptor) { + private fun getClassOrProtocolObjCName(descriptor: ClassDescriptor): String { + val objCMapping = if (descriptor.isInterface) objCProtocolNames else objCClassNames + return objCMapping.getOrPut(descriptor) { StringBuilder().apply { - if (descriptor.module != moduleDescriptor) { - append(descriptor.module.namePrefix) - } + val containingDeclaration = descriptor.containingDeclaration + if (containingDeclaration is ClassDescriptor) { + append(getClassOrProtocolObjCName(containingDeclaration)) + .append(descriptor.name.asString().capitalize()) - descriptor.parentsWithSelf.takeWhile { it is ClassDescriptor } - .toList().reversed() - .joinTo(this, "") { it.name.asString().capitalize() } - }.mangledSequence { append("_") } - }.mangleClassOrProtocolName() + } else { + append(topLevelNamePrefix).appendTopLevelClassBaseName(descriptor) + } + }.mangledBySuffixUnderscores() + } + } + + private fun StringBuilder.appendTopLevelClassBaseName(descriptor: ClassDescriptor) = apply { + if (descriptor.module != moduleDescriptor) { + append(descriptor.module.namePrefix) + } + append(descriptor.name.asString()) } override fun getSelector(method: FunctionDescriptor): String = methodSelectors.getOrPut(method) { @@ -258,7 +297,7 @@ internal class ObjCExportNamerImpl( return objectInstanceSelectors.getOrPut(descriptor) { val name = descriptor.name.asString().decapitalize().mangleIfSpecialFamily("get") - StringBuilder(name).mangledSequence { append("_") } + StringBuilder(name).mangledBySuffixUnderscores() } } @@ -272,7 +311,7 @@ internal class ObjCExportNamerImpl( if (index == 0) lower else lower.capitalize() }.joinToString("").mangleIfSpecialFamily("the") - StringBuilder(name).mangledSequence { append("_") } + StringBuilder(name).mangledBySuffixUnderscores() } } @@ -280,8 +319,8 @@ internal class ObjCExportNamerImpl( val any = builtIns.any predefinedClassNames.forEach { descriptor, name -> - // Note: it is a hack. - classNames.forceAssign(descriptor, name.swiftName) + objCClassNames.forceAssign(descriptor, name.objCName) + swiftClassAndProtocolNames.forceAssign(descriptor, name.swiftName) } fun ClassDescriptor.method(name: String) = @@ -336,7 +375,7 @@ internal class ObjCExportNamerImpl( private fun String.startsWithWords(words: String) = this.startsWith(words) && (this.length == words.length || !this[words.length].isLowerCase()) - private abstract inner class Mapping() { + private abstract inner class Mapping() { private val elementToName = mutableMapOf() private val nameToElements = mutableMapOf>() @@ -390,6 +429,8 @@ private inline fun StringBuilder.mangledSequence(crossinline mangle: StringBuild this@mangledSequence.toString() } +private fun StringBuilder.mangledBySuffixUnderscores() = this.mangledSequence { append("_") } + private fun ObjCExportMapper.canHaveCommonSubtype(first: ClassDescriptor, second: ClassDescriptor): Boolean { if (first.isSubclassOf(second) || second.isSubclassOf(first)) { return true diff --git a/backend.native/tests/framework/values/values.swift b/backend.native/tests/framework/values/values.swift index 43eea95028d..9969f50285b 100644 --- a/backend.native/tests/framework/values/values.swift +++ b/backend.native/tests/framework/values/values.swift @@ -377,7 +377,7 @@ func testDataClass() throws { } func testCompanionObj() throws { - try assertEquals(actual: WithCompanionAndObjectCompanion().str, expected: "String") + try assertEquals(actual: WithCompanionAndObject.Companion().str, expected: "String") try assertEquals(actual: ValuesKt.getCompanionObject().str, expected: "String") let namedFromCompanion = ValuesKt.getCompanionObject().named