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
|
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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user