[Commonizer] Better handling of ObjcMethod and related annotations

They are important for overridability rules, so can't be just ignored.
Some arguments are still different on different platforms, so
commonized version is a bit strange.

But the only important part is selector, which should be same on
all platforms.

^KT-57588
This commit is contained in:
Pavel Kunyavskiy
2024-01-23 17:36:15 +01:00
committed by Space Team
parent ffed0ecf1f
commit 0a07f16896
6 changed files with 103 additions and 59 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.commonizer.core
import org.jetbrains.kotlin.commonizer.cir.CirAnnotation
import org.jetbrains.kotlin.commonizer.cir.CirClassType
import org.jetbrains.kotlin.commonizer.cir.CirConstantValue
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.commonizer.utils.COMMONIZER_OBJC_INTEROP_CALLABLE_ANNOTATION_ID
import org.jetbrains.kotlin.commonizer.utils.DEPRECATED_ANNOTATION_CLASS_ID
@@ -20,16 +21,50 @@ object AnnotationsCommonizer : AssociativeCommonizer<List<CirAnnotation>> {
val firstAnnotations = first.associateBy { it.type.classifierId }
val secondAnnotations = second.associateBy { it.type.classifierId }
return setOfNotNull(
commonizedObjcCallableAnnotation(first, second),
commonizedDeprecatedAnnotation(firstAnnotations, secondAnnotations)
).plus(commonizedSimpleAnnotations(firstAnnotations, secondAnnotations)).toList()
return buildList {
addAll(commonizedObjcCallableAnnotation(first, second))
commonizedDeprecatedAnnotation(firstAnnotations, secondAnnotations)?.let { add(it) }
addAll(commonizedSimpleAnnotations(firstAnnotations, secondAnnotations))
}.distinct()
}
private fun commonizedObjcCallableAnnotation(first: List<CirAnnotation>, second: List<CirAnnotation>): CirAnnotation? {
return if (first.any { it.type.classifierId.isObjCInteropCallableAnnotation } &&
second.any { it.type.classifierId.isObjCInteropCallableAnnotation }
) objCCallableAnnotation else null
private fun commonizedObjcCallableAnnotation(firstAnnotations: List<CirAnnotation>, secondAnnotations: List<CirAnnotation>): List<CirAnnotation> {
val firstObjCCallable = firstAnnotations.filter { it.type.classifierId.isObjCInteropCallableAnnotation }
val secondObjCCallable = secondAnnotations.filter { it.type.classifierId.isObjCInteropCallableAnnotation }
return buildList {
if (firstObjCCallable.isNotEmpty() && secondObjCCallable.isNotEmpty()) {
add(objCCallableAnnotation)
firstObjCCallable.forEach { first ->
secondObjCCallable.forEach loop@{ second ->
if (first.type.classifierId == second.type.classifierId) {
val annotation = CirAnnotation.createInterned(
CirClassType.createInterned(
classId = first.type.classifierId,
outerType = null, arguments = emptyList(), isMarkedNullable = false
),
buildMap {
for ((key, value) in first.constantValueArguments) {
if (second.constantValueArguments[key] == value) {
put(key, value)
continue
}
when (key.name) {
"selector", "initSelector" -> return@loop
"encoding" -> put(key, CirConstantValue.StringValue(""))
"designated" -> put(key, CirConstantValue.BooleanValue(false))
"isStret" -> {}
else -> error("No default is known for $key")
}
}
},
emptyMap()
)
add(annotation)
}
}
}
}
}
}
private fun commonizedSimpleAnnotations(first: Annotations, second: Annotations): List<CirAnnotation> {
@@ -87,39 +87,40 @@ expect fun functionMismatchedParameterNames30(arg0: Int, vararg variadicArgument
expect fun functionMismatchedParameterNames31(i: Int, s: String)
@kotlin.commonizer.ObjCCallable
@kotlinx.cinterop.ObjCMethod("fmpn34:s:", "", false)
expect fun functionMismatchedParameterNames34(i: Int, s: String)
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn37:arg1:", "", false)
expect fun functionMismatchedParameterNames37(arg0: Int, arg1: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn38:s:", "", false)
expect fun functionMismatchedParameterNames38(i: Int, s: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn39:s:", "", false)
expect fun functionMismatchedParameterNames39(i: Int, s: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn40:s:", "", false)
expect fun functionMismatchedParameterNames40(i: Int, s: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn41:arg1:", "", false)
expect fun functionMismatchedParameterNames41(arg0: Int, arg1: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn42:arg1:", "", false)
expect fun functionMismatchedParameterNames42(arg0: Int, arg1: String)
// hasStableParameterNames=false
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("fmpn43:arg1:", "", false)
expect fun functionMismatchedParameterNames43(arg0: Int, arg1: String)
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("ofpn:s", "", false)
expect fun overloadedFunctionByParameterNames(i: Int, s: String)
@kotlin.commonizer.ObjCCallable
@kotlin.commonizer.ObjCCallable @kotlinx.cinterop.ObjCMethod("ofpn:xs", "", false)
expect fun overloadedFunctionByParameterNames(xi: Int, xs: String)
expect inline fun inlineFunction1(lazyMessage: () -> String)
@@ -3,5 +3,4 @@ package kotlinx.cinterop
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.BINARY)
annotation class ObjCMethod() // fake annotation class without properties
annotation class ObjCMethod(val selector: String, val encoding: String, val isStret: Boolean)
@@ -42,61 +42,61 @@ fun functionMismatchedParameterNames30(arg0: Int, vararg variadicArguments: Int)
fun functionMismatchedParameterNames31(i: Int, s: String) {}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn32:s:", "", false)
fun functionMismatchedParameterNames32(i: Int, s: String) {
}
fun functionMismatchedParameterNames33(i: Int, s: String) {}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn34:s:", "", false)
fun functionMismatchedParameterNames34(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn35:s:", "", false)
fun functionMismatchedParameterNames35(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn36:arg1:", "", false)
fun functionMismatchedParameterNames36(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn37:arg1:", "", false)
fun functionMismatchedParameterNames37(arg0: Int, arg1: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn38:s:", "", false)
fun functionMismatchedParameterNames38(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn39:s:", "", false)
fun functionMismatchedParameterNames39(i: Int, s: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn40:s:", "", false)
fun functionMismatchedParameterNames40(i: Int, s: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn41:arg1:", "", false)
fun functionMismatchedParameterNames41(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn42:arg1:", "", false)
fun functionMismatchedParameterNames42(arg0: Int, arg1: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn43:arg1:", "", false)
fun functionMismatchedParameterNames43(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("ofpn:s:", "", false)
fun overloadedFunctionByParameterNames(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("ofpn:xs:", "", false)
fun overloadedFunctionByParameterNames(xi: Int, xs: String) {
}
@@ -43,59 +43,59 @@ fun functionMismatchedParameterNames30(arg0: Int, vararg variadicArguments: Int)
fun functionMismatchedParameterNames31(i: Int, s: String) {}
fun functionMismatchedParameterNames32(i: Int, s: String) {}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn33:s:", "", false)
fun functionMismatchedParameterNames33(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn34:s:", "", false)
fun functionMismatchedParameterNames34(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn35:arg1:", "", false)
fun functionMismatchedParameterNames35(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn36:s:", "", false)
fun functionMismatchedParameterNames36(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn37:arg1:", "", false)
fun functionMismatchedParameterNames37(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn38:s:", "", false)
fun functionMismatchedParameterNames38(i: Int, s: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn39:s:", "", false)
fun functionMismatchedParameterNames39(i: Int, s: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn40:s:", "", false)
fun functionMismatchedParameterNames40(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn41:arg1:", "", false)
fun functionMismatchedParameterNames41(arg0: Int, arg1: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn42:arg1:", "", false)
fun functionMismatchedParameterNames42(arg0: Int, arg1: String) {
}
// hasStableParameterNames=false
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("fmpn43:arg1:", "", false)
fun functionMismatchedParameterNames43(arg0: Int, arg1: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("ofpn:s:", "", false)
fun overloadedFunctionByParameterNames(i: Int, s: String) {
}
@kotlinx.cinterop.ObjCMethod
@kotlinx.cinterop.ObjCMethod("ofpn:xs:", "", false)
fun overloadedFunctionByParameterNames(xi: Int, xs: String) {
}
@@ -21,23 +21,27 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
simpleSingleSourceTarget(
"a", """
import kotlinx.cinterop.*
@ObjCMethod(0)
@ObjCMethod("x", "xab", false)
fun x() {}
@ObjCMethod("wrongSelector1", "", false)
fun y() {}
""".trimIndent()
)
simpleSingleSourceTarget(
"b", """
import kotlinx.cinterop.*
@ObjCMethod(1)
@ObjCMethod("x", "xab", false)
fun x() {}
@ObjCMethod("wrongSelector2", "", false)
fun y() {}
""".trimIndent()
)
simpleSingleSourceTarget(
"c", """
import kotlinx.cinterop.*
@ObjCMethod(2)
@ObjCMethod("x", "xc", true)
fun x() {}
""".trimIndent()
)
@@ -45,7 +49,7 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
simpleSingleSourceTarget(
"d", """
import kotlinx.cinterop.*
@ObjCMethod(2)
@ObjCMethod("x", "xd")
fun x() {}
""".trimIndent()
)
@@ -54,15 +58,19 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
result.assertCommonized(
"(a, b)", """
import kotlin.commonizer.*
@ObjCCallable
import kotlinx.cinterop.*
@ObjCCallable @ObjCMethod("x", "xab", false)
expect fun x()
@ObjCCallable
expect fun y()
""".trimIndent()
)
result.assertCommonized(
"(c, d)", """
import kotlin.commonizer.*
@ObjCCallable
import kotlinx.cinterop.*
@ObjCCallable @ObjCMethod("x", "")
expect fun x()
""".trimIndent()
)
@@ -70,7 +78,8 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
result.assertCommonized(
"(a, b, c, d)", """
import kotlin.commonizer.*
@ObjCCallable
import kotlinx.cinterop.*
@ObjCCallable @ObjCMethod("x", "")
expect fun x()
""".trimIndent()
)
@@ -119,7 +128,7 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
simpleSingleSourceTarget(
"a", """
import kotlinx.cinterop.*
@ObjCMethod(0)
@ObjCMethod("x", "xa")
fun x() {}
""".trimIndent()
)
@@ -127,7 +136,7 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
simpleSingleSourceTarget(
"b", """
import kotlinx.cinterop.*
@ObjCConstructor(1)
@ObjCConstructor("x", false)
fun x() {}
""".trimIndent()
)
@@ -135,7 +144,7 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
simpleSingleSourceTarget(
"c", """
import kotlinx.cinterop.*
@ObjCFactory(2)
@ObjCFactory("x", "xa")
fun x() {}
""".trimIndent()
)
@@ -183,7 +192,7 @@ class HierarchicalCInteropCallableAnnotationCommonizationTest : AbstractInlineSo
"a", """
import kotlinx.cinterop.*
@ObjCMethod(0)
@ObjCMethod("x", "xa")
fun x() {}
""".trimIndent()
)
@@ -231,9 +240,9 @@ private fun InlineSourceBuilder.ModuleBuilder.objCAnnotations() {
source(
"""
package kotlinx.cinterop
annotation class ObjCMethod(val x: Int)
annotation class ObjCConstructor(val x: Int)
annotation class ObjCFactory(val x: Int)
public annotation class ObjCMethod(val selector: String, val encoding: String, val isStret: Boolean = false)
public annotation class ObjCConstructor(val initSelector: String, val designated: Boolean)
public annotation class ObjCFactory(val selector: String, val encoding: String, val isStret: Boolean = false)
""".trimIndent(),
"kotlinx.kt"
)