From ee7917cf364db537bb808facd421e344349b1387 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 28 Feb 2020 15:29:52 +0300 Subject: [PATCH] [Interop] Additional simple mangling of special names such as Companion (#3919) --- .../kotlin/native/interop/gen/CodeUtils.kt | 16 ++++++++++++ .../kotlin/native/interop/gen/Mappings.kt | 1 + .../kotlin/native/interop/gen/ObjCStubs.kt | 2 +- .../interop/gen/StubIrElementBuilders.kt | 14 +++++----- backend.native/tests/build.gradle | 20 ++++++++++++++ .../tests/interop/basics/mangling.def | 6 +++++ .../tests/interop/basics/mangling.kt | 9 +++++++ .../tests/interop/basics/mangling2.def | 6 +++++ .../tests/interop/basics/mangling2.kt | 9 +++++++ backend.native/tests/interop/objc/smoke.h | 26 ++++++++++++++++++- backend.native/tests/interop/objc/smoke.kt | 16 ++++++++++++ backend.native/tests/interop/objc/smoke.m | 8 +++++- 12 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 backend.native/tests/interop/basics/mangling.def create mode 100644 backend.native/tests/interop/basics/mangling.kt create mode 100644 backend.native/tests/interop/basics/mangling2.def create mode 100644 backend.native/tests/interop/basics/mangling2.kt diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CodeUtils.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CodeUtils.kt index 8a84cfee1c4..6ad1baf79d9 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CodeUtils.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CodeUtils.kt @@ -44,6 +44,22 @@ fun String.asSimpleName(): String = if (this in kotlinKeywords || this.contains( this } +/** + * Yet another mangler, particularly to avoid secondary clash, e.g. when a property + * in prototype (interface) is mangled and that will cause another clash in the class + * which implements this interface. + * Rationale: keep algorithm simple but use the mangling characters which are rare + * in normal code, and keep mangling easy readable. + */ +internal fun mangleSimple(name: String): String { + val reserved = setOf("Companion") + val postfix = "\$" + return if (name in reserved) + "$name$postfix" + else + name +} + /** * Returns the expression to be parsed by Kotlin as string literal with given contents, * i.e. transforms `foo$bar` to `"foo\$bar"`. diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt index 0d7c7223823..80b9beada07 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt @@ -407,6 +407,7 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when is EnumType -> { val pkg = declarationMapper.getPackageFor(type.def) val kotlinName = declarationMapper.getKotlinNameForValue(type.def) + .let { mangleSimple(it) } // enum class requires additional mangling when { declarationMapper.isMappedToStrict(type.def) -> { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 6f9b895f787..0e5f9f8f653 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -583,7 +583,7 @@ private class ObjCPropertyStubBuilder( is ObjCCategory -> ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass)) } val origin = StubOrigin.ObjCProperty(property, container) - return listOf(PropertyStub(property.name, kotlinType.toStubIrType(), kind, modality, receiver, origin = origin)) + return listOf(PropertyStub(mangleSimple(property.name), kotlinType.toStubIrType(), kind, modality, receiver, origin = origin)) } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt index 4f73a60c624..1e5ce84c82a 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt @@ -88,6 +88,7 @@ internal class StructStubBuilder( val fieldRefType = context.mirror(field.type) val unwrappedFieldType = field.type.unwrapTypedefs() val origin = StubOrigin.StructMember(field) + val fieldName = mangleSimple(field.name) if (unwrappedFieldType is ArrayType) { val type = (fieldRefType as TypeMirror.ByValue).valueType val annotations = if (platform == KotlinPlatform.JVM) { @@ -103,7 +104,7 @@ internal class StructStubBuilder( } val kind = PropertyStub.Kind.Val(getter) // TODO: Should receiver be added? - PropertyStub(field.name, type.toStubIrType(), kind, annotations = annotations, origin = origin) + PropertyStub(fieldName, type.toStubIrType(), kind, annotations = annotations, origin = origin) } else { val pointedType = fieldRefType.pointedType.toStubIrType() val pointedTypeArgument = TypeArgumentStub(pointedType) @@ -121,14 +122,14 @@ internal class StructStubBuilder( } } val kind = PropertyStub.Kind.Var(getter, setter) - PropertyStub(field.name, fieldRefType.argType.toStubIrType(), kind, origin = origin) + PropertyStub(fieldName, fieldRefType.argType.toStubIrType(), kind, origin = origin) } else { val accessor = when (context.generationMode) { GenerationMode.SOURCE_CODE -> PropertyAccessor.Getter.MemberAt(offset, hasValueAccessor = false) GenerationMode.METADATA -> PropertyAccessor.Getter.ExternalGetter(listOf(AnnotationStub.CStruct.MemberAt(offset))) } val kind = PropertyStub.Kind.Val(accessor) - PropertyStub(field.name, pointedType, kind, origin = origin) + PropertyStub(fieldName, pointedType, kind, origin = origin) } } } catch (e: Throwable) { @@ -141,6 +142,7 @@ internal class StructStubBuilder( val typeInfo = typeMirror.info val kotlinType = typeMirror.argType val signed = field.type.isIntegerTypeSigned() + val fieldName = mangleSimple(field.name) val kind = when (context.generationMode) { GenerationMode.SOURCE_CODE -> { val readBits = PropertyAccessor.Getter.ReadBits(field.offset, field.size, signed) @@ -155,7 +157,7 @@ internal class StructStubBuilder( PropertyStub.Kind.Var(readBits, writeBits) } } - PropertyStub(field.name, kotlinType.toStubIrType(), kind, origin = StubOrigin.StructMember(field)) + PropertyStub(fieldName, kotlinType.toStubIrType(), kind, origin = StubOrigin.StructMember(field)) } val superClass = context.platform.getRuntimeType("CStructVar") @@ -277,7 +279,7 @@ internal class EnumStubBuilder( .mapIndexed { index, constant -> val literal = context.tryCreateIntegralStub(enumDef.baseType, constant.value) ?: error("Cannot create enum value ${constant.value} of type ${enumDef.baseType}") - val entry = EnumEntryStub(constant.name, literal, StubOrigin.EnumEntry(constant), index) + val entry = EnumEntryStub(mangleSimple(constant.name), literal, StubOrigin.EnumEntry(constant), index) val aliases = aliasConstants .filter { it.value == constant.value } .map { constructAliasProperty(it, entry) } @@ -715,4 +717,4 @@ internal class TypedefStubBuilder( } } } -} \ No newline at end of file +} diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index b8cbffe2727..1066fb4a71a 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3348,6 +3348,14 @@ createInterop("ccallbacksAndVarargs") { it.defFile 'interop/basics/ccallbacksAndVarargs.def' } +createInterop("cmangling") { + it.defFile 'interop/basics/mangling.def' +} + +createInterop("cmangling2") { + it.defFile 'interop/basics/mangling2.def' +} + if (PlatformInfo.isAppleTarget(project)) { createInterop("objcSmoke") { it.defFile 'interop/objc/objcSmoke.def' @@ -3505,6 +3513,18 @@ interopTest("interop_types") { interop = 'ctypes' } +interopTest("interop_mangling") { + disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. + source = "interop/basics/mangling.kt" + interop = 'cmangling' +} + +interopTest("interop_mangling2") { + disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. + source = "interop/basics/mangling2.kt" + interop = 'cmangling2' +} + interopTest("interop_values") { disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. source = "interop/basics/values.kt" diff --git a/backend.native/tests/interop/basics/mangling.def b/backend.native/tests/interop/basics/mangling.def new file mode 100644 index 00000000000..99fe71b2fa1 --- /dev/null +++ b/backend.native/tests/interop/basics/mangling.def @@ -0,0 +1,6 @@ +--- + +// test mangling of special names + +enum _Companion {Companion, Any}; +enum _Companion companion = Companion; diff --git a/backend.native/tests/interop/basics/mangling.kt b/backend.native/tests/interop/basics/mangling.kt new file mode 100644 index 00000000000..3bf58ae1b84 --- /dev/null +++ b/backend.native/tests/interop/basics/mangling.kt @@ -0,0 +1,9 @@ +import kotlinx.cinterop.* +import kotlin.test.* +import mangling.* + +fun main() { + companion = _Companion.`Companion$` + assertEquals(_Companion.`Companion$`, companion) +} + diff --git a/backend.native/tests/interop/basics/mangling2.def b/backend.native/tests/interop/basics/mangling2.def new file mode 100644 index 00000000000..60dec063505 --- /dev/null +++ b/backend.native/tests/interop/basics/mangling2.def @@ -0,0 +1,6 @@ +--- + +// test mangling of special names + +enum Companion {One, Two}; + diff --git a/backend.native/tests/interop/basics/mangling2.kt b/backend.native/tests/interop/basics/mangling2.kt new file mode 100644 index 00000000000..3179e430f5e --- /dev/null +++ b/backend.native/tests/interop/basics/mangling2.kt @@ -0,0 +1,9 @@ +import kotlinx.cinterop.* +import kotlin.test.* +import mangling2.* + +fun main() { + val mangled = `Companion$`.Two + assertEquals(`Companion$`.Two, mangled) +} + diff --git a/backend.native/tests/interop/objc/smoke.h b/backend.native/tests/interop/objc/smoke.h index 0d96a914897..ef6c4f6d103 100644 --- a/backend.native/tests/interop/objc/smoke.h +++ b/backend.native/tests/interop/objc/smoke.h @@ -248,4 +248,28 @@ extern BOOL customStringDeallocated; @property int value; - (int)instanceMethod; + (int)classMethod:(int)first :(int)second; -@end; \ No newline at end of file +@end; + +// [KT-36067] cinterop tool fails when there is a structure member named Companion + +struct EnumFieldMangleStruct { + enum {Companion, Any} smth; +}; +struct EnumFieldMangleStruct enumMangledStruct = { Any }; + +struct MyStruct { + int Companion; // simple clash + int _Companion; + int $_Companion; + int super; +}; + +struct MyStruct myStruct = {11, 12, 13, 14}; + +@protocol Proto +@property int Companion; // clash on implementing +@end; + +@interface FooMangled : NSObject +//- (void) CompanionS; // mangleSimple does not support this: it may clash after mangling +@end; diff --git a/backend.native/tests/interop/objc/smoke.kt b/backend.native/tests/interop/objc/smoke.kt index 01289d53541..801f53f092a 100644 --- a/backend.native/tests/interop/objc/smoke.kt +++ b/backend.native/tests/interop/objc/smoke.kt @@ -33,6 +33,7 @@ fun run() { testLocalizedStrings() testConstructorReturnsNull() testCallableReferences() + testMangling() assertEquals(2, ForwardDeclaredEnum.TWO.value) @@ -514,6 +515,21 @@ fun testCallableReferences() { assertEquals(42, boundInstanceMethodRef()) } +fun testMangling() { + assertEquals(11, myStruct.`Companion$`) + assertEquals(12, myStruct._Companion) + assertEquals(13, myStruct.`$_Companion`) + assertEquals(14, myStruct.`super`) + + val objc = FooMangled() + objc.`Companion$` = 99 + assertEquals(99, objc.Companion()) + assertEquals(99, objc.`Companion$`) + + enumMangledStruct.smth = Companion + assertEquals(Companion, enumMangledStruct.smth) +} + private val Any.objCClassName: String get() = object_getClassName(this)!!.toKString() diff --git a/backend.native/tests/interop/objc/smoke.m b/backend.native/tests/interop/objc/smoke.m index 5197ac80827..628f20af0ed 100644 --- a/backend.native/tests/interop/objc/smoke.m +++ b/backend.native/tests/interop/objc/smoke.m @@ -335,4 +335,10 @@ BOOL customStringDeallocated = NO; + (int)classMethod:(int)first :(int)second { return first + second; } -@end; \ No newline at end of file +@end; + + +// [KT-36067] mangling +@implementation FooMangled : NSObject +@synthesize Companion; +@end;