diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt index e1b5e106f92..9d0ea0a21b2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt @@ -29,6 +29,8 @@ object BinaryOptions : BinaryOptionRegistry() { val objcExportSuspendFunctionLaunchThreadRestriction by option() + val objcExportDisableSwiftMemberNameMangling by booleanOption() + val gcSchedulerType by option() val gcMarkSingleThreaded by booleanOption() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt index 4ffb104e2fe..a4e3f9783b8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanCompilerFrontendServices.kt @@ -40,6 +40,9 @@ internal fun StorageComponentContainer.initContainer(config: KonanConfig) { override val objcGenerics: Boolean get() = config.configuration.getBoolean(KonanConfigKeys.OBJC_GENERICS) + override val disableSwiftMemberNameMangling: Boolean + get() = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling) + override val unitSuspendFunctionExport: UnitSuspendFunctionObjCExport get() = config.unitSuspendFunctionObjCExport }) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index fe2190d5f53..3b07a39871d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -48,13 +48,15 @@ internal fun produceObjCExportInterface( val mapper = ObjCExportMapper(frontendServices.deprecationResolver, unitSuspendFunctionExport = unitSuspendFunctionExport) val moduleDescriptors = listOf(moduleDescriptor) + moduleDescriptor.getExportedDependencies(config) val objcGenerics = config.configuration.getBoolean(KonanConfigKeys.OBJC_GENERICS) + val disableSwiftMemberNameMangling = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling) val namer = ObjCExportNamerImpl( moduleDescriptors.toSet(), moduleDescriptor.builtIns, mapper, topLevelNamePrefix, local = false, - objcGenerics = objcGenerics + objcGenerics = objcGenerics, + disableSwiftMemberNameMangling = disableSwiftMemberNameMangling, ) val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer, objcGenerics) headerGenerator.translateModule() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportLazy.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportLazy.kt index 528b4536ef1..1c48dab1dd3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportLazy.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportLazy.kt @@ -44,6 +44,10 @@ interface ObjCExportLazy { fun isIncluded(moduleInfo: ModuleInfo): Boolean fun getCompilerModuleName(moduleInfo: ModuleInfo): String val objcGenerics: Boolean + + val disableSwiftMemberNameMangling: Boolean + get() = false + val unitSuspendFunctionExport: UnitSuspendFunctionObjCExport } @@ -447,6 +451,7 @@ internal fun createNamerConfiguration(configuration: ObjCExportLazy.Configuratio } override val objcGenerics = configuration.objcGenerics + override val disableSwiftMemberNameMangling = configuration.disableSwiftMemberNameMangling } } 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 705f9fc55eb..e62699c59bf 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 @@ -54,6 +54,9 @@ interface ObjCExportNamer { val topLevelNamePrefix: String fun getAdditionalPrefix(module: ModuleDescriptor): String? val objcGenerics: Boolean + + val disableSwiftMemberNameMangling: Boolean + get() = false } val topLevelNamePrefix: String @@ -278,7 +281,8 @@ internal class ObjCExportNamerImpl( mapper: ObjCExportMapper, topLevelNamePrefix: String, local: Boolean, - objcGenerics: Boolean = false + objcGenerics: Boolean = false, + disableSwiftMemberNameMangling: Boolean = false, ) : this( object : ObjCExportNamer.Configuration { override val topLevelNamePrefix: String @@ -290,6 +294,9 @@ internal class ObjCExportNamerImpl( override val objcGenerics: Boolean get() = objcGenerics + override val disableSwiftMemberNameMangling: Boolean + get() = disableSwiftMemberNameMangling + }, builtIns, mapper, @@ -332,20 +339,24 @@ internal class ObjCExportNamerImpl( } private val methodSwiftNames = object : Mapping() { - override fun conflict(first: FunctionDescriptor, second: FunctionDescriptor): Boolean = - !mapper.canHaveSameSelector(first, second) + override fun conflict(first: FunctionDescriptor, second: FunctionDescriptor): Boolean { + if (configuration.disableSwiftMemberNameMangling) return false // Ignore all conflicts. + return !mapper.canHaveSameSelector(first, second) + } // Note: this condition is correct but can be too strict. } - private inner class PropertyNameMapping : Mapping() { + private inner class PropertyNameMapping(val forSwift: Boolean) : Mapping() { override fun reserved(name: String) = name in Reserved.propertyNames - override fun conflict(first: PropertyDescriptor, second: PropertyDescriptor): Boolean = - !mapper.canHaveSameName(first, second) + override fun conflict(first: PropertyDescriptor, second: PropertyDescriptor): Boolean { + if (forSwift && configuration.disableSwiftMemberNameMangling) return false // Ignore all conflicts. + return !mapper.canHaveSameName(first, second) + } } - private val objCPropertyNames = PropertyNameMapping() - private val swiftPropertyNames = PropertyNameMapping() + private val objCPropertyNames = PropertyNameMapping(forSwift = false) + private val swiftPropertyNames = PropertyNameMapping(forSwift = true) private open inner class GlobalNameMapping : Mapping() { final override fun conflict(first: T, second: T): Boolean = true diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 2905fac1058..ce0270dca1f 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -5715,6 +5715,38 @@ if (isAppleTarget(project)) { } } + frameworkTest('testObjCExportNoSwiftMemberNameMangling') { + final String frameworkName = 'KtNoSwiftMemberNameMangling' + final String frameworkArtifactName = 'Kt' + final String dir = "$testOutputFramework/testObjCExportNoSwiftMemberNameMangling" + + def libraryName = frameworkName + "Library" + konanArtifacts { + library(libraryName, targets: [target.name]) { + srcDir "objcexport/library" + artifactName "test-library" + + if (!useCustomDist) { + dependsOn ":${target.name}CrossDistRuntime", ':distCompiler' + } + + extraOpts "-Xshort-module-name=MyLibrary" + extraOpts "-module-name", "org.jetbrains.kotlin.native.test-library" + } + } + framework(frameworkName) { + sources = ['objcexport'] + artifact = frameworkArtifactName + library = libraryName + opts = ["-Xbinary=objcExportDisableSwiftMemberNameMangling=true"] + } + swiftSources = ['objcexport'] + swiftExtraOpts = [ '-D', 'DISABLE_MEMBER_NAME_MANGLING' ] + if (isNoopGC) { + swiftExtraOpts += ["-D", "NOOP_GC"] + } + } + frameworkTest('testObjCExportStatic') { final String frameworkName = 'KtStatic' final String frameworkArtifactName = 'Kt' diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h old mode 100755 new mode 100644 index 0a0baee9aea..68cd285a787 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h @@ -1564,6 +1564,33 @@ __attribute__((swift_name("Person.WorkerContractor"))) @property (readonly) int32_t id __attribute__((swift_name("id"))); @end +__attribute__((swift_name("SwiftNameManglingI1"))) +@protocol KtSwiftNameManglingI1 +@required +- (int32_t)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (int32_t)clashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (int32_t)swiftClashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (int32_t)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) int32_t clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((swift_name("SwiftNameManglingI2"))) +@protocol KtSwiftNameManglingI2 +@required +- (id)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (id)swiftClashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (id)clashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (id)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) id clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameManglingKt"))) +@interface KtSwiftNameManglingKt : KtBase ++ (id)i1 __attribute__((swift_name("i1()"))); ++ (id)i2 __attribute__((swift_name("i2()"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("ThrowableAsError"))) @interface KtThrowableAsError : KtKotlinThrowable diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h index 5e9677f4801..ef10b463cab 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h @@ -1499,6 +1499,33 @@ __attribute__((swift_name("Person.WorkerContractor"))) @property (readonly) int32_t id __attribute__((swift_name("id"))); @end +__attribute__((swift_name("SwiftNameManglingI1"))) +@protocol KtSwiftNameManglingI1 +@required +- (int32_t)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (int32_t)clashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (int32_t)swiftClashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (int32_t)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) int32_t clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((swift_name("SwiftNameManglingI2"))) +@protocol KtSwiftNameManglingI2 +@required +- (id)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (id)swiftClashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (id)clashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (id)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) id clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameManglingKt"))) +@interface KtSwiftNameManglingKt : KtBase ++ (id)i1 __attribute__((swift_name("i1()"))); ++ (id)i2 __attribute__((swift_name("i2()"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("ThrowableAsError"))) @interface KtThrowableAsError : KtKotlinThrowable diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h index 5e61583e101..218a606fd09 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h @@ -1499,6 +1499,33 @@ __attribute__((swift_name("Person.WorkerContractor"))) @property (readonly) int32_t id __attribute__((swift_name("id"))); @end +__attribute__((swift_name("SwiftNameManglingI1"))) +@protocol KtSwiftNameManglingI1 +@required +- (int32_t)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (int32_t)clashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (int32_t)swiftClashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (int32_t)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) int32_t clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((swift_name("SwiftNameManglingI2"))) +@protocol KtSwiftNameManglingI2 +@required +- (id)clashingMethod __attribute__((swift_name("clashingMethod()"))); +- (id)swiftClashingMethodWithObjCNameInI1 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI1()"))); +- (id)clashingMethodWithObjCNameInI2 __attribute__((swift_name("swiftClashingMethodWithObjCNameInI2()"))); +- (id)clashingMethodWithObjCNameInBoth __attribute__((swift_name("swiftClashingMethodWithObjCNameInBoth()"))); +@property (readonly) id clashingProperty __attribute__((swift_name("clashingProperty"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("SwiftNameManglingKt"))) +@interface KtSwiftNameManglingKt : KtBase ++ (id)i1 __attribute__((swift_name("i1()"))); ++ (id)i2 __attribute__((swift_name("i2()"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("ThrowableAsError"))) @interface KtThrowableAsError : KtKotlinThrowable diff --git a/kotlin-native/backend.native/tests/objcexport/kt38641.swift b/kotlin-native/backend.native/tests/objcexport/kt38641.swift index 871f23f23d6..191df1eaa95 100644 --- a/kotlin-native/backend.native/tests/objcexport/kt38641.swift +++ b/kotlin-native/backend.native/tests/objcexport/kt38641.swift @@ -29,8 +29,10 @@ private func testVar() throws { private func testTwoProperties() throws { let t = KT38641.TwoProperties() +#if !DISABLE_MEMBER_NAME_MANGLING try assertEquals(actual: t.description_, expected: "description") try assertEquals(actual: t.description__, expected: "description_") +#endif } private func testOverrideVal() throws { diff --git a/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.kt b/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.kt new file mode 100644 index 00000000000..4f23ecee28b --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.kt @@ -0,0 +1,57 @@ +@file:OptIn(kotlin.experimental.ExperimentalObjCName::class) + +package swiftNameMangling + +interface SwiftNameManglingI1 { + val clashingProperty: Int + + fun clashingMethod(): Int + + @ObjCName(swiftName = "swiftClashingMethodWithObjCNameInI1") + fun clashingMethodWithObjCNameInI1(): Int + + fun swiftClashingMethodWithObjCNameInI2(): Int + + @ObjCName(swiftName = "swiftClashingMethodWithObjCNameInBoth") + fun clashingMethodWithObjCNameInBoth(): Int +} + +fun i1() = object : SwiftNameManglingI1 { + override val clashingProperty: Int + get() = 1 + + override fun clashingMethod(): Int = 2 + + override fun clashingMethodWithObjCNameInI1(): Int = 3 + + override fun swiftClashingMethodWithObjCNameInI2(): Int = 4 + + override fun clashingMethodWithObjCNameInBoth(): Int = 5 +} + +interface SwiftNameManglingI2 { + val clashingProperty: Any + + fun clashingMethod(): Any + + fun swiftClashingMethodWithObjCNameInI1(): Any + + @ObjCName(swiftName = "swiftClashingMethodWithObjCNameInI2") + fun clashingMethodWithObjCNameInI2(): Any + + @ObjCName(swiftName = "swiftClashingMethodWithObjCNameInBoth") + fun clashingMethodWithObjCNameInBoth(): Any +} + +fun i2() = object : SwiftNameManglingI2 { + override val clashingProperty: Any + get() = "one" + + override fun clashingMethod(): Any = "two" + + override fun swiftClashingMethodWithObjCNameInI1(): Any = "three" + + override fun clashingMethodWithObjCNameInI2(): Any = "four" + + override fun clashingMethodWithObjCNameInBoth(): Any = "five" +} diff --git a/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.swift b/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.swift new file mode 100644 index 00000000000..f03cfa7abe4 --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/swiftNameMangling.swift @@ -0,0 +1,28 @@ +import Kt + +private func test1() throws { + let i1 = SwiftNameManglingKt.i1() + let i2 = SwiftNameManglingKt.i2() + +#if DISABLE_MEMBER_NAME_MANGLING + try assertEquals(actual: i1.clashingProperty, expected: 1) + try assertEquals(actual: i1.clashingMethod(), expected: 2) + try assertEquals(actual: i1.swiftClashingMethodWithObjCNameInI1(), expected: 3) + try assertEquals(actual: i1.swiftClashingMethodWithObjCNameInI2(), expected: 4) + try assertEquals(actual: i1.swiftClashingMethodWithObjCNameInBoth(), expected: 5) + + try assertEquals(actual: i2.clashingProperty as! String, expected: "one") + try assertEquals(actual: i2.clashingMethod() as! String, expected: "two") + try assertEquals(actual: i2.swiftClashingMethodWithObjCNameInI1() as! String, expected: "three") + try assertEquals(actual: i2.swiftClashingMethodWithObjCNameInI2() as! String, expected: "four") + try assertEquals(actual: i2.swiftClashingMethodWithObjCNameInBoth() as! String, expected: "five") +#endif +} + +class SwiftNameManglingTests : SimpleTestProvider { + override init() { + super.init() + + test("Test1", test1) + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/objcexport/topLevelMangling.swift b/kotlin-native/backend.native/tests/objcexport/topLevelMangling.swift index f22966e8f2e..e6a228299db 100644 --- a/kotlin-native/backend.native/tests/objcexport/topLevelMangling.swift +++ b/kotlin-native/backend.native/tests/objcexport/topLevelMangling.swift @@ -12,7 +12,12 @@ private func testPropertiesInDifferentFilesAreNotMangled() throws { private func testFunctionsInSameFileAreMangled() throws { try assertEquals(actual: TopLevelManglingAKt.sameNumber(value: Int32(1)), expected: 1) - try assertEquals(actual: TopLevelManglingAKt.sameNumber(value_: Int64(2)), expected: 2) +#if DISABLE_MEMBER_NAME_MANGLING + let sameNumberLong = TopLevelManglingAKt.sameNumber(value: Int64(2)) +#else + let sameNumberLong = TopLevelManglingAKt.sameNumber(value_: Int64(2)) +#endif + try assertEquals(actual: sameNumberLong, expected: 2) } class TopLevelManglingTests : SimpleTestProvider { diff --git a/kotlin-native/backend.native/tests/objcexport/values.swift b/kotlin-native/backend.native/tests/objcexport/values.swift index 0bcd401130b..ad3abf957c6 100644 --- a/kotlin-native/backend.native/tests/objcexport/values.swift +++ b/kotlin-native/backend.native/tests/objcexport/values.swift @@ -726,8 +726,11 @@ func testClashes() throws { let test2: TestClashes2 = test try assertEquals(actual: 1, expected: test1.clashingProperty) + +#if !DISABLE_MEMBER_NAME_MANGLING try assertEquals(actual: 1, expected: test2.clashingProperty_ as! Int32) try assertEquals(actual: 2, expected: test2.clashingProperty__ as! Int32) +#endif } func testInvalidIdentifiers() throws { @@ -742,7 +745,9 @@ func testInvalidIdentifiers() throws { try assertEquals(actual: TestInvalidIdentifiers.CompanionS()._42, expected: 42) +#if !DISABLE_MEMBER_NAME_MANGLING try assertEquals(actual: Set([test.__, test.___]), expected: Set(["_".utf16.first, "_".utf16.first])) +#endif } class ImplementingHiddenSubclass : TestDeprecation.ImplementingHidden {