[Interop] Introduce InteropMangler (#3574)

To be able to reference external declarations from KLIBs we need to compute UniqId
for them. Thus we add InteropMangler that can compute mangle
(which in turn is used for UniqId) for declarations from NativeIndex.
This commit is contained in:
Sergey Bogolepov
2019-11-13 14:48:00 +07:00
committed by GitHub
parent 296d4e72e8
commit 54f4a1216e
3 changed files with 267 additions and 0 deletions
+4
View File
@@ -35,6 +35,10 @@ dependencies {
compile project(':Interop:Indexer')
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
compile project(path: ":endorsedLibraries:kotlinx.cli", configuration: "jvmRuntimeElements")
testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$buildKotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test:$buildKotlinVersion"
}
compileKotlin {
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.native.interop.indexer.*
/**
* Allows to nest declaration mangles.
*/
sealed class ManglingContext {
abstract val prefix: String
class Module(name: String) : ManglingContext() {
override val prefix: String = "$name."
}
/**
* Used to represent containers like structures, classes, etc.
*/
class Entity(name: String, parentContext: ManglingContext = Empty) : ManglingContext() {
override val prefix: String = "${parentContext.prefix}$name."
}
object Empty : ManglingContext() {
override val prefix: String = ""
}
}
/**
* We need a way to refer external declarations from Kotlin Libraries
* by stable unique identifier. To be able to do it, we mangle them.
*/
interface InteropMangler {
val StructDecl.uniqueSymbolName: String
val EnumDef.uniqueSymbolName: String
val ObjCClass.uniqueSymbolName: String
val ObjCClass.metaClassUniqueSymbolName: String
val ObjCProtocol.uniqueSymbolName: String
val ObjCProtocol.metaClassUniqueSymbolName: String
val ObjCMethod.uniqueSymbolName: String
val ObjCProperty.uniqueSymbolName: String
val TypedefDef.uniqueSymbolName: String
val FunctionDecl.uniqueSymbolName: String
val ConstantDef.uniqueSymbolName: String
val WrappedMacroDef.uniqueSymbolName: String
val GlobalDecl.uniqueSymbolName: String
}
/**
* Mangler that mimics behaviour of the one from the Kotlin compiler.
*/
class KotlinLikeInteropMangler(val context: ManglingContext = ManglingContext.Empty) : InteropMangler {
private val prefix = context.prefix
override val StructDecl.uniqueSymbolName: String
get() = "structdecl:$prefix$spelling"
override val EnumDef.uniqueSymbolName: String
get() = "enumdef:$prefix$spelling"
override val ObjCClass.uniqueSymbolName: String
get() = "objcclass:$prefix$name"
override val ObjCClass.metaClassUniqueSymbolName: String
get() = "objcmetaclass:$prefix$name"
override val ObjCProtocol.uniqueSymbolName: String
get() = "objcprotocol:$prefix$name"
override val ObjCProtocol.metaClassUniqueSymbolName: String
get() = "objcmetaprotocol:$prefix$name"
override val ObjCMethod.uniqueSymbolName: String
get() = "objcmethod:$prefix$selector"
override val ObjCProperty.uniqueSymbolName: String
get() = "objcproperty:$prefix$name"
override val TypedefDef.uniqueSymbolName: String
get() = "typedef:$prefix$name"
override val FunctionDecl.uniqueSymbolName: String
get() = "funcdecl:$prefix$functionName"
override val ConstantDef.uniqueSymbolName: String
get() = "macrodef:$prefix$name"
override val WrappedMacroDef.uniqueSymbolName: String
get() = "macrodef:$prefix$name"
override val GlobalDecl.uniqueSymbolName: String
get() = "globaldecl:$prefix$name"
private val FunctionDecl.functionName: String
get() = name
}
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.native.interop.indexer.*
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
private val fakeLocation = Location(HeaderId("doesntmatter"))
private class FakeObjCClass(name: String) : ObjCClass(name) {
override val location: Location = fakeLocation
override val protocols: List<ObjCProtocol> = emptyList()
override val methods: List<ObjCMethod> = emptyList()
override val properties: List<ObjCProperty> = emptyList()
override val isForwardDeclaration: Boolean = false
override val binaryName: String? = null
override val baseClass: ObjCClass? = null
}
private class FakeStructDecl(spelling: String) : StructDecl(spelling) {
override val def: StructDef? = null
override val location: Location = fakeLocation
}
class ManglingSmokeTests {
private val mangler: InteropMangler = KotlinLikeInteropMangler()
private val nsStringClass = FakeObjCClass("NSString")
private val int32Type = IntegerType(32, true, "int32_t")
@Test
fun `typedef should not affect mangling`() {
val cBoolTypedef = Typedef(TypedefDef(CBoolType, "MyBool", fakeLocation))
val functions = listOf(
FunctionDecl("a", listOf(Parameter("a", CBoolType, false)), CBoolType, "", false, false),
FunctionDecl("a", listOf(Parameter("b", CBoolType, false)), cBoolTypedef, "", false, false),
FunctionDecl("a", listOf(Parameter("a", cBoolTypedef, false)), CBoolType, "", false, false),
FunctionDecl("a", listOf(Parameter("a", cBoolTypedef, false)), cBoolTypedef, "", false, false)
)
with (mangler) {
functions.reduce { left, right ->
assertEquals(left.uniqueSymbolName, right.uniqueSymbolName)
left
}
}
}
@Test
fun `mangling should not depend on parameter names`() {
val functionDeclarationA = FunctionDecl("a", listOf(Parameter("a", CBoolType, false)), CBoolType, "", false, false)
val functionDeclarationB = FunctionDecl("a", listOf(Parameter("b", CBoolType, false)), CBoolType, "", false, false)
with (mangler) {
assertEquals(functionDeclarationA.uniqueSymbolName, functionDeclarationB.uniqueSymbolName)
}
}
@Test
fun `parameter type name embedded in function name`() {
val functionDeclarationA = FunctionDecl("a", listOf(Parameter("a", CharType, false)), CBoolType, "", false, false)
val functionDeclarationB = FunctionDecl("achar", emptyList(), CBoolType, "", false, false)
with (mangler) {
assertNotEquals(functionDeclarationA.uniqueSymbolName, functionDeclarationB.uniqueSymbolName)
}
}
@Test
fun `objc methods with same names but different parameters`() {
val nsStringPtrType = ObjCObjectPointer(nsStringClass, ObjCPointer.Nullability.Nullable, listOf())
val methodA = ObjCMethod("name::", "v28@0:8@16i24",
listOf(Parameter("name", nsStringPtrType, false), Parameter("age", int32Type, false)), VoidType,
false, false, false, false, false, false, false)
val methodB = ObjCMethod("name:", "v20@0:8i16",
listOf(Parameter("age", int32Type, false)), VoidType,
false, false, false, false, false, false, false)
with (mangler) {
assertNotEquals(methodA.uniqueSymbolName, methodB.uniqueSymbolName)
}
}
@Test
fun `objc methods with same names but different parameter names`() {
val nsStringPtrType = ObjCObjectPointer(nsStringClass, ObjCPointer.Nullability.Nullable, listOf())
val methodA = ObjCMethod("desc:a:", "v28@0:8@16i24",
listOf(Parameter("name", int32Type, false), Parameter("a", int32Type, false)), VoidType,
false, false, false, false, false, false, false)
val methodB = ObjCMethod("desc:b:", "v28@0:8@16i24",
listOf(Parameter("name", int32Type, false), Parameter("b", int32Type, false)), VoidType,
false, false, false, false, false, false, false)
with (mangler) {
assertNotEquals(methodA.uniqueSymbolName, methodB.uniqueSymbolName)
}
}
@Test
fun `struct smoke`() {
val structA = FakeStructDecl("struct_name")
val structB = FakeStructDecl("structName")
with (mangler) {
assertNotEquals(structA.uniqueSymbolName, structB.uniqueSymbolName)
}
}
@Test
fun `constants smoke`() {
val constant = IntegerConstantDef("DEBUG", CBoolType, 1)
val macro = WrappedMacroDef("DEBUG", CBoolType)
with (mangler) {
assertEquals(constant.uniqueSymbolName, macro.uniqueSymbolName)
}
}
@Test
fun `different modules`() {
val moduleA = ManglingContext.Module("A")
val moduleB = ManglingContext.Module("B")
val manglerA = KotlinLikeInteropMangler(moduleA)
val manglerB = KotlinLikeInteropMangler(moduleB)
val declaration = WrappedMacroDef("DEBUG", CBoolType)
assertNotEquals(
with(manglerA) { declaration.uniqueSymbolName },
with(manglerB) { declaration.uniqueSymbolName }
)
}
@Test
fun `different classes`() {
val module = ManglingContext.Module("Foundation")
val classA = ManglingContext.Entity("NSArray", module)
val classB = ManglingContext.Entity("NSMutableArray", module)
val manglerA = KotlinLikeInteropMangler(classA)
val manglerB = KotlinLikeInteropMangler(classB)
val getter = ObjCMethod("size", "Q16@0:8", emptyList(), IntegerType(64, false, "unsigned long"),
false, false, false, false, false, false, false)
val property = ObjCProperty("size", getter, null)
assertNotEquals(
with(manglerA) { property.uniqueSymbolName },
with(manglerB) { property.uniqueSymbolName }
)
}
}