Improve Objective-C designated initializers detection (#2028)
If Objective-C class doesn't declare a designated initializer, then all of its initializers are considered designated now. See also https://github.com/JetBrains/kotlin-native/issues/1995#issuecomment-418335764
This commit is contained in:
committed by
GitHub
parent
7f00e581b5
commit
55f4c224e9
+1
-1
@@ -842,7 +842,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
nsReturnsRetained = hasAttribute(cursor, NS_RETURNS_RETAINED),
|
||||
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
||||
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0),
|
||||
isDesginatedInitializer = hasAttribute(cursor, OBJC_DESGINATED_INITIALIZER)
|
||||
isExplicitlyDesignatedInitializer = hasAttribute(cursor, OBJC_DESGINATED_INITIALIZER)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclar
|
||||
data class ObjCMethod(
|
||||
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
||||
val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
||||
val isOptional: Boolean, val isInit: Boolean, val isDesginatedInitializer: Boolean
|
||||
val isOptional: Boolean, val isInit: Boolean, val isExplicitlyDesignatedInitializer: Boolean
|
||||
) {
|
||||
|
||||
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
||||
|
||||
+37
-23
@@ -67,7 +67,8 @@ private fun ObjCMethod.getFirstKotlinParameterNameCandidate(forConstructorOrFact
|
||||
|
||||
class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
||||
val method: ObjCMethod,
|
||||
private val container: ObjCContainer) : KotlinStub, NativeBacked {
|
||||
private val container: ObjCContainer,
|
||||
private val isDesignatedInitializer: Boolean) : KotlinStub, NativeBacked {
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||
if (context.nativeBridges.isSupported(this)) {
|
||||
@@ -86,7 +87,7 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
||||
when (container) {
|
||||
is ObjCClass -> {
|
||||
// TODO: consider generating non-designated initializers as factories.
|
||||
val designated = method.isDesginatedInitializer ||
|
||||
val designated = isDesignatedInitializer ||
|
||||
stubGenerator.configuration.disableDesignatedInitializerChecks
|
||||
|
||||
result.add("")
|
||||
@@ -364,25 +365,8 @@ private val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
||||
private fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.methods.asSequence().filter { it.isClass == isClass }
|
||||
|
||||
private fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> {
|
||||
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
||||
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
||||
// but seems to consider them as non-designated if class declares its own ones.
|
||||
// Simulate the similar behaviour:
|
||||
return if (!isMeta && container.declaredMethods(isMeta).any { it.isDesginatedInitializer }) {
|
||||
this.map {
|
||||
if (it.isDesginatedInitializer) {
|
||||
it.copy(isDesginatedInitializer = false)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
// TODO: exclude methods that are marked as unavailable in [container].
|
||||
}
|
||||
private fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
|
||||
this // TODO: exclude methods that are marked as unavailable in [container].
|
||||
|
||||
private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.immediateSuperTypes.flatMap { it.methodsWithInherited(isClass) }
|
||||
@@ -392,6 +376,27 @@ private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<Obj
|
||||
private fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
(this.declaredMethods(isClass) + this.inheritedMethods(isClass)).distinctBy { it.selector }
|
||||
|
||||
private fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<String>): Set<String> {
|
||||
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
||||
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
||||
// but seems to consider them as non-designated if class declares its own ones explicitly.
|
||||
// Simulate the similar behaviour:
|
||||
val explicitlyDesignatedInitializers = this.methods.filter { it.isExplicitlyDesignatedInitializer && !it.isClass }
|
||||
|
||||
if (explicitlyDesignatedInitializers.isNotEmpty()) {
|
||||
explicitlyDesignatedInitializers.mapTo(result) { it.selector }
|
||||
} else {
|
||||
this.declaredMethods(isClass = false).filter { it.isInit }.mapTo(result) { it.selector }
|
||||
this.baseClass?.getDesignatedInitializerSelectors(result)
|
||||
}
|
||||
|
||||
this.superTypes.filterIsInstance<ObjCProtocol>()
|
||||
.flatMap { it.declaredMethods(isClass = false) }.filter { it.isInit }
|
||||
.mapTo(result) { it.selector }
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
||||
|
||||
@@ -446,8 +451,17 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
}
|
||||
}
|
||||
|
||||
private val designatedInitializerSelectors = if (container is ObjCClass && !isMeta) {
|
||||
container.getDesignatedInitializerSelectors(mutableSetOf())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private val methodToStub = methods.map {
|
||||
it to ObjCMethodStub(stubGenerator, it, container)
|
||||
it to ObjCMethodStub(
|
||||
stubGenerator, it, container,
|
||||
isDesignatedInitializer = it.selector in designatedInitializerSelectors
|
||||
)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
@@ -568,7 +582,7 @@ class ObjCCategoryStub(
|
||||
// TODO: consider removing members that are also present in the class or its supertypes.
|
||||
|
||||
private val methodToStub = category.methods.map {
|
||||
it to ObjCMethodStub(stubGenerator, it, category)
|
||||
it to ObjCMethodStub(stubGenerator, it, category, isDesignatedInitializer = false)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
|
||||
Reference in New Issue
Block a user