Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Commit

Permalink
Revise usage of Swift's "import as member" in ObjCExport
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
SvyatoslavScherbina committed Jan 27, 2020
1 parent e866a14 commit b805dc5
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -100,47 +100,38 @@ 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('_')
}
}

private class ObjCExportNamingHelper(
private val topLevelNamePrefix: String
private val topLevelNamePrefix: String,
private val objcGenerics: Boolean
) {

fun translateFileName(fileName: String): String =
Expand All @@ -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
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand All @@ -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()
}

val upGenerics = generateSequence(this) { it.containingDeclaration as? ClassDescriptor }
.any { it.typeConstructor.parameters.isNotEmpty() }
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()

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
Expand Down
7 changes: 6 additions & 1 deletion backend.native/tests/framework/values/expectedLazy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")));
Expand Down
2 changes: 2 additions & 0 deletions backend.native/tests/framework/values/values.kt
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ class Deeply {
class Type {
val thirtyTwo = 32
}

interface IType
}
}

Expand Down
1 change: 1 addition & 0 deletions backend.native/tests/framework/values/values.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit b805dc5

Please sign in to comment.