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 configuration: ObjCExportNamer.Configuration
) : ObjCExportNameTranslator { ) : ObjCExportNameTranslator {
private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix) private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix, configuration.objcGenerics)
override fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName = override fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName =
helper.getFileClassName(file) helper.getFileClassName(file)
@@ -100,39 +100,29 @@ internal open class ObjCExportNameTranslatorImpl(
private fun getClassOrProtocolSwiftName( private fun getClassOrProtocolSwiftName(
ktClassOrObject: KtClassOrObject ktClassOrObject: KtClassOrObject
): String = buildString { ): String = buildString {
val ownName = ktClassOrObject.name!!.toIdentifier()
val outerClass = ktClassOrObject.getStrictParentOfType<KtClassOrObject>() val outerClass = ktClassOrObject.getStrictParentOfType<KtClassOrObject>()
if (outerClass != null) { if (outerClass != null) {
append(getClassOrProtocolSwiftName(outerClass)) appendNameWithContainer(ktClassOrObject, 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())
}
} else { } 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 { override fun getTypeParameterName(ktTypeParameter: KtTypeParameter): String = buildString {
append(ktTypeParameter.name!!.toIdentifier()) append(ktTypeParameter.name!!.toIdentifier())
while (helper.isTypeParameterNameReserved(this.toString())) append('_') while (helper.isTypeParameterNameReserved(this.toString())) append('_')
@@ -140,7 +130,8 @@ internal open class ObjCExportNameTranslatorImpl(
} }
private class ObjCExportNamingHelper( private class ObjCExportNamingHelper(
private val topLevelNamePrefix: String private val topLevelNamePrefix: String,
private val objcGenerics: Boolean
) { ) {
fun translateFileName(fileName: String): String = fun translateFileName(fileName: String): String =
@@ -164,6 +155,71 @@ private class ObjCExportNamingHelper(
fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName = fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName =
getFileClassName(file.name) 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) { fun mangleSwiftNestedClassName(name: String): String = when (name) {
"Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167 "Type" -> "${name}_" // See https://github.com/JetBrains/kotlin-native/issues/3167
else -> name else -> name
@@ -209,7 +265,7 @@ internal class ObjCExportNamerImpl(
private val objcGenerics get() = configuration.objcGenerics private val objcGenerics get() = configuration.objcGenerics
override val topLevelNamePrefix get() = configuration.topLevelNamePrefix 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( private fun String.toSpecialStandardClassOrProtocolName() = ObjCExportNamer.ClassOrProtocolName(
swiftName = "Kotlin$this", swiftName = "Kotlin$this",
@@ -327,31 +383,7 @@ internal class ObjCExportNamerImpl(
StringBuilder().apply { StringBuilder().apply {
val containingDeclaration = descriptor.containingDeclaration val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor) { if (containingDeclaration is ClassDescriptor) {
append(getClassOrProtocolSwiftName(containingDeclaration)) appendNameWithContainer(descriptor, 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())
}
} else if (containingDeclaration is PackageFragmentDescriptor) { } else if (containingDeclaration is PackageFragmentDescriptor) {
appendTopLevelClassBaseName(descriptor) appendTopLevelClassBaseName(descriptor)
} else { } else {
@@ -360,21 +392,20 @@ internal class ObjCExportNamerImpl(
}.mangledBySuffixUnderscores() }.mangledBySuffixUnderscores()
} }
private fun ClassDescriptor.hasGenericsInHierarchy(): Boolean { private fun StringBuilder.appendNameWithContainer(
fun ClassDescriptor.hasGenericsChildren(): Boolean = clazz: ClassDescriptor,
unsubstitutedMemberScope.getContributedDescriptors() containingClass: ClassDescriptor
.asSequence() ) = helper.appendNameWithContainer(
.filterIsInstance<ClassDescriptor>() this,
.any { clazz, clazz.name.asString().toIdentifier(),
it.typeConstructor.parameters.isNotEmpty() || containingClass, getClassOrProtocolSwiftName(containingClass),
it.hasGenericsChildren() object : ObjCExportNamingHelper.ClassInfoProvider<ClassDescriptor> {
} override fun hasGenerics(clazz: ClassDescriptor): Boolean =
clazz.typeConstructor.parameters.isNotEmpty()
val upGenerics = generateSequence(this) { it.containingDeclaration as? ClassDescriptor } override fun isInterface(clazz: ClassDescriptor): Boolean = clazz.isInterface
.any { it.typeConstructor.parameters.isNotEmpty() } }
)
return upGenerics || hasGenericsChildren()
}
private fun getClassOrProtocolObjCName(descriptor: ClassDescriptor): String { private fun getClassOrProtocolObjCName(descriptor: ClassDescriptor): String {
val objCMapping = if (descriptor.isInterface) objCProtocolNames else objCClassNames 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"))); @property (readonly) int32_t thirtyTwo __attribute__((swift_name("thirtyTwo")));
@end; @end;
__attribute__((swift_name("DeeplyNestedIType")))
@protocol ValuesDeeplyNestedIType
@required
@end;
__attribute__((objc_subclassing_restricted)) __attribute__((objc_subclassing_restricted))
__attribute__((swift_name("WithGenericDeeply"))) __attribute__((swift_name("WithGenericDeeply")))
@interface ValuesWithGenericDeeply : ValuesBase @interface ValuesWithGenericDeeply : ValuesBase
@@ -324,7 +329,7 @@ __attribute__((swift_name("WithGenericDeeply.Nested")))
@end; @end;
__attribute__((objc_subclassing_restricted)) __attribute__((objc_subclassing_restricted))
__attribute__((swift_name("WithGenericDeeply.NestedType"))) __attribute__((swift_name("WithGenericDeeplyNestedType")))
@interface ValuesWithGenericDeeplyNestedType<T> : ValuesBase @interface ValuesWithGenericDeeplyNestedType<T> : ValuesBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@@ -283,6 +283,8 @@ class Deeply {
class Type { class Type {
val thirtyTwo = 32 val thirtyTwo = 32
} }
interface IType
} }
} }
@@ -546,6 +546,7 @@ func testNames() throws {
try assertEquals(actual: WithGenericDeeplyNestedType<AnyObject>().thirtyThree, expected: 33) 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: CKeywords(float: 1.0, enum : 42, goto: true).goto_, expected: true)
try assertEquals(actual: TypeOuter.Type_().thirtyFour, expected: 34) try assertEquals(actual: TypeOuter.Type_().thirtyFour, expected: 34)
try assertTrue(String(describing: DeeplyNestedIType.self).hasSuffix("DeeplyNestedIType"))
} }
class Base123 : Base23, ExtendedBase1 { class Base123 : Base23, ExtendedBase1 {
@@ -230,7 +230,7 @@ func testGenericInnerClass() throws {
let deep2 = GenOuterDeep2() let deep2 = GenOuterDeep2()
let deep2Before = GenOuterDeep2.Before(deep2) let deep2Before = GenOuterDeep2.Before(deep2)
let deep2After = GenOuterDeep2.After(deep2) let deep2After = GenOuterDeep2.After(deep2)
let deep2soi = GenOuterDeep2GenShallowOuterInner(deep2) let deep2soi = GenOuterDeep2.GenShallowOuterInner(deep2)
let deep2si = GenOuterDeep2GenShallowOuterInnerGenShallowInner<SomeData>(deep2soi) let deep2si = GenOuterDeep2GenShallowOuterInnerGenShallowInner<SomeData>(deep2soi)
let deep2i = GenOuterDeep2GenShallowOuterInnerGenShallowInnerGenDeepInner<SomeData>(deep2si) let deep2i = GenOuterDeep2GenShallowOuterInnerGenShallowInnerGenDeepInner<SomeData>(deep2si)