From 0a07f168966cb219235c8765968edf3994846152 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Tue, 23 Jan 2024 17:36:15 +0100 Subject: [PATCH] [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 --- .../commonizer/core/AnnotationsCommonizer.kt | 51 ++++++++++++++++--- .../commonized/common/package_root.kt | 19 +++---- .../common/package_kotlinx_cinterop.kt | 3 +- .../original/ios/package_root.kt | 26 +++++----- .../original/macos/package_root.kt | 26 +++++----- ...eropCallableAnnotationCommonizationTest.kt | 37 +++++++++----- 6 files changed, 103 insertions(+), 59 deletions(-) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AnnotationsCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AnnotationsCommonizer.kt index bd87b0f7b82..6d7ef39c6e5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AnnotationsCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/AnnotationsCommonizer.kt @@ -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> { 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, second: List): CirAnnotation? { - return if (first.any { it.type.classifierId.isObjCInteropCallableAnnotation } && - second.any { it.type.classifierId.isObjCInteropCallableAnnotation } - ) objCCallableAnnotation else null + private fun commonizedObjcCallableAnnotation(firstAnnotations: List, secondAnnotations: List): List { + 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 { diff --git a/native/commonizer/testData/functionCommonization/valueParameters/commonized/common/package_root.kt b/native/commonizer/testData/functionCommonization/valueParameters/commonized/common/package_root.kt index 08f63a01ca6..77454c6b166 100644 --- a/native/commonizer/testData/functionCommonization/valueParameters/commonized/common/package_root.kt +++ b/native/commonizer/testData/functionCommonization/valueParameters/commonized/common/package_root.kt @@ -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) diff --git a/native/commonizer/testData/functionCommonization/valueParameters/dependency/common/package_kotlinx_cinterop.kt b/native/commonizer/testData/functionCommonization/valueParameters/dependency/common/package_kotlinx_cinterop.kt index db8c0319224..1e830fe8aac 100644 --- a/native/commonizer/testData/functionCommonization/valueParameters/dependency/common/package_kotlinx_cinterop.kt +++ b/native/commonizer/testData/functionCommonization/valueParameters/dependency/common/package_kotlinx_cinterop.kt @@ -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) diff --git a/native/commonizer/testData/functionCommonization/valueParameters/original/ios/package_root.kt b/native/commonizer/testData/functionCommonization/valueParameters/original/ios/package_root.kt index 1106353d94b..7568f5de339 100644 --- a/native/commonizer/testData/functionCommonization/valueParameters/original/ios/package_root.kt +++ b/native/commonizer/testData/functionCommonization/valueParameters/original/ios/package_root.kt @@ -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) { } diff --git a/native/commonizer/testData/functionCommonization/valueParameters/original/macos/package_root.kt b/native/commonizer/testData/functionCommonization/valueParameters/original/macos/package_root.kt index a2400441e69..04e16555741 100644 --- a/native/commonizer/testData/functionCommonization/valueParameters/original/macos/package_root.kt +++ b/native/commonizer/testData/functionCommonization/valueParameters/original/macos/package_root.kt @@ -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) { } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalCInteropCallableAnnotationCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalCInteropCallableAnnotationCommonizationTest.kt index 342f087fd9c..5f7a54c15ff 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalCInteropCallableAnnotationCommonizationTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/HierarchicalCInteropCallableAnnotationCommonizationTest.kt @@ -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" )