diff --git a/backend.native/tests/objcexport/expectedLazy.h b/backend.native/tests/objcexport/expectedLazy.h index 7c3eee7c21d..9d5ed562745 100644 --- a/backend.native/tests/objcexport/expectedLazy.h +++ b/backend.native/tests/objcexport/expectedLazy.h @@ -1289,6 +1289,28 @@ __attribute__((swift_name("ValuesKt"))) + (int32_t)testAbstractInterfaceCall2X:(id)x __attribute__((swift_name("testAbstractInterfaceCall2(x:)"))); + (void)fooA:(KtKotlinAtomicReference *)a __attribute__((swift_name("foo(a:)"))); + (BOOL)testGH3825Gh3825:(id)gh3825 error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("testGH3825(gh3825:)"))); ++ (NSDictionary *)mapBoolean2String __attribute__((swift_name("mapBoolean2String()"))); ++ (NSDictionary *)mapByte2Short __attribute__((swift_name("mapByte2Short()"))); ++ (NSDictionary *)mapShort2Byte __attribute__((swift_name("mapShort2Byte()"))); ++ (NSDictionary *)mapInt2Long __attribute__((swift_name("mapInt2Long()"))); ++ (NSDictionary *)mapLong2Long __attribute__((swift_name("mapLong2Long()"))); ++ (NSDictionary *)mapUByte2Boolean __attribute__((swift_name("mapUByte2Boolean()"))); ++ (NSDictionary *)mapUShort2Byte __attribute__((swift_name("mapUShort2Byte()"))); ++ (NSDictionary *)mapUInt2Long __attribute__((swift_name("mapUInt2Long()"))); ++ (NSDictionary *)mapULong2Long __attribute__((swift_name("mapULong2Long()"))); ++ (NSDictionary *)mapFloat2Float __attribute__((swift_name("mapFloat2Float()"))); ++ (NSDictionary *)mapDouble2String __attribute__((swift_name("mapDouble2String()"))); ++ (KtMutableDictionary *)mutBoolean2String __attribute__((swift_name("mutBoolean2String()"))); ++ (KtMutableDictionary *)mutByte2Short __attribute__((swift_name("mutByte2Short()"))); ++ (KtMutableDictionary *)mutShort2Byte __attribute__((swift_name("mutShort2Byte()"))); ++ (KtMutableDictionary *)mutInt2Long __attribute__((swift_name("mutInt2Long()"))); ++ (KtMutableDictionary *)mutLong2Long __attribute__((swift_name("mutLong2Long()"))); ++ (KtMutableDictionary *)mutUByte2Boolean __attribute__((swift_name("mutUByte2Boolean()"))); ++ (KtMutableDictionary *)mutUShort2Byte __attribute__((swift_name("mutUShort2Byte()"))); ++ (KtMutableDictionary *)mutUInt2Long __attribute__((swift_name("mutUInt2Long()"))); ++ (KtMutableDictionary *)mutULong2Long __attribute__((swift_name("mutULong2Long()"))); ++ (KtMutableDictionary *)mutFloat2Float __attribute__((swift_name("mutFloat2Float()"))); ++ (KtMutableDictionary *)mutDouble2String __attribute__((swift_name("mutDouble2String()"))); @property (class, readonly) double dbl __attribute__((swift_name("dbl"))); @property (class, readonly) float flt __attribute__((swift_name("flt"))); @property (class, readonly) int32_t integer __attribute__((swift_name("integer"))); diff --git a/backend.native/tests/objcexport/values.kt b/backend.native/tests/objcexport/values.kt index 524a6c443c6..f70e39f201d 100644 --- a/backend.native/tests/objcexport/values.kt +++ b/backend.native/tests/objcexport/values.kt @@ -975,4 +975,29 @@ fun testGH3825(gh3825: GH3825) { assertFailsWith { gh3825.call2({ fail() }, true) } gh3825.call2({ count += 1 }, false) assertEquals(3, count) -} \ No newline at end of file +} + +fun mapBoolean2String(): Map = mapOf(Pair(false, "false"), Pair(true, "true")) +fun mapByte2Short(): Map = mapOf(Pair(-1, 2)) +fun mapShort2Byte(): Map = mapOf(Pair(-2, 1)) +fun mapInt2Long(): Map = mapOf(Pair(-4, 8)) +fun mapLong2Long(): Map = mapOf(Pair(-8, 8)) +fun mapUByte2Boolean(): Map = mapOf(Pair(0x80U, true)) +fun mapUShort2Byte(): Map = mapOf(Pair(0x8000U, 1)) +fun mapUInt2Long(): Map = mapOf(Pair(0x7FFF_FFFFU, 7), Pair(0x8000_0000U, 8)) +fun mapULong2Long(): Map = mapOf(Pair(0x8000_0000_0000_0000UL, 8)) +fun mapFloat2Float(): Map = mapOf(Pair(3.14f, 100f)) +fun mapDouble2String(): Map = mapOf(Pair(2.718281828459045, "2.718281828459045")) + +fun mutBoolean2String(): MutableMap = mutableMapOf(Pair(false, "false"), Pair(true, "true")) +fun mutByte2Short(): MutableMap = mutableMapOf(Pair(-1, 2)) +fun mutShort2Byte(): MutableMap = mutableMapOf(Pair(-2, 1)) +fun mutInt2Long(): MutableMap = mutableMapOf(Pair(-4, 8)) +fun mutLong2Long(): MutableMap = mutableMapOf(Pair(-8, 8)) +fun mutUByte2Boolean(): MutableMap = mutableMapOf(Pair(128U, true)) +fun mutUShort2Byte(): MutableMap = mutableMapOf(Pair(32768U, 1)) +fun mutUInt2Long(): MutableMap = mutableMapOf(Pair(0x8000_0000U, 8)) +fun mutULong2Long(): MutableMap = mutableMapOf(Pair(0x8000_0000_0000_0000UL, 8)) +fun mutFloat2Float(): MutableMap = mutableMapOf(Pair(3.14f, 100f)) +fun mutDouble2String(): MutableMap = mutableMapOf(Pair(2.718281828459045, "2.718281828459045")) + diff --git a/backend.native/tests/objcexport/values.swift b/backend.native/tests/objcexport/values.swift index b4af9338317..094c45e3e19 100644 --- a/backend.native/tests/objcexport/values.swift +++ b/backend.native/tests/objcexport/values.swift @@ -1233,6 +1233,46 @@ func testGH3825() throws { try assertEquals(actual: count, expected: 3) } + +func testMapsExport() throws { + // Original reproducer failed in different way for MutableMap (iOS 11) and Map (MacOS 10.14, iOS 13) + + try assertEquals(actual: ValuesKt.mapBoolean2String()[true], expected: "true") + try assertEquals(actual: ValuesKt.mapByte2Short()[-1], expected: 2) + try assertEquals(actual: ValuesKt.mapShort2Byte()[-2], expected: 1) + try assertEquals(actual: ValuesKt.mapInt2Long()[-4], expected: 8) + try assertEquals(actual: ValuesKt.mapLong2Long()[-8], expected: 8) + try assertEquals(actual: ValuesKt.mapUByte2Boolean()[128], expected: true) + try assertEquals(actual: ValuesKt.mapUShort2Byte()[0x8000], expected: 1) + try assertEquals(actual: ValuesKt.mapUInt2Long()[0x7FFFFFFF], expected: 7) + // the following samples require explicit cast to KotlinUInt or KotlinULong + try assertEquals(actual: ValuesKt.mapUInt2Long()[KotlinUInt(0x8000_0000)], expected: 8) + _ = ValuesKt.mapULong2Long() as! [KotlinULong: KotlinLong] // test cast + var u64: UInt64 = 0x8000_0000_0000_0000 + try assertEquals(actual: ValuesKt.mapULong2Long()[KotlinULong(value: u64)], expected: 8) + + _ = ValuesKt.mapFloat2Float() as! [KotlinFloat: KotlinFloat] // test cast + try assertEquals(actual: ValuesKt.mapFloat2Float()[3.14], expected: 100.0) + try assertEquals(actual: ValuesKt.mapDouble2String()[2.718281828459045], expected: "2.718281828459045") + + // test also explicit cast to [:] of primitiva types, e.g. [Int: Int] + try assertEquals(actual: (ValuesKt.mutBoolean2String() as! [Bool: String])[true], expected: "true") + try assertEquals(actual: (ValuesKt.mutByte2Short() as! [Int8: Int16])[-1], expected: 2) + try assertEquals(actual: (ValuesKt.mutShort2Byte() as! [Int16: Int8])[-2], expected: 1) + try assertEquals(actual: (ValuesKt.mutInt2Long() as! [Int: Int64])[-4], expected: 8) + try assertEquals(actual: (ValuesKt.mutLong2Long() as! [Int64: Int64])[-8], expected: 8) + + try assertEquals(actual: (ValuesKt.mutUByte2Boolean() as! [UInt8: Bool])[128], expected: true) + try assertEquals(actual: (ValuesKt.mutUShort2Byte() as! [UInt16: Int8])[0x8000], expected: 1) + // the following samples require explicit cast to KotlinUInt or KotlinULong + try assertEquals(actual: (ValuesKt.mutUInt2Long() as! [UInt: Int64])[UInt(0x8000_0000)], expected: 8) + + try assertEquals(actual: (ValuesKt.mutULong2Long() as! [UInt64: Int64])[u64], expected: 8) + + try assertEquals(actual: (ValuesKt.mutFloat2Float() as! [Float: Float])[3.14], expected: 100.0) + try assertEquals(actual: (ValuesKt.mutDouble2String() as! [Double: String])[2.718281828459045], expected: "2.718281828459045") +} + // -------- Execution of the test -------- class ValuesTests : SimpleTestProvider { @@ -1288,6 +1328,7 @@ class ValuesTests : SimpleTestProvider { test("TestGH3525", testGH3525) test("TestStringConversion", testStringConversion) test("TestGH3825", testGH3825) + test("TestMapsExport", testMapsExport) // Stress test, must remain the last one: test("TestGH2931", testGH2931) diff --git a/runtime/src/objc/cpp/ObjCExportNumbers.mm b/runtime/src/objc/cpp/ObjCExportNumbers.mm index 3795e329890..e721dab6991 100644 --- a/runtime/src/objc/cpp/ObjCExportNumbers.mm +++ b/runtime/src/objc/cpp/ObjCExportNumbers.mm @@ -122,6 +122,10 @@ private fun genBoolean(): String = """ BOOL value_; } +- (void)getValue:(void *)value { + *(BOOL*)value = value_; +} + - (instancetype)initWithBool:(BOOL)value { self = [super init]; value_ = value; @@ -167,6 +171,10 @@ private fun genInteger( $cType value_; } +- (void)getValue:(void *)value { + *($cType*)value = value_; +} + - (instancetype)initWith${kind.capitalize()}:($cType)value { self = [super init]; value_ = value; @@ -218,6 +226,10 @@ private fun genFloating( $cType value_; } +- (void)getValue:(void *)value { + *($cType*)value = value_; +} + - (instancetype)initWith${kind.capitalize()}:($cType)value { self = [super init]; value_ = value; @@ -270,6 +282,10 @@ ${if (cType != "double") """ BOOL value_; } +- (void)getValue:(void *)value { + *(BOOL*)value = value_; +} + - (instancetype)initWithBool:(BOOL)value { self = [super init]; value_ = value; @@ -307,6 +323,10 @@ ${if (cType != "double") """ char value_; } +- (void)getValue:(void *)value { + *(char*)value = value_; +} + - (instancetype)initWithChar:(char)value { self = [super init]; value_ = value; @@ -347,6 +367,10 @@ ${if (cType != "double") """ short value_; } +- (void)getValue:(void *)value { + *(short*)value = value_; +} + - (instancetype)initWithShort:(short)value { self = [super init]; value_ = value; @@ -387,6 +411,10 @@ ${if (cType != "double") """ int value_; } +- (void)getValue:(void *)value { + *(int*)value = value_; +} + - (instancetype)initWithInt:(int)value { self = [super init]; value_ = value; @@ -427,6 +455,10 @@ ${if (cType != "double") """ long long value_; } +- (void)getValue:(void *)value { + *(long long*)value = value_; +} + - (instancetype)initWithLongLong:(long long)value { self = [super init]; value_ = value; @@ -467,6 +499,10 @@ ${if (cType != "double") """ unsigned char value_; } +- (void)getValue:(void *)value { + *(unsigned char*)value = value_; +} + - (instancetype)initWithUnsignedChar:(unsigned char)value { self = [super init]; value_ = value; @@ -507,6 +543,10 @@ ${if (cType != "double") """ unsigned short value_; } +- (void)getValue:(void *)value { + *(unsigned short*)value = value_; +} + - (instancetype)initWithUnsignedShort:(unsigned short)value { self = [super init]; value_ = value; @@ -547,6 +587,10 @@ ${if (cType != "double") """ unsigned int value_; } +- (void)getValue:(void *)value { + *(unsigned int*)value = value_; +} + - (instancetype)initWithUnsignedInt:(unsigned int)value { self = [super init]; value_ = value; @@ -587,6 +631,10 @@ ${if (cType != "double") """ unsigned long long value_; } +- (void)getValue:(void *)value { + *(unsigned long long*)value = value_; +} + - (instancetype)initWithUnsignedLongLong:(unsigned long long)value { self = [super init]; value_ = value; @@ -627,6 +675,10 @@ ${if (cType != "double") """ float value_; } +- (void)getValue:(void *)value { + *(float*)value = value_; +} + - (instancetype)initWithFloat:(float)value { self = [super init]; value_ = value; @@ -674,6 +726,10 @@ ${if (cType != "double") """ double value_; } +- (void)getValue:(void *)value { + *(double*)value = value_; +} + - (instancetype)initWithDouble:(double)value { self = [super init]; value_ = value; @@ -707,4 +763,4 @@ ${if (cType != "double") """ @end; -#endif // KONAN_OBJC_INTEROP \ No newline at end of file +#endif // KONAN_OBJC_INTEROP