Revise usage of Swift's "import as member" in ObjCExport

* Avoid emitting accidentally nested protocols
* Unify PSI and descriptors implementation
  (thus fix it with generics involved in ObjCExportLazy)
* Avoid class name translation dependency on its contents
* Simplify the policy
This commit is contained in:
Svyatoslav Scherbina
2020-01-21 12:22:48 +03:00
committed by SvyatoslavScherbina
parent 70700002be
commit 55452eab4d
5 changed files with 110 additions and 71 deletions
@@ -83,7 +83,7 @@ internal open class ObjCExportNameTranslatorImpl(
configuration: ObjCExportNamer.Configuration
) : ObjCExportNameTranslator {
private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix)
private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix, configuration.objcGenerics)
override fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName =
helper.getFileClassName(file)
@@ -100,39 +100,29 @@ internal open class ObjCExportNameTranslatorImpl(
private fun getClassOrProtocolSwiftName(
ktClassOrObject: KtClassOrObject
): String = buildString {
val ownName = ktClassOrObject.name!!.toIdentifier()
val outerClass = ktClassOrObject.getStrictParentOfType<KtClassOrObject>()
if (outerClass != null) {
append(getClassOrProtocolSwiftName(outerClass))
val importAsMember = when {
// FIXME: generics.
ktClassOrObject.isInterface || outerClass.isInterface -> {
// Swift doesn't support neither nested nor outer protocols.
false
}
this.contains('.') -> {
// Swift doesn't support swift_name with deeply nested names.
// It seems to support "OriginalObjCName.SwiftName" though,
// but this doesn't seem neither documented nor reliable.
false
}
else -> true
}
if (importAsMember) {
append(".").append(helper.mangleSwiftNestedClassName(ownName))
} else {
append(ownName.capitalize())
}
appendNameWithContainer(ktClassOrObject, outerClass)
} else {
append(ownName)
append(ktClassOrObject.name!!.toIdentifier())
}
}
private fun StringBuilder.appendNameWithContainer(
ktClassOrObject: KtClassOrObject,
outerClass: KtClassOrObject
) = helper.appendNameWithContainer(
this,
ktClassOrObject, ktClassOrObject.name!!.toIdentifier(),
outerClass, getClassOrProtocolSwiftName(outerClass),
object : ObjCExportNamingHelper.ClassInfoProvider<KtClassOrObject> {
override fun hasGenerics(clazz: KtClassOrObject): Boolean =
clazz.typeParametersWithOuter.count() != 0
override fun isInterface(clazz: KtClassOrObject): Boolean = ktClassOrObject.isInterface
}
)
override fun getTypeParameterName(ktTypeParameter: KtTypeParameter): String = buildString {
append(ktTypeParameter.name!!.toIdentifier())
while (helper.isTypeParameterNameReserved(this.toString())) append('_')
@@ -140,7 +130,8 @@ internal open class ObjCExportNameTranslatorImpl(
}
private class ObjCExportNamingHelper(
private val topLevelNamePrefix: String
private val topLevelNamePrefix: String,
private val objcGenerics: Boolean
) {
fun translateFileName(fileName: String): String =
@@ -164,6 +155,71 @@ private class ObjCExportNamingHelper(
fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName =
getFileClassName(file.name)
fun <T> appendNameWithContainer(
builder: StringBuilder,
clazz: T,
ownName: String,
containingClass: T,
containerName: String,
provider: ClassInfoProvider<T>
) = builder.apply {
if (clazz.canBeSwiftInner(provider)) {
append(containerName)
if (!this.contains('.') && containingClass.canBeSwiftOuter(provider)) {
// AB -> AB.C
append('.')
append(mangleSwiftNestedClassName(ownName))
} else {
// AB -> ABC
// A.B -> A.BC
append(ownName.capitalize())
}
} else {
// AB, A.B -> ABC
val dotIndex = containerName.indexOf('.')
if (dotIndex == -1) {
append(containerName)
} else {
append(containerName.substring(0, dotIndex))
append(containerName.substring(dotIndex + 1).capitalize())
}
append(ownName.capitalize())
}
}
interface ClassInfoProvider<T> {
fun hasGenerics(clazz: T): Boolean
fun isInterface(clazz: T): Boolean
}
private fun <T> T.canBeSwiftOuter(provider: ClassInfoProvider<T>): Boolean = when {
objcGenerics && provider.hasGenerics(this) -> {
// Swift nested classes are static but capture outer's generics.
false
}
provider.isInterface(this) -> {
// Swift doesn't support outer protocols.
false
}
else -> true
}
private fun <T> T.canBeSwiftInner(provider: ClassInfoProvider<T>): Boolean = when {
objcGenerics && provider.hasGenerics(this) -> {
// Swift compiler doesn't seem to handle this case properly.
false
}
provider.isInterface(this) -> {
// Swift doesn't support nested protocols.
false
}
else -> true
}
fun mangleSwiftNestedClassName(name: String): String = when (name) {
"Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167
else -> name
@@ -209,7 +265,7 @@ internal class ObjCExportNamerImpl(
private val objcGenerics get() = configuration.objcGenerics
override val topLevelNamePrefix get() = configuration.topLevelNamePrefix
private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix)
private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix, objcGenerics)
private fun String.toSpecialStandardClassOrProtocolName() = ObjCExportNamer.ClassOrProtocolName(
swiftName = "Kotlin$this",
@@ -327,31 +383,7 @@ internal class ObjCExportNamerImpl(
StringBuilder().apply {
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor) {
append(getClassOrProtocolSwiftName(containingDeclaration))
val importAsMember = when {
objcGenerics && descriptor.hasGenericsInHierarchy() -> false
descriptor.isInterface || containingDeclaration.isInterface -> {
// Swift doesn't support neither nested nor outer protocols.
false
}
this.contains('.') -> {
// Swift doesn't support swift_name with deeply nested names.
// It seems to support "OriginalObjCName.SwiftName" though,
// but this doesn't seem neither documented nor reliable.
false
}
else -> true
}
val ownName = descriptor.name.asString().toIdentifier()
if (importAsMember) {
append(".").append(helper.mangleSwiftNestedClassName(ownName))
} else {
append(ownName.capitalize())
}
appendNameWithContainer(descriptor, containingDeclaration)
} else if (containingDeclaration is PackageFragmentDescriptor) {
appendTopLevelClassBaseName(descriptor)
} else {
@@ -360,21 +392,20 @@ internal class ObjCExportNamerImpl(
}.mangledBySuffixUnderscores()
}
private fun ClassDescriptor.hasGenericsInHierarchy(): Boolean {
fun ClassDescriptor.hasGenericsChildren(): Boolean =
unsubstitutedMemberScope.getContributedDescriptors()
.asSequence()
.filterIsInstance<ClassDescriptor>()
.any {
it.typeConstructor.parameters.isNotEmpty() ||
it.hasGenericsChildren()
}
private fun StringBuilder.appendNameWithContainer(
clazz: ClassDescriptor,
containingClass: ClassDescriptor
) = helper.appendNameWithContainer(
this,
clazz, clazz.name.asString().toIdentifier(),
containingClass, getClassOrProtocolSwiftName(containingClass),
object : ObjCExportNamingHelper.ClassInfoProvider<ClassDescriptor> {
override fun hasGenerics(clazz: ClassDescriptor): Boolean =
clazz.typeConstructor.parameters.isNotEmpty()
val upGenerics = generateSequence(this) { it.containingDeclaration as? ClassDescriptor }
.any { it.typeConstructor.parameters.isNotEmpty() }
return upGenerics || hasGenericsChildren()
}
override fun isInterface(clazz: ClassDescriptor): Boolean = clazz.isInterface
}
)
private fun getClassOrProtocolObjCName(descriptor: ClassDescriptor): String {
val objCMapping = if (descriptor.isInterface) objCProtocolNames else objCClassNames
@@ -309,6 +309,11 @@ __attribute__((swift_name("Deeply.NestedType")))
@property (readonly) int32_t thirtyTwo __attribute__((swift_name("thirtyTwo")));
@end;
__attribute__((swift_name("DeeplyNestedIType")))
@protocol ValuesDeeplyNestedIType
@required
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("WithGenericDeeply")))
@interface ValuesWithGenericDeeply : ValuesBase
@@ -324,7 +329,7 @@ __attribute__((swift_name("WithGenericDeeply.Nested")))
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("WithGenericDeeply.NestedType")))
__attribute__((swift_name("WithGenericDeeplyNestedType")))
@interface ValuesWithGenericDeeplyNestedType<T> : ValuesBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@@ -283,6 +283,8 @@ class Deeply {
class Type {
val thirtyTwo = 32
}
interface IType
}
}
@@ -546,6 +546,7 @@ func testNames() throws {
try assertEquals(actual: WithGenericDeeplyNestedType<AnyObject>().thirtyThree, expected: 33)
try assertEquals(actual: CKeywords(float: 1.0, enum : 42, goto: true).goto_, expected: true)
try assertEquals(actual: TypeOuter.Type_().thirtyFour, expected: 34)
try assertTrue(String(describing: DeeplyNestedIType.self).hasSuffix("DeeplyNestedIType"))
}
class Base123 : Base23, ExtendedBase1 {
@@ -230,7 +230,7 @@ func testGenericInnerClass() throws {
let deep2 = GenOuterDeep2()
let deep2Before = GenOuterDeep2.Before(deep2)
let deep2After = GenOuterDeep2.After(deep2)
let deep2soi = GenOuterDeep2GenShallowOuterInner(deep2)
let deep2soi = GenOuterDeep2.GenShallowOuterInner(deep2)
let deep2si = GenOuterDeep2GenShallowOuterInnerGenShallowInner<SomeData>(deep2soi)
let deep2i = GenOuterDeep2GenShallowOuterInnerGenShallowInnerGenDeepInner<SomeData>(deep2si)