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:
committed by
SvyatoslavScherbina
parent
70700002be
commit
55452eab4d
+100
-69
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user