[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:
@@ -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 {
|
||||
|
||||
+101
@@ -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
|
||||
}
|
||||
+162
@@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user