From 080ecf0d11e150a9b43a14673dc76faf9e2ef4f3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Tue, 25 Sep 2018 14:41:30 +0300 Subject: [PATCH] Properly handle extensions to inlined classes when producing framework See https://github.com/JetBrains/kotlin-native/issues/2120#issuecomment-424262886 --- .../backend/konan/objcexport/ObjCExportMapper.kt | 9 +++------ backend.native/tests/framework/values/values.kt | 14 +++++++++++++- .../tests/framework/values/values.swift | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt index b11fa3e8205..a070703c84b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt @@ -11,12 +11,10 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors import org.jetbrains.kotlin.backend.konan.descriptors.isArray import org.jetbrains.kotlin.backend.konan.descriptors.isInterface -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -34,18 +32,17 @@ internal abstract class ObjCExportMapper { } } -private fun ObjCExportMapper.isRepresentedAsObjCInterface(descriptor: ClassDescriptor): Boolean = - !descriptor.isInterface && !isSpecialMapped(descriptor) - internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescriptor): ClassDescriptor? { if (descriptor.dispatchReceiverParameter != null) return null val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null + // FIXME: this code must rely on type mapping instead of copying its logic. + if (extensionReceiverType.isObjCObjectType()) return null val erasedClass = extensionReceiverType.getErasedTypeClass() - return if (this.isRepresentedAsObjCInterface(erasedClass)) { + return if (!erasedClass.isInterface && !erasedClass.isInlined() && !this.isSpecialMapped(erasedClass)) { erasedClass } else { // E.g. receiver is protocol, or some type with custom mapping. diff --git a/backend.native/tests/framework/values/values.kt b/backend.native/tests/framework/values/values.kt index b4728be5b22..f3cd7e72a15 100644 --- a/backend.native/tests/framework/values/values.kt +++ b/backend.native/tests/framework/values/values.kt @@ -132,6 +132,9 @@ fun Any?.toString(): String = this?.toString() ?: "null" fun Any?.print() = println(this.toString()) +fun Char.boxChar(): Char? = this +fun Char?.isA(): Boolean = (this == 'A') + // Lambdas val sumLambda = { x: Int, y: Int -> x + y } @@ -223,4 +226,13 @@ fun box(ic2: IC2): Any = ic2 fun box(ic3: IC3): Any = ic3 fun concatenateInlineClassValues(ic1: IC1, ic1N: IC1?, ic2: IC2, ic2N: IC2?, ic3: IC3, ic3N: IC3?): String = - "${ic1.value} ${ic1N?.value} ${ic2.value} ${ic2N?.value} ${ic3.value} ${ic3N?.value}" \ No newline at end of file + "${ic1.value} ${ic1N?.value} ${ic2.value} ${ic2N?.value} ${ic3.value} ${ic3N?.value}" + +fun IC1.getValue1() = this.value +fun IC1?.getValueOrNull1() = this?.value + +fun IC2.getValue2() = value +fun IC2?.getValueOrNull2() = this?.value + +fun IC3.getValue3() = value +fun IC3?.getValueOrNull3() = this?.value diff --git a/backend.native/tests/framework/values/values.swift b/backend.native/tests/framework/values/values.swift index 9969f50285b..6938cdcc4ed 100644 --- a/backend.native/tests/framework/values/values.swift +++ b/backend.native/tests/framework/values/values.swift @@ -314,6 +314,11 @@ func testAnyPrint() throws { print("END") } +func testCharExtensions() throws { + try assertTrue(ValuesKt.isA(ValuesKt.boxChar(65))) + try assertFalse(ValuesKt.isA(ValuesKt.boxChar(66))) +} + func testLambda() throws { try assertEquals(actual: ValuesKt.sumLambda(3, 4), expected: 7) } @@ -404,6 +409,15 @@ func testInlineClasses() throws { actual: ValuesKt.concatenateInlineClassValues(ic1: ic1, ic1N: nil, ic2: ic2, ic2N: nil, ic3: nil, ic3N: nil), expected: "42 null foo null null null" ) + + try assertEquals(actual: ValuesKt.getValue1(ic1), expected: 42) + try assertEquals(actual: ValuesKt.getValueOrNull1(ic1N) as! Int, expected: 17) + + try assertEquals(actual: ValuesKt.getValue2(ic2), expected: "foo") + try assertEquals(actual: ValuesKt.getValueOrNull2(ic2N), expected: "bar") + + try assertEquals(actual: ValuesKt.getValue3(ic3), expected: ic3) + try assertEquals(actual: ValuesKt.getValueOrNull3(ic3N), expected: nil) } // -------- Execution of the test -------- @@ -431,6 +445,7 @@ class ValuesTests : TestProvider { TestCase(name: "TestStringExtension", method: withAutorelease(testStrExtFun)), TestCase(name: "TestAnyToString", method: withAutorelease(testAnyToString)), TestCase(name: "TestAnyPrint", method: withAutorelease(testAnyPrint)), + TestCase(name: "TestCharExtensions", method: withAutorelease(testCharExtensions)), TestCase(name: "TestLambda", method: withAutorelease(testLambda)), TestCase(name: "TestInterfaceExtension", method: withAutorelease(testInterfaceExtension)), TestCase(name: "TestClassInstances", method: withAutorelease(testClassInstances)),