Interop/StubGenerator: attempt to handle anonymous enums

This commit is contained in:
Svyatoslav Scherbina
2016-10-05 08:27:51 +03:00
parent f94b48cbe0
commit 6f354a06c4
2 changed files with 83 additions and 20 deletions
@@ -209,7 +209,15 @@ private class NativeIndexImpl : NativeIndex() {
val name = entityName!! val name = entityName!!
val value = clang_getEnumConstantDeclValue(info.cursor) val value = clang_getEnumConstantDeclValue(info.cursor)
getEnumDefAt(container.cursor).values.add(EnumValue(name, value)) val values = getEnumDefAt(container.cursor).values
val existingValue = values.find { it.name == name }
if (existingValue == null) {
values.add(EnumValue(name, value))
} else {
// in some cases Clang may index the same definition multiple times; ignore redeclaration
// TODO: implement the same fix for structs
assert (existingValue.value == value)
}
} }
} }
} }
@@ -32,6 +32,17 @@ class StubGenerator(
return if (strippedCName !in forbiddenStructNames) strippedCName else (strippedCName + "Struct") return if (strippedCName !in forbiddenStructNames) strippedCName else (strippedCName + "Struct")
} }
val EnumDef.isAnonymous: Boolean
get() = spelling.contains("(anonymous ") // TODO: it is a hack
/**
* Indicates whether this enum should be represented as Kotlin enum.
*/
val EnumDef.isKotlinEnum: Boolean
// TODO: if an anonymous enum defines e.g. a function return value or struct field type,
// then it probably should be represented as Kotlin enum
get() = !isAnonymous
/** /**
* The name to be used for this enum in Kotlin * The name to be used for this enum in Kotlin
*/ */
@@ -39,12 +50,16 @@ class StubGenerator(
get() = if (spelling.startsWith("enum ")) { get() = if (spelling.startsWith("enum ")) {
spelling.substringAfter(' ') spelling.substringAfter(' ')
} else { } else {
assert (!isAnonymous)
spelling spelling
} }
val RecordType.kotlinName: String val RecordType.kotlinName: String
get() = decl.kotlinName get() = decl.kotlinName
val EnumType.isKotlinEnum: Boolean
get() = def.isKotlinEnum
val EnumType.kotlinName: String val EnumType.kotlinName: String
get() = def.kotlinName get() = def.kotlinName
@@ -116,7 +131,11 @@ class StubGenerator(
is ArrayType -> "void*" // TODO is ArrayType -> "void*" // TODO
is EnumType -> this.def.spelling is EnumType -> if (this.def.isAnonymous) {
this.def.baseType.getStringRepresentation()
} else {
this.def.spelling
}
else -> throw kotlin.NotImplementedError() else -> throw kotlin.NotImplementedError()
} }
@@ -155,7 +174,11 @@ class StubGenerator(
is RecordType -> NativeRefType("${type.kotlinName}") is RecordType -> NativeRefType("${type.kotlinName}")
is EnumType -> NativeRefType("${type.kotlinName}.ref") is EnumType -> if (type.isKotlinEnum) {
NativeRefType("${type.kotlinName}.ref")
} else {
getKotlinTypeForRefTo(type.def.baseType)
}
is PointerType -> { is PointerType -> {
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) { if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
@@ -247,11 +270,15 @@ class StubGenerator(
} }
} }
is EnumType -> OutValueBinding( is EnumType -> if (type.isKotlinEnum) {
kotlinType = type.kotlinName, OutValueBinding(
kotlinConv = { "$it.value" }, kotlinType = type.kotlinName,
kotlinJniBridgeType = type.def.baseType.kotlinType kotlinConv = { "$it.value" },
) kotlinJniBridgeType = type.def.baseType.kotlinType
)
} else {
getOutValueBinding(type.def.baseType)
}
is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type)) is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type))
@@ -289,11 +316,15 @@ class StubGenerator(
} }
} }
is EnumType -> InValueBinding( is EnumType -> if (type.isKotlinEnum) {
kotlinJniBridgeType = type.def.baseType.kotlinType, InValueBinding(
conv = { "${type.kotlinName}.byValue($it)" }, kotlinJniBridgeType = type.def.baseType.kotlinType,
kotlinType = type.kotlinName conv = { "${type.kotlinName}.byValue($it)" },
) kotlinType = type.kotlinName
)
} else {
getInValueBinding(type.def.baseType)
}
is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type)) is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type))
@@ -313,10 +344,10 @@ class StubGenerator(
/** /**
* Produces to [out] the definition of Kotlin class representing the reference to given struct. * Produces to [out] the definition of Kotlin class representing the reference to given struct.
*/ */
private fun generateKotlinStruct(decl: StructDecl) { private fun generateStruct(decl: StructDecl) {
val def = decl.def val def = decl.def
if (def == null) { if (def == null) {
generateKotlinForwardStruct(decl) generateForwardStruct(decl)
return return
} }
@@ -344,7 +375,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 generateKotlinForwardStruct(s: StructDecl) { private fun generateForwardStruct(s: StructDecl) {
val className = s.kotlinName val className = s.kotlinName
out("class $className(ptr: NativePtr) : NativeRef(ptr) {") out("class $className(ptr: NativePtr) : NativeRef(ptr) {")
out(" companion object : Type<$className>(::$className)") out(" companion object : Type<$className>(::$className)")
@@ -352,9 +383,14 @@ class StubGenerator(
} }
/** /**
* Produces to [out] the definition of Kotlin class representing the value of given enum. * Produces to [out] the Kotlin definitions for given enum.
*/ */
private fun generateKotlinEnum(e: EnumDef) { private fun generateEnum(e: EnumDef) {
if (!e.isKotlinEnum) {
generateEnumAsValues(e)
return
}
val baseRefType = getKotlinTypeForRefTo(e.baseType) val baseRefType = getKotlinTypeForRefTo(e.baseType)
out("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType}) {") out("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType}) {")
@@ -379,6 +415,25 @@ class StubGenerator(
out("}") out("}")
} }
/**
* Produces to [out] the Kotlin definitions for given enum which shouldn't be represented as Kotlin enum.
*
* @see isKotlinEnum
*/
private fun generateEnumAsValues(e: EnumDef) {
// TODO: if this enum defines e.g. a type of struct field, then it should be generated inside the struct class
// to prevent name clashing
if (e.values.isEmpty()) {
return
}
out("// ${e.spelling}")
e.values.forEach {
out("val ${it.name}: ${e.baseType.kotlinType} = ${it.value}")
}
}
/** /**
* Constructs [InValueBinding] for return value of Kotlin binding for given C function. * Constructs [InValueBinding] for return value of Kotlin binding for given C function.
*/ */
@@ -508,7 +563,7 @@ class StubGenerator(
nativeIndex.structs.forEach { s -> nativeIndex.structs.forEach { s ->
try { try {
transaction { transaction {
generateKotlinStruct(s) generateStruct(s)
out("") out("")
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -517,7 +572,7 @@ class StubGenerator(
} }
nativeIndex.enums.forEach { e -> nativeIndex.enums.forEach { e ->
generateKotlinEnum(e) generateEnum(e)
out("") out("")
} }