Improve name clash resolution in interop stub generation

* Handle more keywords.
* Handle more identifier usages.
* Use backticks to escape identifiers that clash with keywords.
This commit is contained in:
Svyatoslav Scherbina
2017-03-23 18:57:23 +03:00
committed by SvyatoslavScherbina
parent ff8acd21e6
commit 7487364bcd
@@ -23,11 +23,19 @@ class StubGenerator(
val excludedFunctions: Set<String> val excludedFunctions: Set<String>
get() = configuration.excludedFunctions get() = configuration.excludedFunctions
val keywords = setOf("object", "val") // TODO val keywords = setOf(
"as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in",
"interface", "is", "null", "object", "package", "return", "super", "this", "throw",
"true", "try", "typealias", "val", "var", "when", "while"
)
fun String.mangleIfKeyword(): String { /**
* For this identifier constructs the string to be parsed by Kotlin as `SimpleName`
* defined [here](https://kotlinlang.org/docs/reference/grammar.html#SimpleName).
*/
fun String.asSimpleName(): String {
return if (this in keywords) { return if (this in keywords) {
"_" + this "`$this`"
} else { } else {
this this
} }
@@ -352,18 +360,18 @@ class StubGenerator(
private fun mirror(type: Type): TypeMirror = when (type) { private fun mirror(type: Type): TypeMirror = when (type) {
is PrimitiveType -> mirror(type) is PrimitiveType -> mirror(type)
is RecordType -> byRefTypeMirror(type.kotlinName) is RecordType -> byRefTypeMirror(type.kotlinName.asSimpleName())
is EnumType -> when { is EnumType -> when {
type.def.isStrictEnum -> { type.def.isStrictEnum -> {
val className = type.def.kotlinName val classSimpleName = type.def.kotlinName.asSimpleName()
val info = TypeInfo.Enum(className, type.def.baseType.kotlinType) val info = TypeInfo.Enum(classSimpleName, type.def.baseType.kotlinType)
TypeMirror.ByValue("$className.Var", info, className) TypeMirror.ByValue("$classSimpleName.Var", info, classSimpleName)
} }
!type.def.isAnonymous -> { !type.def.isAnonymous -> {
val baseTypeMirror = mirror(type.def.baseType) val baseTypeMirror = mirror(type.def.baseType)
val name = type.def.kotlinName val name = type.def.kotlinName
TypeMirror.ByValue("${name}Var", baseTypeMirror.info, name) TypeMirror.ByValue("${name}Var", baseTypeMirror.info, name.asSimpleName())
} }
else -> mirror(type.def.baseType) else -> mirror(type.def.baseType)
} }
@@ -392,8 +400,8 @@ class StubGenerator(
val baseType = mirror(type.def.aliased) val baseType = mirror(type.def.aliased)
val name = type.def.name val name = type.def.name
when (baseType) { when (baseType) {
is TypeMirror.ByValue -> TypeMirror.ByValue("${name}Var", baseType.info, name) is TypeMirror.ByValue -> TypeMirror.ByValue("${name}Var", baseType.info, name.asSimpleName())
is TypeMirror.ByRef -> TypeMirror.ByRef(name, baseType.info) is TypeMirror.ByRef -> TypeMirror.ByRef(name.asSimpleName(), baseType.info)
} }
} }
@@ -566,8 +574,7 @@ class StubGenerator(
return return
} }
val className = decl.kotlinName block("class ${decl.kotlinName.asSimpleName()}(override val rawPtr: NativePtr) : CStructVar()") {
block("class $className(override val rawPtr: NativePtr) : CStructVar()") {
out("") out("")
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
out("") out("")
@@ -577,11 +584,11 @@ class StubGenerator(
assert(field.offset % 8 == 0L) assert(field.offset % 8 == 0L)
val offset = field.offset / 8 val offset = field.offset / 8
val fieldRefType = mirror(field.type) val fieldRefType = mirror(field.type)
out("val ${field.name}: ${fieldRefType.pointedTypeName}") out("val ${field.name.asSimpleName()}: ${fieldRefType.pointedTypeName}")
out(" get() = memberAt($offset)") out(" get() = memberAt($offset)")
out("") out("")
} catch (e: Throwable) { } catch (e: Throwable) {
println("Warning: cannot generate definition for field $className.${field.name}") println("Warning: cannot generate definition for field ${decl.kotlinName}.${field.name}")
} }
} }
} }
@@ -591,8 +598,7 @@ class StubGenerator(
* Produces to [out] the definition of Kotlin class representing the reference to given forward (incomplete) struct. * Produces to [out] the definition of Kotlin class representing the reference to given forward (incomplete) struct.
*/ */
private fun generateForwardStruct(s: StructDecl) { private fun generateForwardStruct(s: StructDecl) {
val className = s.kotlinName out("class ${s.kotlinName.asSimpleName()}(override val rawPtr: NativePtr) : COpaque")
out("class $className(override val rawPtr: NativePtr) : COpaque")
} }
/** /**
@@ -606,19 +612,20 @@ class StubGenerator(
val baseTypeMirror = mirror(e.baseType) val baseTypeMirror = mirror(e.baseType)
block("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType})") { block("enum class ${e.kotlinName.asSimpleName()}(val value: ${e.baseType.kotlinType})") {
e.constants.forEach { e.constants.forEach {
out("${it.name}(${it.value}),") out("${it.name.asSimpleName()}(${it.value}),")
} }
out(";") out(";")
out("") out("")
block("companion object") { block("companion object") {
out("fun byValue(value: ${e.baseType.kotlinType}) = ${e.kotlinName}.values().find { it.value == value }!!") out("fun byValue(value: ${e.baseType.kotlinType}) = " +
"${e.kotlinName.asSimpleName()}.values().find { it.value == value }!!")
} }
out("") out("")
block("class Var(override val rawPtr: NativePtr) : CEnumVar()") { block("class Var(override val rawPtr: NativePtr) : CEnumVar()") {
out("companion object : Type(${baseTypeMirror.pointedTypeName}.size.toInt())") out("companion object : Type(${baseTypeMirror.pointedTypeName}.size.toInt())")
out("var value: ${e.kotlinName}") out("var value: ${e.kotlinName.asSimpleName()}")
out(" get() = byValue(this.reinterpret<${baseTypeMirror.pointedTypeName}>().value)") out(" get() = byValue(this.reinterpret<${baseTypeMirror.pointedTypeName}>().value)")
out(" set(value) { this.reinterpret<${baseTypeMirror.pointedTypeName}>().value = value.value }") out(" set(value) { this.reinterpret<${baseTypeMirror.pointedTypeName}>().value = value.value }")
} }
@@ -666,7 +673,7 @@ class StubGenerator(
} }
for (constant in constants) { for (constant in constants) {
out("val ${constant.name}: $typeName = ${constant.value}") out("val ${constant.name.asSimpleName()}: $typeName = ${constant.value}")
} }
} }
@@ -676,10 +683,10 @@ class StubGenerator(
when (baseMirror) { when (baseMirror) {
is TypeMirror.ByValue -> { is TypeMirror.ByValue -> {
val name = def.name val valueTypeName = (mirror as TypeMirror.ByValue).valueTypeName
val varTypeName = mirror.info.constructPointedType(name) val varTypeAliasee = mirror.info.constructPointedType(valueTypeName)
out("typealias ${mirror.pointedTypeName} = $varTypeName") out("typealias ${mirror.pointedTypeName} = $varTypeAliasee")
out("typealias ${(mirror as TypeMirror.ByValue).valueTypeName} = ${baseMirror.valueTypeName}") out("typealias $valueTypeName = ${baseMirror.valueTypeName}")
} }
is TypeMirror.ByRef -> { is TypeMirror.ByRef -> {
out("typealias ${mirror.pointedTypeName} = ${baseMirror.pointedTypeName}") out("typealias ${mirror.pointedTypeName} = ${baseMirror.pointedTypeName}")
@@ -721,7 +728,7 @@ class StubGenerator(
val paramNames = func.parameters.mapIndexed { i: Int, parameter: Parameter -> val paramNames = func.parameters.mapIndexed { i: Int, parameter: Parameter ->
val name = parameter.name val name = parameter.name
if (name != null && name != "") { if (name != null && name != "") {
name.mangleIfKeyword() name
} else { } else {
"arg$i" "arg$i"
} }
@@ -750,10 +757,10 @@ class StubGenerator(
val retValBinding = retValBinding(func) val retValBinding = retValBinding(func)
val args = paramNames.mapIndexed { i: Int, name: String -> val args = paramNames.mapIndexed { i: Int, name: String ->
"$name: " + paramBindings[i].kotlinType "${name.asSimpleName()}: " + paramBindings[i].kotlinType
}.joinToString(", ") }.joinToString(", ")
val header = "fun ${func.name}($args): ${retValBinding.kotlinType}" val header = "fun ${func.name.asSimpleName()}($args): ${retValBinding.kotlinType}"
if (!func.requiresKotlinAdapter()) { if (!func.requiresKotlinAdapter()) {
assert(platform == KotlinPlatform.NATIVE) assert(platform == KotlinPlatform.NATIVE)
@@ -769,9 +776,9 @@ class StubGenerator(
if (binding.kotlinConv != null) { if (binding.kotlinConv != null) {
externalParamName = "_$name" externalParamName = "_$name"
out("val $externalParamName = " + binding.kotlinConv.invoke(name)) out("val $externalParamName = " + binding.kotlinConv.invoke(name.asSimpleName()))
} else { } else {
externalParamName = name externalParamName = name.asSimpleName()
} }
externalParamName externalParamName
@@ -784,7 +791,7 @@ class StubGenerator(
} }
val callee = when (platform) { val callee = when (platform) {
KotlinPlatform.JVM -> "externals.${func.name}" KotlinPlatform.JVM -> "externals.${func.name.asSimpleName()}"
KotlinPlatform.NATIVE -> func.kotlinExternalName KotlinPlatform.NATIVE -> func.kotlinExternalName
} }
out("val res = $callee(" + arguments.joinToString(", ") + ")") out("val res = $callee(" + arguments.joinToString(", ") + ")")
@@ -1028,7 +1035,7 @@ class StubGenerator(
// Also it provokes constant propagation which can reduce binary compatibility // Also it provokes constant propagation which can reduce binary compatibility
// when replacing interop stubs without recompiling the application. // when replacing interop stubs without recompiling the application.
out("val ${constant.name}: $kotlinType = $literal") out("val ${constant.name.asSimpleName()}: $kotlinType = $literal")
} }
/** /**
@@ -1075,12 +1082,12 @@ class StubGenerator(
val retValBinding = retValBinding(func) val retValBinding = retValBinding(func)
val args = paramNames.mapIndexed { i: Int, name: String -> val args = paramNames.mapIndexed { i: Int, name: String ->
"$name: " + paramBindings[i].kotlinJniBridgeType "${name.asSimpleName()}: " + paramBindings[i].kotlinJniBridgeType
}.joinToString(", ") }.joinToString(", ")
when (platform) { when (platform) {
KotlinPlatform.JVM -> { KotlinPlatform.JVM -> {
out("external fun ${func.name}($args): ${retValBinding.kotlinJniBridgeType}") out("external fun ${func.name.asSimpleName()}($args): ${retValBinding.kotlinJniBridgeType}")
} }
KotlinPlatform.NATIVE -> { KotlinPlatform.NATIVE -> {
out(func.symbolNameAnnotation) out(func.symbolNameAnnotation)