diff --git a/compiler/testData/diagnostics/nativeTests/objCName.kt b/compiler/testData/diagnostics/nativeTests/objCName.kt new file mode 100644 index 00000000000..99fc9cf5cc9 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/objCName.kt @@ -0,0 +1,179 @@ +// FILE: kotlin.kt +package kotlin.native + +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.FUNCTION +) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +public annotation class ObjCName(val name: String = "", val swiftName: String = "", val exact: Boolean = false) + +// FILE: test.kt +@ObjCName("ObjCClass", "SwiftClass") +open class KotlinClass { + @ObjCName("objCProperty") + open var kotlinProperty: Int = 0 + @ObjCName(swiftName = "swiftFunction") + open fun @receiver:ObjCName("objCReceiver") Int.kotlinFunction( + @ObjCName("objCParam") kotlinParam: Int + ): Int = this + kotlinParam +} + +@ObjCName("ObjCSubClass", "SwiftSubClass") +class KotlinSubClass: KotlinClass() { + @ObjCName("objCProperty") + override var kotlinProperty: Int = 1 + @ObjCName(swiftName = "swiftFunction") + override fun @receiver:ObjCName("objCReceiver") Int.kotlinFunction( + @ObjCName("objCParam") kotlinParam: Int + ): Int = this + kotlinParam * 2 +} + +@ObjCName() +val invalidObjCName: Int = 0 + +@ObjCName("validName", "invalid.name") +val invalidCharactersObjCNameA: Int = 0 + +@ObjCName("invalid.name", "validName") +val invalidCharactersObjCNameB: Int = 0 + +@ObjCName("validName1", "1validName") +val invalidFirstCharacterObjCNameA: Int = 0 + +@ObjCName("1validName", "validName1") +val invalidFirstCharacterObjCNameB: Int = 0 + +@ObjCName(swiftName = "SwiftMissingExactName", exact = true) +class MissingExactName + +interface KotlinInterfaceA { + @ObjCName("objCPropertyA", "swiftPropertyA") + var kotlinPropertyA: Int + @ObjCName("objCPropertyB", "swiftPropertyB") + var kotlinPropertyB: Int + @ObjCName("objCPropertyB") + var kotlinPropertyC: Int + @ObjCName(swiftName ="swiftPropertyB") + var kotlinPropertyD: Int + var kotlinPropertyE: Int + var kotlinPropertyF: Int + + @ObjCName("objCFunctionA", "swiftFunctionA") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionA( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionB", "swiftFunctionB") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionB( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionC", "swiftFunctionC") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionC( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionD", "swiftFunctionD") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionD( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionE", "swiftFunctionE") + fun Int.kotlinFunctionE(@ObjCName("objCParam", "swiftParam") kotlinParam: Int): Int +} + +interface KotlinInterfaceB { + @ObjCName("objCPropertyA", "swiftPropertyA") + var kotlinPropertyA: Int + @ObjCName("objCPropertyBB", "swiftPropertyB") + var kotlinPropertyB: Int + @ObjCName(swiftName ="swiftPropertyC") + var kotlinPropertyC: Int + @ObjCName("objCPropertyD") + var kotlinPropertyD: Int + @ObjCName("objCPropertyE") + var kotlinPropertyE: Int + var kotlinPropertyF: Int + + @ObjCName("objCFunctionA", "swiftFunctionA") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionA( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionBB", "swiftFunctionB") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionB( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionC", "swiftFunctionC") + fun @receiver:ObjCName("objCReceiverC", "swiftReceiver") Int.kotlinFunctionC( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int + @ObjCName("objCFunctionD", "swiftFunctionD") + fun @receiver:ObjCName("objCReceiver", "swiftReceiver") Int.kotlinFunctionD( + @ObjCName("objCParamD", "swiftParam") kotlinParam: Int + ): Int + fun @receiver:ObjCName("objCFunctionE", "swiftFunctionE") Int.kotlinFunctionE( + @ObjCName("objCParam", "swiftParam") kotlinParam: Int + ): Int +} + +class KotlinOverrideClass: KotlinInterfaceA, KotlinInterfaceB { + override var kotlinPropertyA: Int = 0 + override var kotlinPropertyB: Int = 0 + override var kotlinPropertyC: Int = 0 + override var kotlinPropertyD: Int = 0 + override var kotlinPropertyE: Int = 0 + override var kotlinPropertyF: Int = 0 + + override fun Int.kotlinFunctionA(kotlinParam: Int): Int = this + kotlinParam + override fun Int.kotlinFunctionB(kotlinParam: Int): Int = this + kotlinParam + override fun Int.kotlinFunctionC(kotlinParam: Int): Int = this + kotlinParam + override fun Int.kotlinFunctionD(kotlinParam: Int): Int = this + kotlinParam + override fun Int.kotlinFunctionE(kotlinParam: Int): Int = this + kotlinParam +} + +@ObjCName("ObjCExactChecks", exact = true) +class ExactChecks { + @ObjCName("objCProperty", exact = true) + var property: Int = 0 + @ObjCName("objCFunction", exact = true) + fun @receiver:ObjCName("objCReceiver", exact = true) Int.function( + @ObjCName("objCParam", exact = true) param: Int + ): Int = this * param +} + +@ObjCName("ObjCEnumExactChecks", exact = true) +enum class EnumExactChecks { + @ObjCName("objCEntryOne", exact = true) + ENTRY_ONE, + @ObjCName("objCEntryTwo") + ENTRY_TWO +} + +open class Base { + @ObjCName("foo1") + open fun foo() {} +} + +interface I { + @ObjCName("foo2") + fun foo() +} + +open class Derived : Base(), I + +open class Derived2 : Derived() { + override fun foo() {} +} + +private const val exact = false +private const val objcName = "nonLiteralArgsObjC" + +@ObjCName( + objcName, + "nonLiteralArgs" + "Swift", + exact +) +val nonLiteralArgs: Int = 0 + +@ObjCName("invalidArgsObjC", false, "not a boolean") +val invalidArgs: Int = 0 diff --git a/compiler/testData/diagnostics/nativeTests/objCName.txt b/compiler/testData/diagnostics/nativeTests/objCName.txt new file mode 100644 index 00000000000..cb06e56ea0c --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/objCName.txt @@ -0,0 +1,163 @@ +package + +private const val exact: kotlin.Boolean = false +@kotlin.native.ObjCName(exact = "not a boolean", name = "invalidArgsObjC", swiftName = false) public val invalidArgs: kotlin.Int = 0 +@kotlin.native.ObjCName(name = "validName", swiftName = "invalid.name") public val invalidCharactersObjCNameA: kotlin.Int = 0 +@kotlin.native.ObjCName(name = "invalid.name", swiftName = "validName") public val invalidCharactersObjCNameB: kotlin.Int = 0 +@kotlin.native.ObjCName(name = "validName1", swiftName = "1validName") public val invalidFirstCharacterObjCNameA: kotlin.Int = 0 +@kotlin.native.ObjCName(name = "1validName", swiftName = "validName1") public val invalidFirstCharacterObjCNameB: kotlin.Int = 0 +@kotlin.native.ObjCName public val invalidObjCName: kotlin.Int = 0 +@kotlin.native.ObjCName(exact = false, name = "nonLiteralArgsObjC", swiftName = "nonLiteralArgsSwift") public val nonLiteralArgs: kotlin.Int = 0 +private const val objcName: kotlin.String = "nonLiteralArgsObjC" + +public open class Base { + public constructor Base() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.native.ObjCName(name = "foo1") public open fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class Derived : Base, I { + public constructor Derived() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.native.ObjCName(name = "foo1") public open override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class Derived2 : Derived { + public constructor Derived2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.native.ObjCName(exact = true, name = "ObjCEnumExactChecks") public final enum class EnumExactChecks : kotlin.Enum { + @kotlin.native.ObjCName(exact = true, name = "objCEntryOne") enum entry ENTRY_ONE + + @kotlin.native.ObjCName(name = "objCEntryTwo") enum entry ENTRY_TWO + + private constructor EnumExactChecks() + @kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: EnumExactChecks): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): EnumExactChecks + public final /*synthesized*/ fun values(): kotlin.Array +} + +@kotlin.native.ObjCName(exact = true, name = "ObjCExactChecks") public final class ExactChecks { + public constructor ExactChecks() + @kotlin.native.ObjCName(exact = true, name = "objCProperty") public final var property: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.native.ObjCName(exact = true, name = "objCFunction") public final fun @receiver:kotlin.native.ObjCName(exact = true, name = "objCReceiver") kotlin.Int.function(/*0*/ @kotlin.native.ObjCName(exact = true, name = "objCParam") param: kotlin.Int): kotlin.Int +} + +public interface I { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.native.ObjCName(name = "foo2") public abstract fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.native.ObjCName(name = "ObjCClass", swiftName = "SwiftClass") public open class KotlinClass { + public constructor KotlinClass() + @kotlin.native.ObjCName(name = "objCProperty") public open var kotlinProperty: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.native.ObjCName(swiftName = "swiftFunction") public open fun @receiver:kotlin.native.ObjCName(name = "objCReceiver") kotlin.Int.kotlinFunction(/*0*/ @kotlin.native.ObjCName(name = "objCParam") kotlinParam: kotlin.Int): kotlin.Int +} + +public interface KotlinInterfaceA { + @kotlin.native.ObjCName(name = "objCPropertyA", swiftName = "swiftPropertyA") public abstract var kotlinPropertyA: kotlin.Int + @kotlin.native.ObjCName(name = "objCPropertyB", swiftName = "swiftPropertyB") public abstract var kotlinPropertyB: kotlin.Int + @kotlin.native.ObjCName(name = "objCPropertyB") public abstract var kotlinPropertyC: kotlin.Int + @kotlin.native.ObjCName(swiftName = "swiftPropertyB") public abstract var kotlinPropertyD: kotlin.Int + public abstract var kotlinPropertyE: kotlin.Int + public abstract var kotlinPropertyF: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.native.ObjCName(name = "objCFunctionA", swiftName = "swiftFunctionA") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionA(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionB", swiftName = "swiftFunctionB") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionB(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionC", swiftName = "swiftFunctionC") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionC(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionD", swiftName = "swiftFunctionD") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionD(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionE", swiftName = "swiftFunctionE") public abstract fun kotlin.Int.kotlinFunctionE(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int +} + +public interface KotlinInterfaceB { + @kotlin.native.ObjCName(name = "objCPropertyA", swiftName = "swiftPropertyA") public abstract var kotlinPropertyA: kotlin.Int + @kotlin.native.ObjCName(name = "objCPropertyBB", swiftName = "swiftPropertyB") public abstract var kotlinPropertyB: kotlin.Int + @kotlin.native.ObjCName(swiftName = "swiftPropertyC") public abstract var kotlinPropertyC: kotlin.Int + @kotlin.native.ObjCName(name = "objCPropertyD") public abstract var kotlinPropertyD: kotlin.Int + @kotlin.native.ObjCName(name = "objCPropertyE") public abstract var kotlinPropertyE: kotlin.Int + public abstract var kotlinPropertyF: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.native.ObjCName(name = "objCFunctionA", swiftName = "swiftFunctionA") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionA(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionBB", swiftName = "swiftFunctionB") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionB(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionC", swiftName = "swiftFunctionC") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiverC", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionC(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + @kotlin.native.ObjCName(name = "objCFunctionD", swiftName = "swiftFunctionD") public abstract fun @receiver:kotlin.native.ObjCName(name = "objCReceiver", swiftName = "swiftReceiver") kotlin.Int.kotlinFunctionD(/*0*/ @kotlin.native.ObjCName(name = "objCParamD", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int + public abstract fun @receiver:kotlin.native.ObjCName(name = "objCFunctionE", swiftName = "swiftFunctionE") kotlin.Int.kotlinFunctionE(/*0*/ @kotlin.native.ObjCName(name = "objCParam", swiftName = "swiftParam") kotlinParam: kotlin.Int): kotlin.Int +} + +public final class KotlinOverrideClass : KotlinInterfaceA, KotlinInterfaceB { + public constructor KotlinOverrideClass() + public open override /*2*/ var kotlinPropertyA: kotlin.Int + public open override /*2*/ var kotlinPropertyB: kotlin.Int + public open override /*2*/ var kotlinPropertyC: kotlin.Int + public open override /*2*/ var kotlinPropertyD: kotlin.Int + public open override /*2*/ var kotlinPropertyE: kotlin.Int + public open override /*2*/ var kotlinPropertyF: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + public open override /*2*/ fun kotlin.Int.kotlinFunctionA(/*0*/ kotlinParam: kotlin.Int): kotlin.Int + public open override /*2*/ fun kotlin.Int.kotlinFunctionB(/*0*/ kotlinParam: kotlin.Int): kotlin.Int + public open override /*2*/ fun kotlin.Int.kotlinFunctionC(/*0*/ kotlinParam: kotlin.Int): kotlin.Int + public open override /*2*/ fun kotlin.Int.kotlinFunctionD(/*0*/ kotlinParam: kotlin.Int): kotlin.Int + public open override /*2*/ fun kotlin.Int.kotlinFunctionE(/*0*/ kotlinParam: kotlin.Int): kotlin.Int +} + +@kotlin.native.ObjCName(name = "ObjCSubClass", swiftName = "SwiftSubClass") public final class KotlinSubClass : KotlinClass { + public constructor KotlinSubClass() + @kotlin.native.ObjCName(name = "objCProperty") public open override /*1*/ var kotlinProperty: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.native.ObjCName(swiftName = "swiftFunction") public open override /*1*/ fun @receiver:kotlin.native.ObjCName(name = "objCReceiver") kotlin.Int.kotlinFunction(/*0*/ @kotlin.native.ObjCName(name = "objCParam") kotlinParam: kotlin.Int): kotlin.Int +} + +@kotlin.native.ObjCName(exact = true, swiftName = "SwiftMissingExactName") public final class MissingExactName { + public constructor MissingExactName() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +package kotlin { + + package kotlin.native { + + @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented public final annotation class ObjCName : kotlin.Annotation { + public constructor ObjCName(/*0*/ name: kotlin.String = ..., /*1*/ swiftName: kotlin.String = ..., /*2*/ exact: kotlin.Boolean = ...) + public final val exact: kotlin.Boolean + public final val name: kotlin.String + public final val swiftName: kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java index a91d2fdb2e3..43291945d8c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java @@ -30,6 +30,12 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes runTest("compiler/testData/diagnostics/nativeTests/identifiers.kt"); } + @Test + @TestMetadata("objCName.kt") + public void testObjCName() throws Exception { + runTest("compiler/testData/diagnostics/nativeTests/objCName.kt"); + } + @Test @TestMetadata("sharedImmutable.kt") public void testSharedImmutable() throws Exception { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt index 4bcc4312d83..f10c7c25822 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt @@ -37,4 +37,5 @@ object KonanFqNames { val gcUnsafeCall = FqName("kotlin.native.internal.GCUnsafeCall") val eagerInitialization = FqName("kotlin.native.EagerInitialization") val noReorderFields = FqName("kotlin.native.internal.NoReorderFields") + val objCName = FqName("kotlin.native.ObjCName") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 2ed6144441c..187233d7104 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -387,9 +387,10 @@ internal class ObjCExportTranslatorImpl( descriptor.enumEntries.forEach { val entryName = namer.getEnumEntrySelector(it) + val swiftName = namer.getEnumEntrySwiftName(it) add { ObjCProperty(entryName, it, type, listOf("class", "readonly"), - declarationAttributes = listOf(swiftNameAttribute(entryName))) + declarationAttributes = listOf(swiftNameAttribute(swiftName))) } } @@ -589,7 +590,8 @@ internal class ObjCExportTranslatorImpl( val getterBridge = mapper.bridgeMethod(baseProperty.getter!!) val type = mapReturnType(getterBridge.returnBridge, property.getter!!, objCExportScope) - val name = namer.getPropertyName(baseProperty) + val propertyName = namer.getPropertyName(baseProperty) + val name = propertyName.objCName val attributes = mutableListOf() @@ -611,7 +613,7 @@ internal class ObjCExportTranslatorImpl( val getterSelector = getSelector(baseProperty.getter!!) val getterName: String? = if (getterSelector != name) getterSelector else null - val declarationAttributes = mutableListOf(swiftNameAttribute(name)) + val declarationAttributes = mutableListOf(swiftNameAttribute(propertyName.swiftName)) declarationAttributes.addIfNotNull(mapper.getDeprecation(property)?.toDeprecationAttribute()) val visibilityComments = visibilityComments(property.visibility, "property") @@ -804,7 +806,7 @@ internal class ObjCExportTranslatorImpl( } } - private val mustBeDocumentedAnnotationsStopList = setOf(StandardNames.FqNames.deprecated) + private val mustBeDocumentedAnnotationsStopList = setOf(StandardNames.FqNames.deprecated, KonanFqNames.objCName) private fun mustBeDocumentedAnnotations(annotations: Annotations): List { return annotations.mapNotNull { it -> it.annotationClass?.let { annotationClass -> @@ -1404,8 +1406,11 @@ internal fun ClassDescriptor.needCompanionObjectProperty(namer: ObjCExportNamer, val companionObject = companionObjectDescriptor if (companionObject == null || !mapper.shouldBeExposed(companionObject)) return false - if (kind == ClassKind.ENUM_CLASS && enumEntries.any { namer.getEnumEntrySelector(it) == ObjCExportNamer.companionObjectPropertyName }) - return false // 'companion' property would clash with enum entry, don't generate it. + if (kind == ClassKind.ENUM_CLASS && enumEntries.any { + namer.getEnumEntrySelector(it) == ObjCExportNamer.companionObjectPropertyName || + namer.getEnumEntrySwiftName(it) == ObjCExportNamer.companionObjectPropertyName + } + ) return false // 'companion' property would clash with enum entry, don't generate it. return true } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt index e6e485148cf..b0f4c9116e3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.common.serialization.findSourceFile +import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport import org.jetbrains.kotlin.backend.konan.cKeywords import org.jetbrains.kotlin.backend.konan.descriptors.isArray import org.jetbrains.kotlin.backend.konan.descriptors.isInterface @@ -24,10 +24,13 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor import org.jetbrains.kotlin.resolve.source.PsiSourceFile +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal interface ObjCExportNameTranslator { fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName @@ -43,6 +46,7 @@ internal interface ObjCExportNameTranslator { interface ObjCExportNamer { data class ClassOrProtocolName(val swiftName: String, val objCName: String, val binaryName: String = objCName) + data class PropertyName(val swiftName: String, val objCName: String) interface Configuration { val topLevelNamePrefix: String @@ -56,9 +60,10 @@ interface ObjCExportNamer { fun getClassOrProtocolName(descriptor: ClassDescriptor): ClassOrProtocolName fun getSelector(method: FunctionDescriptor): String fun getSwiftName(method: FunctionDescriptor): String - fun getPropertyName(property: PropertyDescriptor): String + fun getPropertyName(property: PropertyDescriptor): PropertyName fun getObjectInstanceSelector(descriptor: ClassDescriptor): String fun getEnumEntrySelector(descriptor: ClassDescriptor): String + fun getEnumEntrySwiftName(descriptor: ClassDescriptor): String fun getEnumValuesSelector(descriptor: FunctionDescriptor): String fun getTypeParameterName(typeParameterDescriptor: TypeParameterDescriptor): String @@ -98,7 +103,7 @@ fun createNamer( // Note: this class duplicates some of ObjCExportNamerImpl logic, // but operates on different representation. internal open class ObjCExportNameTranslatorImpl( - configuration: ObjCExportNamer.Configuration + private val configuration: ObjCExportNamer.Configuration ) : ObjCExportNameTranslator { private val helper = ObjCExportNamingHelper(configuration.topLevelNamePrefix, configuration.objcGenerics) @@ -109,30 +114,43 @@ internal open class ObjCExportNameTranslatorImpl( override fun getCategoryName(file: KtFile): String = helper.translateFileName(file) - override fun getClassOrProtocolName( - ktClassOrObject: KtClassOrObject - ): ObjCExportNamer.ClassOrProtocolName = helper.swiftClassNameToObjC( - getClassOrProtocolSwiftName(ktClassOrObject) - ) + override fun getClassOrProtocolName(ktClassOrObject: KtClassOrObject): ObjCExportNamer.ClassOrProtocolName = + ObjCExportNamer.ClassOrProtocolName( + swiftName = getClassOrProtocolAsSwiftName(ktClassOrObject, true), + objCName = buildString { + getClassOrProtocolAsSwiftName(ktClassOrObject, false).split('.').forEachIndexed { index, part -> + append(if (index == 0) part else part.replaceFirstChar(Char::uppercaseChar)) + } + } + ) - private fun getClassOrProtocolSwiftName( - ktClassOrObject: KtClassOrObject + private fun getClassOrProtocolAsSwiftName( + ktClassOrObject: KtClassOrObject, + forSwift: Boolean ): String = buildString { - val outerClass = ktClassOrObject.getStrictParentOfType() - if (outerClass != null) { - appendNameWithContainer(ktClassOrObject, outerClass) + val objCName = ktClassOrObject.getObjCName() + if (objCName.isExact) { + append(objCName.asIdentifier(forSwift)) } else { - append(ktClassOrObject.name!!.toIdentifier()) + val outerClass = ktClassOrObject.getStrictParentOfType() + if (outerClass != null) { + appendNameWithContainer(ktClassOrObject, objCName, outerClass, forSwift) + } else { + if (!forSwift) append(configuration.topLevelNamePrefix) + append(objCName.asIdentifier(forSwift)) + } } } private fun StringBuilder.appendNameWithContainer( ktClassOrObject: KtClassOrObject, - outerClass: KtClassOrObject + objCName: ObjCName, + outerClass: KtClassOrObject, + forSwift: Boolean ) = helper.appendNameWithContainer( this, - ktClassOrObject, ktClassOrObject.name!!.toIdentifier(), - outerClass, getClassOrProtocolSwiftName(outerClass), + ktClassOrObject, objCName.asIdentifier(forSwift), + outerClass, getClassOrProtocolAsSwiftName(outerClass, forSwift), object : ObjCExportNamingHelper.ClassInfoProvider { override fun hasGenerics(clazz: KtClassOrObject): Boolean = clazz.typeParametersWithOuter.count() != 0 @@ -162,14 +180,6 @@ private class ObjCExportNamingHelper( return ObjCExportNamer.ClassOrProtocolName(swiftName = baseName, objCName = "$topLevelNamePrefix$baseName") } - fun swiftClassNameToObjC(swiftName: String): ObjCExportNamer.ClassOrProtocolName = - ObjCExportNamer.ClassOrProtocolName(swiftName, buildString { - append(topLevelNamePrefix) - swiftName.split('.').forEachIndexed { index, part -> - append(if (index == 0) part else part.replaceFirstChar(Char::uppercaseChar)) - } - }) - fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName = getFileClassName(file.name) @@ -324,13 +334,16 @@ internal class ObjCExportNamerImpl( // Note: this condition is correct but can be too strict. } - private val propertyNames = object : Mapping() { + private inner class PropertyNameMapping : Mapping() { override fun reserved(name: String) = name in Reserved.propertyNames override fun conflict(first: PropertyDescriptor, second: PropertyDescriptor): Boolean = !mapper.canHaveSameName(first, second) } + private val objCPropertyNames = PropertyNameMapping() + private val swiftPropertyNames = PropertyNameMapping() + private open inner class GlobalNameMapping : Mapping() { final override fun conflict(first: T, second: T): Boolean = true } @@ -362,11 +375,14 @@ internal class ObjCExportNamerImpl( override fun conflict(first: ClassDescriptor, second: ClassDescriptor) = false } - private val enumClassSelectors = object : ClassSelectorNameMapping() { + private inner class EnumNameMapping : ClassSelectorNameMapping() { override fun conflict(first: DeclarationDescriptor, second: DeclarationDescriptor) = first.containingDeclaration == second.containingDeclaration } + private val enumClassSelectors = EnumNameMapping() + private val enumClassSwiftNames = EnumNameMapping() + override fun getFileClassName(file: SourceFile): ObjCExportNamer.ClassOrProtocolName { val candidate by lazy { val fileName = when (file) { @@ -401,23 +417,29 @@ internal class ObjCExportNamerImpl( descriptor: ClassDescriptor ): String = swiftClassAndProtocolNames.getOrPut(descriptor) { StringBuilder().apply { - val containingDeclaration = descriptor.containingDeclaration - if (containingDeclaration is ClassDescriptor) { - appendNameWithContainer(descriptor, containingDeclaration) - } else if (containingDeclaration is PackageFragmentDescriptor) { - appendTopLevelClassBaseName(descriptor) + val objCName = descriptor.getObjCName() + if (objCName.isExact) { + append(objCName.asIdentifier(true)) } else { - error("unexpected class parent: $containingDeclaration") + val containingDeclaration = descriptor.containingDeclaration + if (containingDeclaration is ClassDescriptor) { + appendSwiftNameWithContainer(descriptor, objCName, containingDeclaration) + } else if (containingDeclaration is PackageFragmentDescriptor) { + appendTopLevelClassBaseName(descriptor, objCName, true) + } else { + error("unexpected class parent: $containingDeclaration") + } } }.mangledBySuffixUnderscores() } - private fun StringBuilder.appendNameWithContainer( + private fun StringBuilder.appendSwiftNameWithContainer( clazz: ClassDescriptor, + objCName: ObjCName, containingClass: ClassDescriptor ) = helper.appendNameWithContainer( this, - clazz, clazz.name.asString().toIdentifier(), + clazz, objCName.asIdentifier(true), containingClass, getClassOrProtocolSwiftName(containingClass), object : ObjCExportNamingHelper.ClassInfoProvider { override fun hasGenerics(clazz: ClassDescriptor): Boolean = @@ -431,25 +453,29 @@ internal class ObjCExportNamerImpl( val objCMapping = if (descriptor.isInterface) objCProtocolNames else objCClassNames return objCMapping.getOrPut(descriptor) { StringBuilder().apply { - val containingDeclaration = descriptor.containingDeclaration - if (containingDeclaration is ClassDescriptor) { - append(getClassOrProtocolObjCName(containingDeclaration)) - .append(descriptor.name.asString().toIdentifier().replaceFirstChar(Char::uppercaseChar)) - - } else if (containingDeclaration is PackageFragmentDescriptor) { - append(topLevelNamePrefix).appendTopLevelClassBaseName(descriptor) + val objCName = descriptor.getObjCName() + if (objCName.isExact) { + append(objCName.asIdentifier(false)) } else { - error("unexpected class parent: $containingDeclaration") + val containingDeclaration = descriptor.containingDeclaration + if (containingDeclaration is ClassDescriptor) { + append(getClassOrProtocolObjCName(containingDeclaration)) + .append(objCName.asIdentifier(false).replaceFirstChar(Char::uppercaseChar)) + } else if (containingDeclaration is PackageFragmentDescriptor) { + append(topLevelNamePrefix).appendTopLevelClassBaseName(descriptor, objCName, false) + } else { + error("unexpected class parent: $containingDeclaration") + } } }.mangledBySuffixUnderscores() } } - private fun StringBuilder.appendTopLevelClassBaseName(descriptor: ClassDescriptor) = apply { + private fun StringBuilder.appendTopLevelClassBaseName(descriptor: ClassDescriptor, objCName: ObjCName, forSwift: Boolean) = apply { configuration.getAdditionalPrefix(descriptor.module)?.let { append(it) } - append(descriptor.name.asString().toIdentifier()) + append(objCName.asIdentifier(forSwift)) } override fun getSelector(method: FunctionDescriptor): String = methodSelectors.getOrPut(method) { @@ -465,12 +491,12 @@ internal class ObjCExportNamerImpl( parameters.forEachIndexed { index, (bridge, it) -> val name = when (bridge) { is MethodBridgeValueParameter.Mapped -> when { - it is ReceiverParameterDescriptor -> "" + it is ReceiverParameterDescriptor -> it.getObjCName().asIdentifier(false) { "" } method is PropertySetterDescriptor -> when (parameters.size) { 1 -> "" else -> "value" } - else -> it!!.name.asString().toIdentifier() + else -> it!!.getObjCName().asIdentifier(false) } MethodBridgeValueParameter.ErrorOutParameter -> "error" is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler" @@ -479,11 +505,8 @@ internal class ObjCExportNamerImpl( if (index == 0) { append(when { bridge is MethodBridgeValueParameter.ErrorOutParameter -> "AndReturn" - bridge is MethodBridgeValueParameter.SuspendCompletion -> "With" - method is ConstructorDescriptor -> "With" - else -> "" }) append(name.replaceFirstChar(Char::uppercaseChar)) @@ -518,12 +541,12 @@ internal class ObjCExportNamerImpl( parameters@ for ((bridge, it) in parameters) { val label = when (bridge) { is MethodBridgeValueParameter.Mapped -> when { - it is ReceiverParameterDescriptor -> "_" + it is ReceiverParameterDescriptor -> it.getObjCName().asIdentifier(true) { "_" } method is PropertySetterDescriptor -> when (parameters.size) { 1 -> "_" else -> "value" } - else -> it!!.name.asString().toIdentifier() + else -> it!!.getObjCName().asIdentifier(true) } MethodBridgeValueParameter.ErrorOutParameter -> continue@parameters is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler" @@ -549,38 +572,57 @@ internal class ObjCExportNamerImpl( } } - override fun getPropertyName(property: PropertyDescriptor): String = propertyNames.getOrPut(property) { + override fun getPropertyName(property: PropertyDescriptor): ObjCExportNamer.PropertyName { assert(mapper.isBaseProperty(property)) assert(mapper.isObjCProperty(property)) - - StringBuilder().apply { - append(property.name.asString().toIdentifier()) - }.mangledSequence { - append('_') + val objCName = property.getObjCName() + fun PropertyNameMapping.getOrPut(forSwift: Boolean) = getOrPut(property) { + StringBuilder().apply { + append(objCName.asIdentifier(forSwift)) + }.mangledSequence { + append('_') + } } + return ObjCExportNamer.PropertyName( + swiftName = swiftPropertyNames.getOrPut(true), + objCName = objCPropertyNames.getOrPut(false) + ) } override fun getObjectInstanceSelector(descriptor: ClassDescriptor): String { assert(descriptor.kind == ClassKind.OBJECT) return objectInstanceSelectors.getOrPut(descriptor) { - val name = descriptor.name.asString().replaceFirstChar(Char::lowercaseChar).toIdentifier().mangleIfSpecialFamily("get") - + val name = descriptor.getObjCName().asString(false) + .replaceFirstChar(Char::lowercaseChar).toIdentifier().mangleIfSpecialFamily("get") StringBuilder(name).mangledBySuffixUnderscores() } } + private fun ClassDescriptor.getEnumEntryName(forSwift: Boolean): Sequence { + val name = getObjCName().asIdentifier(forSwift) { + // FOO_BAR_BAZ -> fooBarBaz: + it.split('_').mapIndexed { index, s -> + val lower = s.lowercase() + if (index == 0) lower else lower.replaceFirstChar(Char::uppercaseChar) + }.joinToString("").toIdentifier() + }.mangleIfSpecialFamily("the") + return StringBuilder(name).mangledBySuffixUnderscores() + } + override fun getEnumEntrySelector(descriptor: ClassDescriptor): String { assert(descriptor.kind == ClassKind.ENUM_ENTRY) return enumClassSelectors.getOrPut(descriptor) { - // FOO_BAR_BAZ -> fooBarBaz: - val name = descriptor.name.asString().split('_').mapIndexed { index, s -> - val lower = s.lowercase() - if (index == 0) lower else lower.replaceFirstChar(Char::uppercaseChar) - }.joinToString("").toIdentifier().mangleIfSpecialFamily("the") + descriptor.getEnumEntryName(false) + } + } - StringBuilder(name).mangledBySuffixUnderscores() + override fun getEnumEntrySwiftName(descriptor: ClassDescriptor): String { + assert(descriptor.kind == ClassKind.ENUM_ENTRY) + + return enumClassSwiftNames.getOrPut(descriptor) { + descriptor.getEnumEntryName(true) } } @@ -677,10 +719,12 @@ internal class ObjCExportNamerImpl( } val candidate = when (this) { - is PropertyGetterDescriptor -> this.correspondingProperty.name.asString() - is PropertySetterDescriptor -> "set${this.correspondingProperty.name.asString().replaceFirstChar(kotlin.Char::uppercaseChar)}" - else -> this.name.asString() - }.toIdentifier() + is PropertyGetterDescriptor -> this.correspondingProperty.getObjCName().asIdentifier(forSwift) + is PropertySetterDescriptor -> "set${ + this.correspondingProperty.getObjCName().asString(forSwift).replaceFirstChar(kotlin.Char::uppercaseChar) + }".toIdentifier() + else -> this.getObjCName().asIdentifier(forSwift) + } return candidate.mangleIfSpecialFamily("do") } @@ -902,6 +946,86 @@ private fun ObjCExportMapper.canHaveSameName(first: PropertyDescriptor, second: return bridgePropertyType(first) == bridgePropertyType(second) } +private class ObjCName( + private val kotlinName: String, + private val objCName: String?, + private val swiftName: String?, + val isExact: Boolean +) { + // TODO: Prevent mangling when objCName or swiftName is provided + + fun asString(forSwift: Boolean): String = swiftName.takeIf { forSwift } ?: objCName ?: kotlinName + + fun asIdentifier(forSwift: Boolean, default: (String) -> String = { it.toIdentifier() }): String = + swiftName.takeIf { forSwift } ?: objCName ?: default(kotlinName) +} + +private fun DeclarationDescriptor.getObjCName(): ObjCName { + var objCName: String? = null + var swiftName: String? = null + var isExact = false + annotations.findAnnotation(KonanFqNames.objCName)?.let { annotation -> + objCName = annotation.argumentValue("name")?.value?.cast() + swiftName = annotation.argumentValue("swiftName")?.value?.cast() + isExact = annotation.argumentValue("exact")?.value?.cast() ?: false + } + return ObjCName(name.asString(), objCName, swiftName, isExact) +} + +private fun T.upcast(): T = this + +private fun CallableDescriptor.getObjCName(): ObjCName = + overriddenDescriptors.firstOrNull()?.getObjCName() ?: upcast().getObjCName() + +private fun ParameterDescriptor.getObjCName(): ObjCName { + val callableDescriptor = containingDeclaration as? CallableDescriptor ?: return upcast().getObjCName() + fun CallableDescriptor.getBase(): CallableDescriptor = overriddenDescriptors.firstOrNull()?.getBase() ?: this + val baseCallableDescriptor = callableDescriptor.getBase() + if (callableDescriptor.extensionReceiverParameter == this) { + return baseCallableDescriptor.extensionReceiverParameter!!.upcast().getObjCName() + } + val parameterIndex = callableDescriptor.valueParameters.indexOf(this) + if (parameterIndex != -1) { + return baseCallableDescriptor.valueParameters[parameterIndex].upcast().getObjCName() + } + error("Unexpected parameter: $this") +} + +private val objCNameShortName = KonanFqNames.objCName.shortName().asString() + +private fun KtClassOrObject.getObjCName(): ObjCName { + var objCName: String? = null + var swiftName: String? = null + var isExact = false + annotationEntries.firstOrNull { + it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == objCNameShortName + }?.let { annotation -> + fun ValueArgument.getStringValue(): String? { + val stringTemplateExpression = when (this) { + is KtValueArgument -> stringTemplateExpression + else -> getArgumentExpression() as? KtStringTemplateExpression + } ?: return null + return (stringTemplateExpression.entries.singleOrNull() as? KtLiteralStringTemplateEntry)?.text + } + + fun ValueArgument.getBooleanValue(): Boolean = + (getArgumentExpression() as? KtConstantExpression)?.text?.toBooleanStrictOrNull() ?: false + + val argNames = setOf("name", "swiftName", "exact") + val processedArgs = mutableSetOf() + for (argument in annotation.valueArguments) { + val argName = argument.getArgumentName()?.asName?.asString() ?: (argNames - processedArgs).firstOrNull() ?: break + when (argName) { + "name" -> objCName = argument.getStringValue() + "swiftName" -> swiftName = argument.getStringValue() + "exact" -> isExact = argument.getBooleanValue() + } + processedArgs.add(argName) + } + } + return ObjCName(name!!, objCName, swiftName, isExact) +} + internal val ModuleDescriptor.objCExportAdditionalNamePrefix: String get() { if (this.isNativeStdlib()) return "Kotlin" diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h index f5c3c0c449d..7c22fc2c4b5 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h @@ -1212,6 +1212,132 @@ __attribute__((swift_name("NoAutoreleaseKt"))) + (void)useIntArrayArray:(KtKotlinIntArray *)array __attribute__((swift_name("useIntArray(array:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1A"))) +@interface KtObjCNameC1A : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameAKt"))) +@interface KtObjCNameAKt : KtBase ++ (NSString *)withUserId:(NSString *)userId __attribute__((swift_name("with(userId:)"))); ++ (BOOL)supportsFeatures:(BOOL)features __attribute__((swift_name("supports(_:)"))); ++ (NSString *)scanForPeripheralsWithServices:(int32_t)serviceUUIDs options:(NSString *)options __attribute__((swift_name("scanForPeripherals(withServices:options:)"))); ++ (NSString *)registerForConnectionEventsWithOptions:(NSString *)options __attribute__((swift_name("registerForConnectionEvents(options:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1B"))) +@interface KtObjCNameC1B : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("MySwiftArray"))) +@interface KtMyObjCArray : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)indexOfObject:(int32_t)element __attribute__((swift_name("index(of:)"))); +@property (readonly) int32_t count __attribute__((swift_name("count"))); +@end + +__attribute__((swift_name("ObjCNameI1"))) +@protocol KtObjCNameI1 +@required +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property (readonly) int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2"))) +@interface KtObjCNameC2 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2.SwiftNestedClass"))) +@interface KtObjCNameC2ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftExactNestedClass"))) +@interface ObjCExactNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3"))) +@interface ObjCNameC3 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3.SwiftNestedClass"))) +@interface ObjCNameC3ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC4"))) +@interface KtObjCNameC4 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)fooObjCReceiver:(int32_t)receiver objCParam:(int32_t)param __attribute__((swift_name("foo(objCReceiver:objCParam:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftObject"))) +@interface KtObjCNameObjCObject : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)objCNameObjCObject __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCObject *shared __attribute__((swift_name("shared"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum"))) +@interface KtObjCNameObjCEnum : KtKotlinEnum ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); +- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); +@property (class, readonly) KtObjCNameObjCEnum *objcOne __attribute__((swift_name("swiftOne"))); +@property (class, readonly) KtObjCNameObjCEnum *objcTwo __attribute__((swift_name("companion"))); +@property (class, readonly) KtObjCNameObjCEnum *objcThree __attribute__((swift_name("swiftThree"))); ++ (KtKotlinArray *)values __attribute__((swift_name("values()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum.Companion"))) +@interface KtObjCNameObjCEnumCompanion : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCEnumCompanion *shared __attribute__((swift_name("shared"))); +- (int32_t)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameBKt"))) +@interface KtObjCNameBKt : KtBase ++ (int32_t)getSomeValueOf:(id)receiver __attribute__((swift_name("getSomeValue(of:)"))); +@end + __attribute__((swift_name("OverrideKotlinMethods2"))) @protocol KtOverrideKotlinMethods2 @required diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h index ff9f04318c6..861fb384e62 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h @@ -1147,6 +1147,132 @@ __attribute__((swift_name("NoAutoreleaseKt"))) + (void)useIntArrayArray:(KtKotlinIntArray *)array __attribute__((swift_name("useIntArray(array:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1A"))) +@interface KtObjCNameC1A : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameAKt"))) +@interface KtObjCNameAKt : KtBase ++ (NSString *)withUserId:(NSString *)userId __attribute__((swift_name("with(userId:)"))); ++ (BOOL)supportsFeatures:(BOOL)features __attribute__((swift_name("supports(_:)"))); ++ (NSString *)scanForPeripheralsWithServices:(int32_t)serviceUUIDs options:(NSString *)options __attribute__((swift_name("scanForPeripherals(withServices:options:)"))); ++ (NSString *)registerForConnectionEventsWithOptions:(NSString *)options __attribute__((swift_name("registerForConnectionEvents(options:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1B"))) +@interface KtObjCNameC1B : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("MySwiftArray"))) +@interface KtMyObjCArray : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)indexOfObject:(int32_t)element __attribute__((swift_name("index(of:)"))); +@property (readonly) int32_t count __attribute__((swift_name("count"))); +@end + +__attribute__((swift_name("ObjCNameI1"))) +@protocol KtObjCNameI1 +@required +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property (readonly) int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2"))) +@interface KtObjCNameC2 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2.SwiftNestedClass"))) +@interface KtObjCNameC2ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftExactNestedClass"))) +@interface ObjCExactNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3"))) +@interface ObjCNameC3 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3.SwiftNestedClass"))) +@interface ObjCNameC3ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC4"))) +@interface KtObjCNameC4 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)fooObjCReceiver:(int32_t)receiver objCParam:(int32_t)param __attribute__((swift_name("foo(objCReceiver:objCParam:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftObject"))) +@interface KtObjCNameObjCObject : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)objCNameObjCObject __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCObject *shared __attribute__((swift_name("shared"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum"))) +@interface KtObjCNameObjCEnum : KtKotlinEnum ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); +- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); +@property (class, readonly) KtObjCNameObjCEnum *objcOne __attribute__((swift_name("swiftOne"))); +@property (class, readonly) KtObjCNameObjCEnum *objcTwo __attribute__((swift_name("companion"))); +@property (class, readonly) KtObjCNameObjCEnum *objcThree __attribute__((swift_name("swiftThree"))); ++ (KtKotlinArray *)values __attribute__((swift_name("values()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum.Companion"))) +@interface KtObjCNameObjCEnumCompanion : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCEnumCompanion *shared __attribute__((swift_name("shared"))); +- (int32_t)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameBKt"))) +@interface KtObjCNameBKt : KtBase ++ (int32_t)getSomeValueOf:(id)receiver __attribute__((swift_name("getSomeValue(of:)"))); +@end + __attribute__((swift_name("OverrideKotlinMethods2"))) @protocol KtOverrideKotlinMethods2 @required diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h index fd25ff702f3..5f79fe6f49d 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h @@ -1147,6 +1147,132 @@ __attribute__((swift_name("NoAutoreleaseKt"))) + (void)useIntArrayArray:(KtKotlinIntArray *)array __attribute__((swift_name("useIntArray(array:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1A"))) +@interface KtObjCNameC1A : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameAKt"))) +@interface KtObjCNameAKt : KtBase ++ (NSString *)withUserId:(NSString *)userId __attribute__((swift_name("with(userId:)"))); ++ (BOOL)supportsFeatures:(BOOL)features __attribute__((swift_name("supports(_:)"))); ++ (NSString *)scanForPeripheralsWithServices:(int32_t)serviceUUIDs options:(NSString *)options __attribute__((swift_name("scanForPeripherals(withServices:options:)"))); ++ (NSString *)registerForConnectionEventsWithOptions:(NSString *)options __attribute__((swift_name("registerForConnectionEvents(options:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC1B"))) +@interface KtObjCNameC1B : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (NSString *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("MySwiftArray"))) +@interface KtMyObjCArray : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)indexOfObject:(int32_t)element __attribute__((swift_name("index(of:)"))); +@property (readonly) int32_t count __attribute__((swift_name("count"))); +@end + +__attribute__((swift_name("ObjCNameI1"))) +@protocol KtObjCNameI1 +@required +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property (readonly) int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2"))) +@interface KtObjCNameC2 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)someOtherFunctionReceiver:(int32_t)receiver otherParam:(int32_t)param __attribute__((swift_name("someOtherFunction(receiver:otherParam:)"))); +@property int32_t someOtherValue __attribute__((swift_name("someOtherValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC2.SwiftNestedClass"))) +@interface KtObjCNameC2ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftExactNestedClass"))) +@interface ObjCExactNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3"))) +@interface ObjCNameC3 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameC3.SwiftNestedClass"))) +@interface ObjCNameC3ObjCNestedClass : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@property int32_t nestedValue __attribute__((swift_name("nestedValue"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameC4"))) +@interface KtObjCNameC4 : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)fooObjCReceiver:(int32_t)receiver objCParam:(int32_t)param __attribute__((swift_name("foo(objCReceiver:objCParam:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftObject"))) +@interface KtObjCNameObjCObject : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)objCNameObjCObject __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCObject *shared __attribute__((swift_name("shared"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum"))) +@interface KtObjCNameObjCEnum : KtKotlinEnum ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); +- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); +@property (class, readonly) KtObjCNameObjCEnum *objcOne __attribute__((swift_name("swiftOne"))); +@property (class, readonly) KtObjCNameObjCEnum *objcTwo __attribute__((swift_name("companion"))); +@property (class, readonly) KtObjCNameObjCEnum *objcThree __attribute__((swift_name("swiftThree"))); ++ (KtKotlinArray *)values __attribute__((swift_name("values()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameSwiftEnum.Companion"))) +@interface KtObjCNameObjCEnumCompanion : KtBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) KtObjCNameObjCEnumCompanion *shared __attribute__((swift_name("shared"))); +- (int32_t)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("ObjCNameBKt"))) +@interface KtObjCNameBKt : KtBase ++ (int32_t)getSomeValueOf:(id)receiver __attribute__((swift_name("getSomeValue(of:)"))); +@end + __attribute__((swift_name("OverrideKotlinMethods2"))) @protocol KtOverrideKotlinMethods2 @required diff --git a/kotlin-native/backend.native/tests/objcexport/objCName.swift b/kotlin-native/backend.native/tests/objcexport/objCName.swift new file mode 100644 index 00000000000..32a8f8d35cc --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/objCName.swift @@ -0,0 +1,78 @@ +import Kt + +private func testObjCNameClassNames() throws { + try assertEquals(actual: ObjCNameC1A().foo(), expected: "a") + try assertEquals(actual: ObjCNameC1B().foo(), expected: "b") +} + +private func testObjCNameFunctionName() throws { + try assertEquals(actual: ObjCNameAKt.with(userId: "abc"), expected: "abc") +} + +private func testObjCNameParameterNames() throws { + try assertEquals(actual: ObjCNameAKt.supports(true), expected: true) + try assertEquals(actual: ObjCNameAKt.scanForPeripherals(withServices: 123, options: "abc"), expected: "123 abc") + try assertEquals(actual: ObjCNameAKt.registerForConnectionEvents(options: "abc"), expected: "abc") +} + +private func testObjCNameReceiverName() throws { + let object = SwiftNameC2() + try assertEquals(actual: ObjCNameBKt.getSomeValue(of: object), expected: 0) +} + +private func testObjCNameMySwiftArray() throws { + let array = MySwiftArray() + try assertEquals(actual: array.count, expected: 0) + try assertEquals(actual: array.index(of: 1), expected: 1) +} + +private func testObjCNameOverrides() throws { + let object = SwiftNameC2() + try assertEquals(actual: object.someOtherValue, expected: 0) + object.someOtherValue = 1 + try assertEquals(actual: object.someOtherValue, expected: 1) + try assertEquals(actual: object.someOtherFunction(receiver: 2, otherParam: 4), expected: 8) + try assertEquals(actual: ObjCNameC4().foo(objCReceiver: 3, objCParam: 5), expected: 15) +} + +private func testObjCNameNestedClass() throws { + let object = SwiftNameC2.SwiftNestedClass() + try assertEquals(actual: object.nestedValue, expected: 1) + object.nestedValue = 2 + try assertEquals(actual: object.nestedValue, expected: 2) +} + +private func testObjCNameExact() throws { + try assertEquals(actual: SwiftExactNestedClass().nestedValue, expected: 1) + try assertEquals(actual: SwiftNameC3.SwiftNestedClass().nestedValue, expected: 2) +} + +private func testObjCNameObject() throws { + try assertEquals(actual: ObjCNameSwiftObject.shared, expected: ObjCNameSwiftObject()) +} + +private func testObjCNameEnum() throws { + let enumValues = ObjCNameSwiftEnum.values() + try assertEquals(actual: enumValues.size, expected: 3) + try assertSame(actual: enumValues.get(index: 0) as AnyObject, expected: ObjCNameSwiftEnum.swiftOne) + try assertSame(actual: enumValues.get(index: 1) as AnyObject, expected: ObjCNameSwiftEnum.companion) + try assertSame(actual: enumValues.get(index: 2) as AnyObject, expected: ObjCNameSwiftEnum.swiftThree) + try assertEquals(actual: ObjCNameSwiftEnum.Companion.shared.foo(), expected: 0) +} + +class ObjCNameTests : SimpleTestProvider { + override init() { + super.init() + + test("TestObjCNameClassNames", testObjCNameClassNames) + test("TestObjCNameFunctionName", testObjCNameFunctionName) + test("TestObjCNameParameterNames", testObjCNameParameterNames) + test("TestObjCNameReceiverName", testObjCNameReceiverName) + test("TestObjCNameMySwiftArray", testObjCNameMySwiftArray) + test("TestObjCNameOverrides", testObjCNameOverrides) + test("TestObjCNameNestedClass", testObjCNameNestedClass) + test("TestObjCNameExact", testObjCNameExact) + test("TestObjCNameObject", testObjCNameObject) + test("TestObjCNameEnum", testObjCNameEnum) + } +} diff --git a/kotlin-native/backend.native/tests/objcexport/objCNameA.kt b/kotlin-native/backend.native/tests/objcexport/objCNameA.kt new file mode 100644 index 00000000000..3fe6edd201e --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/objCNameA.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +@file:OptIn(ExperimentalObjCName::class) + +package objCNameA + +import kotlin.experimental.ExperimentalObjCName + +// https://youtrack.jetbrains.com/issue/KT-50767 +@ObjCName("ObjCNameC1A") +class ObjCNameC1 { + fun foo(): String = "a" +} + +// https://youtrack.jetbrains.com/issue/KT-48076 +@ObjCName("with") +fun withUserId(userId: String): String = userId + +// https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/3240586-supports +fun supports(@ObjCName(swiftName = "_") features: Boolean): Boolean = features + +// https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518986-scanforperipherals +fun scanForPeripherals(@ObjCName("withServices") serviceUUIDs: Int, options: String): String = "$serviceUUIDs $options" + +// https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/3174844-registerforconnectionevents +fun registerForConnectionEvents(@ObjCName("withOptions", "options") options: String): String = "$options" diff --git a/kotlin-native/backend.native/tests/objcexport/objCNameB.kt b/kotlin-native/backend.native/tests/objcexport/objCNameB.kt new file mode 100644 index 00000000000..1c3262132df --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/objCNameB.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +@file:OptIn(ExperimentalObjCName::class) + +package objCNameB + +import kotlin.experimental.ExperimentalObjCName + +// https://youtrack.jetbrains.com/issue/KT-50767 +@ObjCName("ObjCNameC1B") +class ObjCNameC1 { + fun foo(): String = "b" +} + +@ObjCName("MyObjCArray", "MySwiftArray") +class MyKotlinArray { + // https://developer.apple.com/documentation/foundation/nsarray/1409982-count + @ObjCName("count") + val size: Int = 0 + // https://developer.apple.com/documentation/foundation/nsarray/1417076-index + @ObjCName(swiftName = "index") + fun indexOf(@ObjCName("object", "of") element: Int): Int = element +} + +interface ObjCNameI1 { + @ObjCName("someOtherValue") + val someValue: Int + @ObjCName("someOtherFunction") + fun @receiver:ObjCName("receiver") Int.someFunction(@ObjCName("otherParam") param: Int): Int +} + +fun @receiver:ObjCName("of") ObjCNameI1.getSomeValue(): Int = someValue + +@ObjCName(swiftName = "SwiftNameC2") +class ObjCNameC2: ObjCNameI1 { + @ObjCName("ObjCNestedClass", "SwiftNestedClass") + class NestedClass { + var nestedValue: Int = 1 + } + + @ObjCName("ObjCExactNestedClass", "SwiftExactNestedClass", true) + class ExactNestedClass { + var nestedValue: Int = 1 + } + + override var someValue: Int = 0 + override fun Int.someFunction(param: Int): Int = this * param +} + +@ObjCName("ObjCNameC3", "SwiftNameC3", true) +class ObjCNameC3 { + @ObjCName("ObjCNestedClass", "SwiftNestedClass") + class NestedClass { + var nestedValue: Int = 2 + } +} + +private interface ObjCNameI2 { + fun @receiver:ObjCName("objCReceiver") Int.foo(@ObjCName("objCParam") param: Int): Int +} + +class ObjCNameC4: ObjCNameI2 { + override fun Int.foo(param: Int): Int = this * param +} + +@ObjCName("ObjCNameObjCObject", "ObjCNameSwiftObject") +object ObjCNameKotlinObject + +@ObjCName("ObjCNameObjCEnum", "ObjCNameSwiftEnum") +enum class ObjCNameKotlinEnum { + @ObjCName("objcOne", "swiftOne") + KOTLIN_ONE, + @ObjCName("objcTwo", "companion") + kotlinTwo, + @ObjCName("objcThree", "swiftThree") + KotlinThree; + + companion object { + fun foo(): Int = 0 + } +} diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Annotations.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Annotations.kt index 0dd82050019..4c0413273fd 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Annotations.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Annotations.kt @@ -5,6 +5,7 @@ package kotlin.native +import kotlin.experimental.ExperimentalObjCName import kotlin.reflect.KClass /** @@ -83,3 +84,19 @@ public annotation class EagerInitialization @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.BINARY) public actual annotation class CName(actual val externName: String = "", actual val shortName: String = "") + +/** + * Instructs the Kotlin compiler to use a custom Objective-C and/or Swift name for this class, property, parameter or function. + * @param exact specifies if the name of a class should be interpreted as the exact name. + * E.g. the compiler won't add a top level prefix or the outer class names to exact names. + */ +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.FUNCTION +) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +@ExperimentalObjCName +public actual annotation class ObjCName(actual val name: String = "", actual val swiftName: String = "", actual val exact: Boolean = false) diff --git a/libraries/stdlib/api/js-v1/kotlin.experimental.kt b/libraries/stdlib/api/js-v1/kotlin.experimental.kt index f9a6132b16d..e00ee054662 100644 --- a/libraries/stdlib/api/js-v1/kotlin.experimental.kt +++ b/libraries/stdlib/api/js-v1/kotlin.experimental.kt @@ -30,6 +30,14 @@ public inline infix fun kotlin.Byte.xor(other: kotlin.Byte): kotlin.Byte @kotlin.internal.InlineOnly public inline infix fun kotlin.Short.xor(other: kotlin.Short): kotlin.Short +@kotlin.RequiresOptIn +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) +@kotlin.annotation.Retention(value = AnnotationRetention.BINARY) +@kotlin.annotation.MustBeDocumented +public final annotation class ExperimentalObjCName : kotlin.Annotation { + public constructor ExperimentalObjCName() +} + @kotlin.RequiresOptIn(level = Level.ERROR) @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) diff --git a/libraries/stdlib/api/js/kotlin.experimental.kt b/libraries/stdlib/api/js/kotlin.experimental.kt index f9a6132b16d..e00ee054662 100644 --- a/libraries/stdlib/api/js/kotlin.experimental.kt +++ b/libraries/stdlib/api/js/kotlin.experimental.kt @@ -30,6 +30,14 @@ public inline infix fun kotlin.Byte.xor(other: kotlin.Byte): kotlin.Byte @kotlin.internal.InlineOnly public inline infix fun kotlin.Short.xor(other: kotlin.Short): kotlin.Short +@kotlin.RequiresOptIn +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) +@kotlin.annotation.Retention(value = AnnotationRetention.BINARY) +@kotlin.annotation.MustBeDocumented +public final annotation class ExperimentalObjCName : kotlin.Annotation { + public constructor ExperimentalObjCName() +} + @kotlin.RequiresOptIn(level = Level.ERROR) @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) diff --git a/libraries/stdlib/src/kotlin/annotations/NativeAnnotations.kt b/libraries/stdlib/src/kotlin/annotations/NativeAnnotations.kt index f90d5be4f20..d0869d90d45 100644 --- a/libraries/stdlib/src/kotlin/annotations/NativeAnnotations.kt +++ b/libraries/stdlib/src/kotlin/annotations/NativeAnnotations.kt @@ -5,6 +5,8 @@ package kotlin.native +import kotlin.experimental.ExperimentalObjCName + /** * Makes top level function available from C/C++ code with the given name. * @@ -46,3 +48,20 @@ public expect annotation class CName(val externName: String = "", val shortName: @Retention(AnnotationRetention.BINARY) @OptionalExpectation expect annotation class FreezingIsDeprecated + +/** + * Instructs the Kotlin compiler to use a custom Objective-C and/or Swift name for this class, property, parameter or function. + * @param exact specifies if the name of a class should be interpreted as the exact name. + * E.g. the compiler won't add a top level prefix or the outer class names to exact names. + */ +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.FUNCTION +) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +@OptionalExpectation +@ExperimentalObjCName +public expect annotation class ObjCName(val name: String = "", val swiftName: String = "", val exact: Boolean = false) diff --git a/libraries/stdlib/src/kotlin/experimental/ExperimentalObjCName.kt b/libraries/stdlib/src/kotlin/experimental/ExperimentalObjCName.kt new file mode 100644 index 00000000000..521eb1e86a5 --- /dev/null +++ b/libraries/stdlib/src/kotlin/experimental/ExperimentalObjCName.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.experimental + +/** + * This annotation marks the experimental [ObjCName][kotlin.native.ObjCName] annotation. + */ +@RequiresOptIn +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +public annotation class ExperimentalObjCName diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 146c345db65..91e3088a38a 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3122,6 +3122,9 @@ public final class kotlin/enums/EnumEntriesKt { public static final fun enumEntries (Lkotlin/jvm/functions/Function0;)Lkotlin/enums/EnumEntries; } +public abstract interface annotation class kotlin/experimental/ExperimentalObjCName : java/lang/annotation/Annotation { +} + public abstract interface annotation class kotlin/experimental/ExperimentalTypeInference : java/lang/annotation/Annotation { } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt index 8ea3e6f6cbc..7406ec4300e 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt @@ -38,7 +38,22 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { "@ThreadLocal is applicable only to property with backing field, to property with delegation or to objects" ) put(ErrorsNative.INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL, "@ThreadLocal is applicable only to top level declarations") - put(ErrorsNative.INVALID_CHARACTERS_NATIVE, "Name {0}", CommonRenderers.STRING); + put(ErrorsNative.INVALID_CHARACTERS_NATIVE, "Name {0}", CommonRenderers.STRING) + put(ErrorsNative.INAPPLICABLE_OBJC_NAME, "@ObjCName is not applicable on overrides") + put(ErrorsNative.INVALID_OBJC_NAME, "@ObjCName should have a name and/or swiftName") + put(ErrorsNative.INVALID_OBJC_NAME_CHARS, "@ObjCName contains illegal characters: {0}", CommonRenderers.STRING) + put( + ErrorsNative.INVALID_OBJC_NAME_FIRST_CHAR, "@ObjCName contains illegal first characters: {0}", + CommonRenderers.STRING + ) + put( + ErrorsNative.INCOMPATIBLE_OBJC_NAME_OVERRIDE, "Member \"{0}\" inherits inconsistent @ObjCName from {1}", + Renderers.NAME, + CommonRenderers.commaSeparated(Renderers.NAME) + ) + put(ErrorsNative.INAPPLICABLE_EXACT_OBJC_NAME, "Exact @ObjCName is only applicable to classes, objects and interfaces") + put(ErrorsNative.MISSING_EXACT_OBJC_NAME, "Exact @ObjCName is required to have an ObjC name") + put(ErrorsNative.NON_LITERAL_OBJC_NAME_ARG, "@ObjCName accepts only literal string and boolean values") } } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt index d8faec6b04a..c7374b1b3a5 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt @@ -36,6 +36,22 @@ object ErrorsNative { val VARIABLE_IN_ENUM = DiagnosticFactory0.create(Severity.INFO) @JvmField val INVALID_CHARACTERS_NATIVE = DiagnosticFactoryForDeprecation1.create(LanguageFeature.ProhibitInvalidCharsInNativeIdentifiers) + @JvmField + val INAPPLICABLE_OBJC_NAME = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val INVALID_OBJC_NAME = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val INVALID_OBJC_NAME_CHARS = DiagnosticFactory1.create(Severity.ERROR) + @JvmField + val INVALID_OBJC_NAME_FIRST_CHAR = DiagnosticFactory1.create(Severity.ERROR) + @JvmField + val INCOMPATIBLE_OBJC_NAME_OVERRIDE = DiagnosticFactory2.create>(Severity.ERROR) + @JvmField + val INAPPLICABLE_EXACT_OBJC_NAME = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val MISSING_EXACT_OBJC_NAME = DiagnosticFactory0.create(Severity.ERROR) + @JvmField + val NON_LITERAL_OBJC_NAME_ARG = DiagnosticFactory0.create(Severity.ERROR) init { Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java) diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt new file mode 100644 index 00000000000..08295bfaf4c --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.konan.diagnostics + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtConstantExpression +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +object NativeObjCNameChecker : DeclarationChecker { + private val objCNameFqName = FqName("kotlin.native.ObjCName") + + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + checkDeclaration(declaration, descriptor, context) + checkOverrides(declaration, descriptor, context) + checkFakeOverrides(declaration, descriptor, context) + } + + private fun checkDeclaration(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + val objCNames = descriptor.getObjCNames().filterNotNull() + if (objCNames.isEmpty()) return + if (descriptor is CallableMemberDescriptor && descriptor.overriddenDescriptors.isNotEmpty()) { + objCNames.forEach { + val reportLocation = DescriptorToSourceUtils.getSourceFromAnnotation(it.annotation) ?: declaration + context.trace.report(ErrorsNative.INAPPLICABLE_OBJC_NAME.on(reportLocation)) + } + } + objCNames.forEach { checkObjCName(it, declaration, descriptor, context) } + } + + // We only allow valid ObjC identifiers (even for Swift names) + private val validFirstChars = ('A'..'Z').toSet() + ('a'..'z').toSet() + '_' + private val validChars = validFirstChars + ('0'..'9').toSet() + + private fun checkObjCName( + objCName: ObjCName, + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + context: DeclarationCheckerContext + ) { + val annotationSource = DescriptorToSourceUtils.getSourceFromAnnotation(objCName.annotation) + annotationSource?.valueArguments?.forEach { + // We don't support constant references since that would require resolution in ObjCExportLazy + val expression = it.getArgumentExpression() ?: return@forEach + if (expression is KtConstantExpression || expression is KtStringTemplateExpression || + (it is KtValueArgument && it.stringTemplateExpression != null) + ) return@forEach + context.trace.report(ErrorsNative.NON_LITERAL_OBJC_NAME_ARG.on(expression)) + } + val reportLocation = annotationSource ?: declaration + if (objCName.name == null && objCName.swiftName == null) { + context.trace.report(ErrorsNative.INVALID_OBJC_NAME.on(reportLocation)) + } + val invalidNameFirstChar = objCName.name?.firstOrNull()?.takeUnless(validFirstChars::contains) + val invalidSwiftNameFirstChar = objCName.swiftName?.firstOrNull()?.takeUnless(validFirstChars::contains) + val invalidFirstChars = setOfNotNull(invalidNameFirstChar, invalidSwiftNameFirstChar) + if (invalidFirstChars.isNotEmpty()) { + context.trace.report(ErrorsNative.INVALID_OBJC_NAME_FIRST_CHAR.on(reportLocation, invalidFirstChars.joinToString(""))) + } + val invalidNameChars = objCName.name?.toSet()?.subtract(validChars) ?: emptySet() + val invalidSwiftNameChars = objCName.swiftName?.toSet()?.subtract(validChars) ?: emptySet() + val invalidChars = invalidNameChars + invalidSwiftNameChars + if (invalidChars.isNotEmpty()) { + context.trace.report(ErrorsNative.INVALID_OBJC_NAME_CHARS.on(reportLocation, invalidChars.joinToString(""))) + } + if (objCName.exact && (descriptor !is ClassDescriptor || descriptor.kind == ClassKind.ENUM_ENTRY)) { + context.trace.report(ErrorsNative.INAPPLICABLE_EXACT_OBJC_NAME.on(reportLocation)) + } + if (objCName.exact && objCName.name == null) { + context.trace.report(ErrorsNative.MISSING_EXACT_OBJC_NAME.on(reportLocation)) + } + } + + private fun checkOverrides(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor !is CallableMemberDescriptor || descriptor.overriddenDescriptors.isEmpty()) return + val objCNames = descriptor.overriddenDescriptors.map { it.getFirstBaseDescriptor().getObjCNames() } + if (!objCNames.allNamesEquals()) { + val containingDeclarations = descriptor.overriddenDescriptors.map { it.containingDeclaration } + context.trace.report(ErrorsNative.INCOMPATIBLE_OBJC_NAME_OVERRIDE.on(declaration, descriptor, containingDeclarations)) + } + } + + private fun checkFakeOverrides(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor !is ClassDescriptor) return + descriptor.defaultType.memberScope + .getContributedDescriptors(DescriptorKindFilter.ALL, MemberScope.Companion.ALL_NAME_FILTER) + .forEach { + if (it !is CallableMemberDescriptor || it.kind.isReal) return@forEach + checkOverrides(declaration, it, context) + } + } + + private fun CallableMemberDescriptor.getFirstBaseDescriptor(): CallableMemberDescriptor = + if (overriddenDescriptors.isEmpty()) this else overriddenDescriptors.first().getFirstBaseDescriptor() + + private class ObjCName( + val annotation: AnnotationDescriptor + ) { + val name: String? = annotation.argumentValue("name")?.value?.safeAs()?.takeIf { it.isNotBlank() } + val swiftName: String? = annotation.argumentValue("swiftName")?.value?.safeAs()?.takeIf { it.isNotBlank() } + val exact: Boolean = annotation.argumentValue("exact")?.value?.safeAs() ?: false + + override fun equals(other: Any?): Boolean = + other is ObjCName && name == other.name && swiftName == other.swiftName && exact == other.exact + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + swiftName.hashCode() + result = 31 * result + exact.hashCode() + return result + } + } + + private fun DeclarationDescriptor.getObjCName(): ObjCName? = annotations.findAnnotation(objCNameFqName)?.let(::ObjCName) + + private fun DeclarationDescriptor.getObjCNames(): List = when (this) { + is FunctionDescriptor -> buildList { + add(getObjCName()) + add(extensionReceiverParameter?.getObjCName()) + valueParameters.forEach { add(it.getObjCName()) } + } + else -> listOf(getObjCName()) + } + + private fun List>.allNamesEquals(): Boolean { + val first = this[0] + for (i in 1 until size) { + if (first != this[i]) return false + } + return true + } +} \ No newline at end of file diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt index 1ff75576d2a..823320aac99 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt @@ -25,7 +25,8 @@ object NativePlatformConfigurator : PlatformConfiguratorBase( ), additionalDeclarationCheckers = listOf( NativeThrowsChecker, NativeSharedImmutableChecker, - NativeTopLevelSingletonChecker, NativeThreadLocalChecker + NativeTopLevelSingletonChecker, NativeThreadLocalChecker, + NativeObjCNameChecker ) ) { override fun configureModuleComponents(container: StorageComponentContainer) {