diff --git a/libraries/tools/idl2k/src/main/kotlin/gen.kt b/libraries/tools/idl2k/src/main/kotlin/gen.kt index 74b96bd440a..4dd21112bf3 100644 --- a/libraries/tools/idl2k/src/main/kotlin/gen.kt +++ b/libraries/tools/idl2k/src/main/kotlin/gen.kt @@ -16,6 +16,8 @@ package org.jetbrains.idl2k +import org.jetbrains.idl2k.util.mapEnumConstant + private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes -> when { "getter" in attributes -> NativeGetterOrSetter.GETTER @@ -89,7 +91,7 @@ fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Att type = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it }, initializer = if (putNoImpl && !attribute.static) { - mapLiteral(attribute.defaultValue, mapType(repository, attribute.type)) + mapLiteral(attribute.defaultValue, mapType(repository, attribute.type), repository.enums) } else if (attribute.defaultValue != null) { "definedExternally" @@ -228,15 +230,21 @@ fun generateUnions(ifaces: List, typedefs: Iterable when { - expectedType == DynamicType -> "arrayOf()" - expectedType is AnyType -> "arrayOf()" - expectedType is UnionType -> "arrayOf()" - else -> "arrayOf()" +private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums: Map) = + if (literal != null && expectedType is SimpleType && expectedType.type in enums.keys) { + expectedType.type + "." + mapEnumConstant(literal.removeSurrounding("\"", "\"")) + } + else { + when (literal) { + "[]" -> when { + expectedType == DynamicType -> "arrayOf()" + expectedType is AnyType -> "arrayOf()" + expectedType is UnionType -> "arrayOf()" + else -> "arrayOf()" + } + else -> literal + } } - else -> literal -} fun implementInterfaces(declarations: List) { val unimplementedMemberMap = getUnimplementedMembers(declarations) diff --git a/libraries/tools/idl2k/src/main/kotlin/idl.kt b/libraries/tools/idl2k/src/main/kotlin/idl.kt index db227c1038d..64759ee5066 100644 --- a/libraries/tools/idl2k/src/main/kotlin/idl.kt +++ b/libraries/tools/idl2k/src/main/kotlin/idl.kt @@ -20,13 +20,12 @@ import org.antlr.v4.runtime.CharStream import org.antlr.v4.runtime.CommonTokenStream import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.ParseTree -import org.antlr.v4.runtime.tree.RuleNode import org.antlr.v4.runtime.tree.TerminalNode import org.antlr.webidl.WebIDLBaseVisitor import org.antlr.webidl.WebIDLLexer import org.antlr.webidl.WebIDLParser import org.antlr.webidl.WebIDLParser.* -import java.util.ArrayList +import java.util.* data class ExtendedAttribute(val name: String?, val call: String, val arguments: List) data class Operation(val name: String, val returnType: Type, val parameters: List, val attributes: List, val static: Boolean) @@ -57,7 +56,7 @@ data class InterfaceDefinition( ) : Definition data class ExtensionInterfaceDefinition(val namespace: String, val name: String, val implements: String) : Definition -data class EnumDefinition(val namespace: String, val name: String) : Definition +data class EnumDefinition(val namespace: String, val name: String, val entries: List) : Definition class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor>() { private val arguments = ArrayList() @@ -337,13 +336,15 @@ class DefinitionVisitor(val extendedAttributes: List, val nam private val constants = ArrayList() private var partial = false private var callback = false + private val enumEntries = mutableListOf() + private var enumEntryExpected = false override fun defaultResult(): Definition = when (kind) { DefinitionKind.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback) DefinitionKind.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, /* dictionary = */ true, partial, callback) DefinitionKind.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "") DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: AnyType(true), namespace, name) - DefinitionKind.ENUM -> EnumDefinition(namespace, name) + DefinitionKind.ENUM -> EnumDefinition(namespace, name, enumEntries) } override fun visitCallbackRestOrInterface(ctx: WebIDLParser.CallbackRestOrInterfaceContext): Definition { @@ -408,12 +409,23 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } override fun visitEnum_(ctx: Enum_Context): Definition { + enumEntryExpected = true kind = DefinitionKind.ENUM name = getName(ctx) + super.visitEnum_(ctx) + + enumEntryExpected = false return defaultResult() } + override fun visitTerminal(node: TerminalNode): Definition { + if (enumEntryExpected && node.symbol.type == WebIDLParser.STRING_WEBIDL) { + enumEntries += node.symbol.text.removeSurrounding("\"", "\"") + } + return super.visitTerminal(node) + } + override fun visitDictionary(ctx: DictionaryContext): Definition { kind = DefinitionKind.DICTIONARY name = getName(ctx) diff --git a/libraries/tools/idl2k/src/main/kotlin/main.kt b/libraries/tools/idl2k/src/main/kotlin/main.kt index 7f716211ba2..befba102cfc 100644 --- a/libraries/tools/idl2k/src/main/kotlin/main.kt +++ b/libraries/tools/idl2k/src/main/kotlin/main.kt @@ -40,7 +40,7 @@ fun main(args: Array) { } } val unions = generateUnions(definitions, repository.typeDefs.values) - val allPackages = definitions.map { it.namespace }.distinct().sorted() + val allPackages = (definitions.asSequence().map { it.namespace } + repository.enums.values.map { it.namespace }).distinct().sorted() implementInterfaces(definitions) outDir.deleteRecursively() @@ -66,7 +66,7 @@ fun main(args: Array) { } w.appendln() - w.render(pkg, definitions, unions) + w.render(pkg, definitions, unions, repository.enums.values.toList()) } } } diff --git a/libraries/tools/idl2k/src/main/kotlin/render.kt b/libraries/tools/idl2k/src/main/kotlin/render.kt index 94875ceca9e..3116c09da8d 100644 --- a/libraries/tools/idl2k/src/main/kotlin/render.kt +++ b/libraries/tools/idl2k/src/main/kotlin/render.kt @@ -16,6 +16,7 @@ package org.jetbrains.idl2k +import org.jetbrains.idl2k.util.mapEnumConstant import java.math.BigInteger private fun O.indent(commented: Boolean = false, level: Int) { @@ -142,7 +143,9 @@ private fun GenerateFunction.isCommented(parent: String) = private fun GenerateAttribute.isRequiredFunctionArgument(owner: String, functionName: String) = "$owner.$functionName.$name" in requiredArguments private fun GenerateFunction.fixRequiredArguments(parent: String) = copy(arguments = arguments.map { arg -> arg.copy(initializer = if (arg.isRequiredFunctionArgument(parent, name)) null else arg.initializer) }) -fun Appendable.render(allTypes: Map, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) { +fun Appendable.render(allTypes: Map, enums: List, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) { + val allTypesAndEnums = allTypes.keys + enums.map { it.name } + append("public external ") if (markerAnnotation) { append("@marker ") @@ -159,7 +162,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn append(iface.name) val primary = iface.primaryConstructor if (primary != null && (primary.constructor.arguments.isNotEmpty() || iface.secondaryConstructors.isNotEmpty())) { - renderArgumentsDeclaration(primary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypes.keys), false) + renderArgumentsDeclaration(primary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false) } val superTypesExclude = inheritanceExclude[iface.name] ?: emptySet() @@ -176,7 +179,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn iface.secondaryConstructors.forEach { secondary -> indent(false, 1) append("constructor") - renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypes.keys), false) + renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false) appendln() } @@ -188,7 +191,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn iface.memberAttributes .filter { it !in superAttributes && !it.static && (it.isVar || (it.isVal && superAttributesByName[it.name]?.hasNoVars() ?: true)) } - .map { it.dynamicIfUnknownType(allTypes.keys) } + .map { it.dynamicIfUnknownType(allTypesAndEnums) } .groupBy { it.name } .mapValues { it.value.filter { "${iface.name}.${it.name}" !in commentOutDeclarations && "${iface.name}.${it.name}: ${it.type.render()}" !in commentOutDeclarations } } .filterValues { it.isNotEmpty() } @@ -217,7 +220,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn } } val memberFunctions = iface.memberFunctions.filter { (it !in superFunctions || it.override) && !it.static } - .map { it.dynamicIfUnknownType(allTypes.keys) }.groupBy { it.signature }.reduceValues(::betterFunction).values + .map { it.dynamicIfUnknownType(allTypesAndEnums) }.groupBy { it.signature }.reduceValues(::betterFunction).values fun doRenderFunction(function: GenerateFunction, level: Int = 1) { renderFunctionDeclaration( @@ -255,7 +258,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn appendln() if (iface.generateBuilderFunction) { - renderBuilderFunction(iface, allSuperTypes, allTypes.keys) + renderBuilderFunction(iface, allSuperTypes, allTypesAndEnums) } } @@ -336,21 +339,40 @@ private fun merge(a: GenerateAttribute, b: GenerateAttribute): GenerateAttribute fun List>.toMultiMap(): Map> = groupBy { it.first }.mapValues { it.value.map { it.second } } -fun Appendable.render(namespace: String, ifaces: List, unions : GenerateUnionTypes) { +fun Appendable.render(enumDefinition: EnumDefinition) { + appendln("/* please, don't implement this interface! */") + appendln("public external interface ${enumDefinition.name} {") + indent(level = 1) + appendln("companion object") + appendln("}") + + for (entry in enumDefinition.entries) { + val entryName = mapEnumConstant(entry) + appendln("public inline val ${enumDefinition.name}.Companion.$entryName: ${enumDefinition.name} " + + "get() = \"$entry\".asDynamic().unsafeCast<${enumDefinition.name}>()") + } + + appendln() +} + +fun Appendable.render(namespace: String, ifaces: List, unions: GenerateUnionTypes, enums: List) { val declaredTypes = ifaces.associateBy { it.name } val allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap declaredTypes.values.filter { it.namespace == namespace }.forEach { - render(allTypes, unions.typeNamesToUnionsMap, it) + render(allTypes, enums, unions.typeNamesToUnionsMap, it) } unions.anonymousUnionsMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach { - render(allTypes, emptyMap(), it, markerAnnotation = true) + render(allTypes, enums, emptyMap(), it, markerAnnotation = true) } unions.typedefsMarkersMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach { - render(allTypes, emptyMap(), it, markerAnnotation = true) + render(allTypes, enums, emptyMap(), it, markerAnnotation = true) } + + enums.filter { it.namespace == namespace } + .forEach { render(it) } } enum class MemberModality { diff --git a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt index d0205e114af..165928b191a 100644 --- a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt +++ b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt @@ -78,7 +78,6 @@ internal fun mapType(repository: Repository, type: Type): Type = when (type) { typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable) typeName in repository.interfaces -> type typeName in repository.typeDefs -> mapTypedef(repository, type) - typeName in repository.enums -> SimpleType("String", type.nullable) else -> type } diff --git a/libraries/tools/idl2k/src/main/kotlin/utils.kt b/libraries/tools/idl2k/src/main/kotlin/utils.kt index 3f4816a3b41..04d83378342 100644 --- a/libraries/tools/idl2k/src/main/kotlin/utils.kt +++ b/libraries/tools/idl2k/src/main/kotlin/utils.kt @@ -41,3 +41,5 @@ fun List>.mutations() : List> { return result } + +fun mapEnumConstant(entry: String) = if (entry.isEmpty()) "EMPTY" else entry.toUpperCase().replace("-", "_") \ No newline at end of file