From 16b985b36ee92d587d903173e731c477b3a782d8 Mon Sep 17 00:00:00 2001 From: Artem Olkov Date: Wed, 14 Feb 2024 11:10:51 +0000 Subject: [PATCH] Support for Unsigned Types in swift export #KT-65668 fixed Merge-request: KT-MR-14300 Merged-by: Artem Olkov --- .../testData/SwiftExport/smokes/smoke0.kt | 16 ++++++++++++++ .../testData/SwiftExport/smokes/smoke0.swift | 18 +++++++++++++++ .../testData/SwiftExport/smokes/smokes.kt | 14 +++++++++++- native/swift/build.gradle.kts | 6 +++-- .../swift-export-standalone/build.gradle.kts | 3 +-- .../standalone/SwiftExportRunner.kt | 22 ++++++++++++++----- .../standalone/builders/buildSwiftModule.kt | 13 +++++++++++ .../testData/functions/golden_result/result.h | 2 +- .../functions/golden_result/result.kt | 6 ++--- .../functions/golden_result/result.swift | 6 ++++- .../testData/functions/input_root/main.kt | 9 ++++---- .../AbstractKotlinSirContextTest.kt | 12 +++++++++- 12 files changed, 105 insertions(+), 22 deletions(-) diff --git a/native/native.tests/testData/SwiftExport/smokes/smoke0.kt b/native/native.tests/testData/SwiftExport/smokes/smoke0.kt index 25bb1365d6d..5a8ed741bae 100644 --- a/native/native.tests/testData/SwiftExport/smokes/smoke0.kt +++ b/native/native.tests/testData/SwiftExport/smokes/smoke0.kt @@ -12,4 +12,20 @@ fun fooInt(): Int { fun fooLong(): Long { return -1 +} + +fun fooUByte(): UByte { + return 0u +} + +fun fooUShort(): UShort { + return 0u +} + +fun fooUInt(): UInt { + return 0u +} + +fun fooULong(): ULong { + return 0u } \ No newline at end of file diff --git a/native/native.tests/testData/SwiftExport/smokes/smoke0.swift b/native/native.tests/testData/SwiftExport/smokes/smoke0.swift index 40fa8aa180d..30d99030f6c 100644 --- a/native/native.tests/testData/SwiftExport/smokes/smoke0.swift +++ b/native/native.tests/testData/SwiftExport/smokes/smoke0.swift @@ -14,6 +14,24 @@ func smoke() throws { try assertEquals(actual: org.kotlin.plus(a: 1, b: 2, c: 3), expected: 1 + 2 + 3) + // we expect strange stuff for plus and minus if UInt8 and UInt16 because kotlin behaves that way: + // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-u-byte/minus.html + try assertEquals(actual: fooUByte(), expected: UInt8.min) + try assertEquals(actual: org.kotlin.minus(a: fooUByte(), b: UInt8(1)), expected: UInt32.max) + try assertEquals(actual: org.kotlin.plus(a: UInt8.max, b: UInt8(1)), expected: UInt32(256)) + + try assertEquals(actual: fooUShort(), expected: UInt16.min) + try assertEquals(actual: org.kotlin.minus(a: fooUShort(), b: UInt16(1)), expected: UInt32.max) + try assertEquals(actual: org.kotlin.plus(a: UInt16.max, b: UInt16(1)), expected: UInt32(65536)) + + try assertEquals(actual: fooUInt(), expected: UInt32.min) + try assertEquals(actual: org.kotlin.minus(a: fooUInt(), b: UInt32(1)), expected: UInt32.max) + try assertEquals(actual: org.kotlin.plus(a: UInt32.max, b: UInt32(1)), expected: UInt32.min) + + try assertEquals(actual: fooULong(), expected: UInt64.min) + try assertEquals(actual: org.kotlin.minus(a: fooULong(), b: UInt64(1)), expected: UInt64.max) + try assertEquals(actual: org.kotlin.plus(a: UInt64.max, b: UInt64(1)), expected: UInt64.min) + try assertEquals(actual: org.kotlin.logicalOr(a: true, b: true), expected: true || true) try assertEquals(actual: org.kotlin.logicalOr(a: true, b: false), expected: true || false) try assertEquals(actual: org.kotlin.logicalOr(a: false, b: false), expected: false || false) diff --git a/native/native.tests/testData/SwiftExport/smokes/smokes.kt b/native/native.tests/testData/SwiftExport/smokes/smokes.kt index a528aa859c9..13d51c42f40 100644 --- a/native/native.tests/testData/SwiftExport/smokes/smokes.kt +++ b/native/native.tests/testData/SwiftExport/smokes/smokes.kt @@ -5,4 +5,16 @@ fun plus(a: Int, b: Int, c: Int) = fun logicalOr(a: Boolean, b: Boolean) = a || b -fun xor(a: Boolean, b: Boolean) = a xor b \ No newline at end of file +fun xor(a: Boolean, b: Boolean) = a xor b + +fun plus(a: UByte, b: UByte) = a + b +fun minus(a: UByte, b: UByte) = a - b + +fun plus(a: UShort, b: UShort) = a + b +fun minus(a: UShort, b: UShort) = a - b + +fun plus(a: UInt, b: UInt) = a + b +fun minus(a: UInt, b: UInt) = a - b + +fun plus(a: ULong, b: ULong) = a + b +fun minus(a: ULong, b: ULong) = a - b \ No newline at end of file diff --git a/native/swift/build.gradle.kts b/native/swift/build.gradle.kts index 9819a97afc8..d03349d6a18 100644 --- a/native/swift/build.gradle.kts +++ b/native/swift/build.gradle.kts @@ -5,7 +5,9 @@ plugins { tasks.register("sirAllTests") { dependsOn( ":native:swift:sir-compiler-bridge:test", - ":native:swift:sir-printer:test", - ":native:swift:swift-export-standalone:test", + ":native:swift:sir-printer:test" ) + if (kotlinBuildProperties.isKotlinNativeEnabled) { + dependsOn(":native:swift:swift-export-standalone:test") + } } diff --git a/native/swift/swift-export-standalone/build.gradle.kts b/native/swift/swift-export-standalone/build.gradle.kts index 32b1ba97950..676ec20acd8 100644 --- a/native/swift/swift-export-standalone/build.gradle.kts +++ b/native/swift/swift-export-standalone/build.gradle.kts @@ -41,9 +41,8 @@ sourceSets { val testDataDir = projectDir.resolve("testData") -projectTest(jUnitMode = JUnitMode.JUnit5) { +val test by nativeTest("test", null) { inputs.dir(testDataDir) - dependsOn(":dist") workingDir = rootDir useJUnitPlatform { } } diff --git a/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/SwiftExportRunner.kt b/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/SwiftExportRunner.kt index 18f23352bee..91f6acaf139 100644 --- a/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/SwiftExportRunner.kt +++ b/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/SwiftExportRunner.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.swiftexport.standalone +import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.swiftexport.standalone.SwiftExportConfig.Companion.BRIDGE_MODULE_NAME import org.jetbrains.kotlin.swiftexport.standalone.SwiftExportConfig.Companion.DEBUG_MODE_ENABLED import org.jetbrains.kotlin.swiftexport.standalone.SwiftExportConfig.Companion.DEFAULT_BRIDGE_MODULE_NAME @@ -12,11 +13,13 @@ import org.jetbrains.kotlin.swiftexport.standalone.builders.buildFunctionBridges import org.jetbrains.kotlin.swiftexport.standalone.builders.buildSwiftModule import org.jetbrains.kotlin.swiftexport.standalone.transformation.transformToSwift import org.jetbrains.kotlin.swiftexport.standalone.writer.dumpResultToFiles +import org.jetbrains.kotlin.utils.KotlinNativePaths import java.nio.file.Path public data class SwiftExportConfig( - val settings: Map, - val logger: SwiftExportLogger, + val settings: Map = emptyMap(), + val logger: SwiftExportLogger = createDummyLogger(), + val distribution: Distribution = Distribution(KotlinNativePaths.homePath.absolutePath) ) { public companion object { public const val DEBUG_MODE_ENABLED: String = "DEBUG_MODE_ENABLED" @@ -73,18 +76,25 @@ public fun createDummyLogger(): SwiftExportLogger = object : SwiftExportLogger { */ public fun runSwiftExport( input: SwiftExportInput, - config: SwiftExportConfig = SwiftExportConfig(emptyMap(), createDummyLogger()), + config: SwiftExportConfig = SwiftExportConfig(), output: SwiftExportOutput, ) { val isDebugModeEnabled = config.settings.containsKey(DEBUG_MODE_ENABLED) val bridgeModuleName = config.settings.getOrElse(BRIDGE_MODULE_NAME) { - config.logger.report(SwiftExportLogger.Severity.Warning, - "Bridging header is not set. Using $DEFAULT_BRIDGE_MODULE_NAME instead") + config.logger.report( + SwiftExportLogger.Severity.Warning, + "Bridging header is not set. Using $DEFAULT_BRIDGE_MODULE_NAME instead" + ) DEFAULT_BRIDGE_MODULE_NAME } - val module = buildSwiftModule(input, isDebugModeEnabled, bridgeModuleName) + val module = buildSwiftModule( + input, + config.distribution, + isDebugModeEnabled, + bridgeModuleName + ) .transformToSwift() val bridgeRequests = module.buildFunctionBridges() module.dumpResultToFiles(bridgeRequests, output) diff --git a/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/builders/buildSwiftModule.kt b/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/builders/buildSwiftModule.kt index d27858fd321..857a6d18020 100644 --- a/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/builders/buildSwiftModule.kt +++ b/native/swift/swift-export-standalone/src/org/jetbrains/kotlin/swiftexport/standalone/builders/buildSwiftModule.kt @@ -10,7 +10,9 @@ import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.standalone.KtAlwaysAccessibleLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession +import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule +import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.platform.konan.NativePlatforms import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.sir.SirModule @@ -18,10 +20,12 @@ import org.jetbrains.kotlin.sir.builder.buildImport import org.jetbrains.kotlin.sir.builder.buildModule import org.jetbrains.kotlin.swiftexport.standalone.SwiftExportInput import org.jetbrains.sir.passes.builder.buildSirDeclarationList +import kotlin.io.path.Path @OptIn(KtAnalysisApiInternals::class) internal fun buildSwiftModule( input: SwiftExportInput, + kotlinDistribution: Distribution, shouldSortInputFiles: Boolean, bridgeModuleName: String, ): SirModule { @@ -31,11 +35,20 @@ internal fun buildSwiftModule( buildKtModuleProvider { platform = NativePlatforms.unspecifiedNativePlatform + val stdlib = addModule( + buildKtLibraryModule { + addBinaryRoot(Path(kotlinDistribution.stdlib)) + platform = NativePlatforms.unspecifiedNativePlatform + libraryName = "stdlib" + } + ) + addModule( buildKtSourceModule { addSourceRoot(input.sourceRoot) platform = NativePlatforms.unspecifiedNativePlatform moduleName = "main" + addRegularDependency(stdlib) } ) } diff --git a/native/swift/swift-export-standalone/testData/functions/golden_result/result.h b/native/swift/swift-export-standalone/testData/functions/golden_result/result.h index 5fddbe0e7db..8edff5064ae 100644 --- a/native/swift/swift-export-standalone/testData/functions/golden_result/result.h +++ b/native/swift/swift-export-standalone/testData/functions/golden_result/result.h @@ -4,7 +4,7 @@ void namespace1_local_functions_foo(); int32_t namespace1_main_foobar__TypesOfArguments__int32_t__(int32_t param); -void namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_float_double__(_Bool arg1, int8_t arg2, int16_t arg3, int32_t arg4, int64_t arg5, float arg10, double arg11); +void namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_uint8_t_uint16_t_uint32_t_uint64_t_float_double__(_Bool arg1, int8_t arg2, int16_t arg3, int32_t arg4, int64_t arg5, uint8_t arg6, uint16_t arg7, uint32_t arg8, uint64_t arg9, float arg10, double arg11); int32_t namespace1_bar(); diff --git a/native/swift/swift-export-standalone/testData/functions/golden_result/result.kt b/native/swift/swift-export-standalone/testData/functions/golden_result/result.kt index 610a8b94b1c..04192ad23d1 100644 --- a/native/swift/swift-export-standalone/testData/functions/golden_result/result.kt +++ b/native/swift/swift-export-standalone/testData/functions/golden_result/result.kt @@ -12,9 +12,9 @@ public fun namespace1_main_foobar(param: Int): Int { return result } -@ExportedBridge("namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_float_double__") -public fun namespace1_main_all_args(arg1: Boolean, arg2: Byte, arg3: Short, arg4: Int, arg5: Long, arg10: Float, arg11: Double): Unit { - val result = namespace1.main.all_args(arg1, arg2, arg3, arg4, arg5, arg10, arg11) +@ExportedBridge("namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_uint8_t_uint16_t_uint32_t_uint64_t_float_double__") +public fun namespace1_main_all_args(arg1: Boolean, arg2: Byte, arg3: Short, arg4: Int, arg5: Long, arg6: UByte, arg7: UShort, arg8: UInt, arg9: ULong, arg10: Float, arg11: Double): Unit { + val result = namespace1.main.all_args(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) return result } diff --git a/native/swift/swift-export-standalone/testData/functions/golden_result/result.swift b/native/swift/swift-export-standalone/testData/functions/golden_result/result.swift index 9dd273b52a7..ebd1b4a2f5b 100644 --- a/native/swift/swift-export-standalone/testData/functions/golden_result/result.swift +++ b/native/swift/swift-export-standalone/testData/functions/golden_result/result.swift @@ -18,10 +18,14 @@ public enum namespace1 { arg3: Swift.Int16, arg4: Swift.Int32, arg5: Swift.Int64, + arg6: Swift.UInt8, + arg7: Swift.UInt16, + arg8: Swift.UInt32, + arg9: Swift.UInt64, arg10: Swift.Float, arg11: Swift.Double ) -> Swift.Void { - namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_float_double__(arg1, arg2, arg3, arg4, arg5, arg10, arg11) + namespace1_main_all_args__TypesOfArguments___Bool_int8_t_int16_t_int32_t_int64_t_uint8_t_uint16_t_uint32_t_uint64_t_float_double__(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) } } public static func bar() -> Swift.Int32 { diff --git a/native/swift/swift-export-standalone/testData/functions/input_root/main.kt b/native/swift/swift-export-standalone/testData/functions/input_root/main.kt index 2513d52d164..5e52f0ab0d0 100644 --- a/native/swift/swift-export-standalone/testData/functions/input_root/main.kt +++ b/native/swift/swift-export-standalone/testData/functions/input_root/main.kt @@ -13,11 +13,10 @@ fun all_args( arg4: Int, arg5: Long, - // TODO: support kotlin Unsigned types - KT-65668 -// arg6: UByte, -// arg7: UShort, -// arg8: UInt, -// arg9: ULong, + arg6: UByte, + arg7: UShort, + arg8: UInt, + arg9: ULong, arg10: Float, arg11: Double, diff --git a/native/swift/swift-export-standalone/tests/org/jetbrains/kotlin/swiftexport/standalone/AbstractKotlinSirContextTest.kt b/native/swift/swift-export-standalone/tests/org/jetbrains/kotlin/swiftexport/standalone/AbstractKotlinSirContextTest.kt index f158cd4e811..81797845983 100644 --- a/native/swift/swift-export-standalone/tests/org/jetbrains/kotlin/swiftexport/standalone/AbstractKotlinSirContextTest.kt +++ b/native/swift/swift-export-standalone/tests/org/jetbrains/kotlin/swiftexport/standalone/AbstractKotlinSirContextTest.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.swiftexport.standalone import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.test.KotlinTestUtils import kotlin.io.path.* @@ -41,7 +42,8 @@ abstract class AbstractSwiftRunnerTestBase { SwiftExportConfig.DEBUG_MODE_ENABLED to "true", SwiftExportConfig.BRIDGE_MODULE_NAME to SwiftExportConfig.DEFAULT_BRIDGE_MODULE_NAME, ), - logger = createDummyLogger() + logger = createDummyLogger(), + distribution = Distribution(KonanHome.konanHomePath), ) ) @@ -50,3 +52,11 @@ abstract class AbstractSwiftRunnerTestBase { KotlinTestUtils.assertEqualsToFile(expectedKotlinBridge, output.kotlinBridges.readText()) } } + +private object KonanHome { + private const val KONAN_HOME_PROPERTY_KEY = "kotlin.internal.native.test.nativeHome" + + val konanHomePath: String + get() = System.getProperty(KONAN_HOME_PROPERTY_KEY) + ?: error("Missing System property: '$KONAN_HOME_PROPERTY_KEY'") +}