[K/N][cinterop] Generate members of included categories as class members

This way inheritors of such class would be able to override category
members the same way they can in Objective-C. Also, API surface of
interop libraries should become a little more stable.
This commit is contained in:
Sergey Bogolepov
2023-01-12 13:14:29 +02:00
committed by Space Team
parent 898dd02d29
commit 72637f19dc
@@ -98,7 +98,7 @@ private class ObjCMethodStubBuilder(
private val method: ObjCMethod,
private val container: ObjCContainer,
private val isDesignatedInitializer: Boolean,
override val context: StubsBuildingContext
override val context: StubsBuildingContext,
) : StubElementBuilder {
private val isStret: Boolean
private val stubReturnType: StubType
@@ -112,6 +112,9 @@ private class ObjCMethodStubBuilder(
private val isOverride: Boolean =
container is ObjCClassOrProtocol && method.isOverride(container)
private val isDeprecatedCategoryMethod: Boolean =
container is ObjCCategory && container in container.clazz.includedCategories
init {
val returnType = method.getReturnType(container.classOrProtocol)
isStret = returnType.isStret(context.configuration.target)
@@ -221,6 +224,7 @@ private class ObjCMethodStubBuilder(
annotations = annotations,
modality = MemberStubModality.FINAL
)
// TODO: Should we deprecate it as well?
createMethod
}
is ObjCProtocol -> null
@@ -228,6 +232,10 @@ private class ObjCMethodStubBuilder(
} else {
null
}
if (isDeprecatedCategoryMethod && annotations.filterIsInstance<AnnotationStub.Deprecated>().isEmpty()) {
val target = if (method.isClass) "class" else "instance"
annotations += AnnotationStub.Deprecated(message = "Use $target method instead", replaceWith = "", level = DeprecationLevel.WARNING)
}
return listOfNotNull(
FunctionStub(
name,
@@ -292,8 +300,9 @@ internal val ObjCClassOrProtocol.selfAndSuperTypes: Sequence<ObjCClassOrProtocol
internal val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
get() = this.immediateSuperTypes.flatMap { it.selfAndSuperTypes }.distinct()
internal fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
this.methods.asSequence().filter { it.isClass == isClass }
private fun ObjCContainer.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
this.methods.asSequence().filter { it.isClass == isClass } +
if (this is ObjCClass) { includedCategoriesMethods(isClass) } else emptyList()
@Suppress("UNUSED_PARAMETER")
internal fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
@@ -331,6 +340,16 @@ internal fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<Stri
internal fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
private fun ObjCClass.includedCategoriesMethods(isMeta: Boolean): List<ObjCMethod> =
includedCategories.flatMap { category ->
category.declaredMethods(isMeta)
}
private fun ObjCClass.includedCategoriesProperties(isMeta: Boolean): List<ObjCProperty> =
includedCategories.flatMap { category ->
category.properties.filter { it.getter.isClass == isMeta }
}
internal abstract class ObjCContainerStubBuilder(
final override val context: StubsBuildingContext,
private val container: ObjCClassOrProtocol,
@@ -381,7 +400,13 @@ internal abstract class ObjCContainerStubBuilder(
this.methods = methods.distinctBy { it.selector }.toList()
this.properties = container.properties.filter { property ->
val properties = container.properties + if (container is ObjCClass) {
container.includedCategoriesProperties(isMeta)
} else {
emptyList()
}
this.properties = properties.filter { property ->
property.getter.isClass == isMeta &&
// Select only properties that don't override anything:
superMethods.none { property.getter.replaces(it) || property.setter?.replaces(it) ?: false }
@@ -589,6 +614,10 @@ private class ObjCPropertyStubBuilder(
private val getterBuilder: ObjCMethodStubBuilder,
private val setterMethod: ObjCMethodStubBuilder?
) : StubElementBuilder {
private val isDeprecatedCategoryProperty =
container is ObjCCategory && container in container.clazz.includedCategories
override fun build(): List<PropertyStub> {
val type = property.getType(container.classOrProtocol)
val kotlinType = context.mirror(type).argType
@@ -601,7 +630,12 @@ private class ObjCPropertyStubBuilder(
is ObjCCategory -> ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass))
}
val origin = StubOrigin.ObjCProperty(property, container)
return listOf(PropertyStub(mangleSimple(property.name), kotlinType.toStubIrType(), kind, modality, receiver, origin = origin))
val annotations = if (isDeprecatedCategoryProperty) {
listOf(AnnotationStub.Deprecated(message = "Use instance property instead", replaceWith = "", level = DeprecationLevel.WARNING))
} else {
emptyList()
}
return listOf(PropertyStub(mangleSimple(property.name), kotlinType.toStubIrType(), kind, modality, receiver, annotations, origin = origin))
}
}