From 6f354a06c4b054007652aafbbff19dad870e6239 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 5 Oct 2016 08:27:51 +0300 Subject: [PATCH] Interop/StubGenerator: attempt to handle anonymous enums --- .../kotlin/native/interop/indexer/Indexer.kt | 10 +- .../native/interop/gen/jvm/StubGenerator.kt | 93 +++++++++++++++---- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index 118c03c62d2..362a8841e81 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -209,7 +209,15 @@ private class NativeIndexImpl : NativeIndex() { val name = entityName!! 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) + } } } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index 630c6ff677c..5dce6adaf20 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -32,6 +32,17 @@ class StubGenerator( 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 */ @@ -39,12 +50,16 @@ class StubGenerator( get() = if (spelling.startsWith("enum ")) { spelling.substringAfter(' ') } else { + assert (!isAnonymous) spelling } val RecordType.kotlinName: String get() = decl.kotlinName + val EnumType.isKotlinEnum: Boolean + get() = def.isKotlinEnum + val EnumType.kotlinName: String get() = def.kotlinName @@ -116,7 +131,11 @@ class StubGenerator( 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() } @@ -155,7 +174,11 @@ class StubGenerator( 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 -> { if (type.pointeeType is VoidType || type.pointeeType is FunctionType) { @@ -247,11 +270,15 @@ class StubGenerator( } } - is EnumType -> OutValueBinding( - kotlinType = type.kotlinName, - kotlinConv = { "$it.value" }, - kotlinJniBridgeType = type.def.baseType.kotlinType - ) + is EnumType -> if (type.isKotlinEnum) { + OutValueBinding( + kotlinType = type.kotlinName, + kotlinConv = { "$it.value" }, + kotlinJniBridgeType = type.def.baseType.kotlinType + ) + } else { + getOutValueBinding(type.def.baseType) + } is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type)) @@ -289,11 +316,15 @@ class StubGenerator( } } - is EnumType -> InValueBinding( - kotlinJniBridgeType = type.def.baseType.kotlinType, - conv = { "${type.kotlinName}.byValue($it)" }, - kotlinType = type.kotlinName - ) + is EnumType -> if (type.isKotlinEnum) { + InValueBinding( + kotlinJniBridgeType = type.def.baseType.kotlinType, + conv = { "${type.kotlinName}.byValue($it)" }, + kotlinType = type.kotlinName + ) + } else { + getInValueBinding(type.def.baseType) + } 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. */ - private fun generateKotlinStruct(decl: StructDecl) { + private fun generateStruct(decl: StructDecl) { val def = decl.def if (def == null) { - generateKotlinForwardStruct(decl) + generateForwardStruct(decl) return } @@ -344,7 +375,7 @@ class StubGenerator( /** * 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 out("class $className(ptr: NativePtr) : NativeRef(ptr) {") 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) out("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType}) {") @@ -379,6 +415,25 @@ class StubGenerator( 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. */ @@ -508,7 +563,7 @@ class StubGenerator( nativeIndex.structs.forEach { s -> try { transaction { - generateKotlinStruct(s) + generateStruct(s) out("") } } catch (e: Throwable) { @@ -517,7 +572,7 @@ class StubGenerator( } nativeIndex.enums.forEach { e -> - generateKotlinEnum(e) + generateEnum(e) out("") }