diff --git a/libraries/tools/idl2k/src/main/kotlin/gen.kt b/libraries/tools/idl2k/src/main/kotlin/gen.kt index 998be95aa23..99b6ea8f456 100644 --- a/libraries/tools/idl2k/src/main/kotlin/gen.kt +++ b/libraries/tools/idl2k/src/main/kotlin/gen.kt @@ -1,6 +1,6 @@ package org.jetbrains.idl2k -import java.util.HashSet +import java.util.* private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes -> when { @@ -17,6 +17,17 @@ fun String.ensureNullable() = when { else -> "$this?" } +fun String.dropNullable() = when { + endsWith(")?") -> this.removeSuffix("?").removeSurrounding("(", ")") + endsWith("?") -> this.removeSuffix("?") + else -> this +} + +fun String.copyNullabilityFrom(type : String) = when { + type.endsWith("?") -> ensureNullable() + else -> this +} + fun generateFunction(repository: Repository, function: Operation, functionName: String, nativeGetterOrSetter: NativeGetterOrSetter = function.getterOrSetter()): GenerateFunction = function.attributes.map { it.call }.toSet().let { attributes -> GenerateFunction( @@ -44,8 +55,26 @@ fun generateFunctions(repository: Repository, function: Operation): List generateFunction(repository, function, "get") NativeGetterOrSetter.SETTER -> generateFunction(repository, function, "set") } + val callbackArgumentsAsLambdas = function.parameters.map { + val interfaceType = repository.interfaces[it.type.dropNullable()] + when { + interfaceType == null -> it + interfaceType.callback -> interfaceType.operations.single().let { callbackFunction -> + it.copy(type = callbackFunction.parameters + .map { mapType(repository, it.type) } + .join(",", "(", ") -> ${mapType(repository, callbackFunction.returnType)}") + .copyNullabilityFrom(it.type)) + } + else -> it + } + } - return listOf(realFunction, getterOrSetterFunction).filterNotNull() + val functionWithCallbackOrNull = when { + callbackArgumentsAsLambdas == function.parameters -> null + else -> generateFunction(repository, function.copy(parameters = callbackArgumentsAsLambdas), function.name, NativeGetterOrSetter.NONE) + } + + return listOf(realFunction, getterOrSetterFunction, functionWithCallbackOrNull).filterNotNull() } fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Attribute): GenerateAttribute = diff --git a/libraries/tools/idl2k/src/main/kotlin/idl.kt b/libraries/tools/idl2k/src/main/kotlin/idl.kt index b49cb96f6b2..d513b5b12d6 100644 --- a/libraries/tools/idl2k/src/main/kotlin/idl.kt +++ b/libraries/tools/idl2k/src/main/kotlin/idl.kt @@ -16,24 +16,20 @@ package org.jetbrains.idl2k -import org.antlr.v4.runtime.* -import org.antlr.webidl.WebIDLBaseListener +import org.antlr.v4.runtime.CharStream +import org.antlr.v4.runtime.CommonTokenStream +import org.antlr.v4.runtime.ParserRuleContext +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 org.antlr.v4.runtime.tree.TerminalNode -import org.antlr.webidl.WebIDLBaseVisitor -import org.jsoup.Jsoup -import java.io.File -import java.io.Reader -import java.io.StringReader -import java.net.URL -import java.util.* +import java.util.ArrayList -data class ExtendedAttribute(val name : String?, val call : String, val arguments : List) -data class Operation(val name : String, val returnType : String, val parameters : List, val attributes : List) -data class Attribute(val name : String, val type : String, val readOnly : Boolean, val defaultValue : String? = null, val vararg : Boolean) -data class Constant(val name : String, val type : String, val value : String?) +data class ExtendedAttribute(val name: String?, val call: String, val arguments: List) +data class Operation(val name: String, val returnType: String, val parameters: List, val attributes: List) +data class Attribute(val name: String, val type: String, val readOnly: Boolean, val defaultValue: String? = null, val vararg: Boolean) +data class Constant(val name: String, val type: String, val value: String?) enum class DefinitionType { INTERFACE @@ -42,11 +38,24 @@ enum class DefinitionType { ENUM DICTIONARY } + trait Definition -data class TypedefDefinition(val types: String, val namespace : String, val name: String) : Definition -data class InterfaceDefinition(val name : String, val namespace : String, val extendedAttributes: List, val operations : List, val attributes : List, val superTypes : List, val constants : List, val dictionary : Boolean = false) : 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 TypedefDefinition(val types: String, val namespace: String, val name: String) : Definition +data class InterfaceDefinition( + val name: String, + val namespace: String, + val extendedAttributes: List, + val operations: List, + val attributes: List, + val superTypes: List, + val constants: List, + val dictionary: Boolean = false, + val partial: Boolean, + val callback: Boolean +) : Definition + +data class ExtensionInterfaceDefinition(val namespace: String, val name: String, val implements: String) : Definition +data class EnumDefinition(val namespace: String, val name: String) : Definition class ExtendedAttributeArgumentsParser : WebIDLBaseVisitor>() { private val arguments = ArrayList() @@ -66,14 +75,14 @@ class ExtendedAttributeArgumentsParser : WebIDLBaseVisitor>() { } class ExtendedAttributeParser : WebIDLBaseVisitor() { - private var name : String? = null - private var call : String = "" + private var name: String? = null + private var call: String = "" private val arguments = ArrayList() override fun defaultResult(): ExtendedAttribute = ExtendedAttribute(name, call, arguments) override fun visitExtendedAttribute(ctx: WebIDLParser.ExtendedAttributeContext): ExtendedAttribute { - call = ctx.children?.filter {it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL}?.firstOrNull()?.getText() ?: "" + call = ctx.children?.filter { it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL }?.firstOrNull()?.getText() ?: "" visitChildren(ctx) return defaultResult() @@ -84,9 +93,9 @@ class ExtendedAttributeParser : WebIDLBaseVisitor() { return defaultResult() } - override fun visitIdentifierList(ctx : IdentifierListContext) : ExtendedAttribute { + override fun visitIdentifierList(ctx: IdentifierListContext): ExtendedAttribute { object : WebIDLBaseVisitor() { - override fun visitTerminal(node : TerminalNode) { + override fun visitTerminal(node: TerminalNode) { if (node.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL) { arguments.add(Attribute(node.getText(), "any", true, vararg = false)) } @@ -140,9 +149,9 @@ class TypeVisitor : WebIDLBaseVisitor() { } } -class OperationVisitor(val attributes : List) : WebIDLBaseVisitor() { - private var name : String = "" - private var returnType : String = "" +class OperationVisitor(val attributes: List) : WebIDLBaseVisitor() { + private var name: String = "" + private var returnType: String = "" private val parameters = ArrayList() private val exts = ArrayList() @@ -177,13 +186,13 @@ class OperationVisitor(val attributes : List) : WebIDLBaseVis } } -class AttributeVisitor(val readOnly : Boolean) : WebIDLBaseVisitor() { - var type : String = "" - var name : String = "" - var defaultValue : String? = null - var vararg : Boolean = false +class AttributeVisitor(val readOnly: Boolean) : WebIDLBaseVisitor() { + var type: String = "" + var name: String = "" + var defaultValue: String? = null + var vararg: Boolean = false - override fun defaultResult() : Attribute = Attribute(name, type, readOnly, defaultValue, vararg) + override fun defaultResult(): Attribute = Attribute(name, type, readOnly, defaultValue, vararg) override fun visitType(ctx: WebIDLParser.TypeContext): Attribute { type = TypeVisitor().visit(ctx) @@ -191,7 +200,7 @@ class AttributeVisitor(val readOnly : Boolean) : WebIDLBaseVisitor() } override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): Attribute { - if (ctx.children?.any {it is TerminalNode && it.getText() == "optional"} ?: false) { + if (ctx.children?.any { it is TerminalNode && it.getText() == "optional" } ?: false) { defaultValue = "noImpl" } return visitChildren(ctx) @@ -200,8 +209,8 @@ class AttributeVisitor(val readOnly : Boolean) : WebIDLBaseVisitor() override fun visitAttributeRest(ctx: WebIDLParser.AttributeRestContext): Attribute { try { name = getName(ctx) - } catch (ignore : Throwable) { - name = ctx.children.filter { it is TerminalNode}.filter {it.getText() != ";"}.last().getText() + } catch (ignore: Throwable) { + name = ctx.children.filter { it is TerminalNode }.filter { it.getText() != ";" }.last().getText() } return defaultResult() } @@ -209,7 +218,7 @@ class AttributeVisitor(val readOnly : Boolean) : WebIDLBaseVisitor() override fun visitArgumentName(ctx: WebIDLParser.ArgumentNameContext): Attribute { try { name = getName(ctx) - } catch (ignore : Throwable) { + } catch (ignore: Throwable) { name = ctx.getText() } return defaultResult() @@ -226,12 +235,12 @@ class AttributeVisitor(val readOnly : Boolean) : WebIDLBaseVisitor() } } -class ConstantVisitor(val attributes : List) : WebIDLBaseVisitor() { - var type : String = "" - var name : String = "" - var value : String? = null +class ConstantVisitor(val attributes: List) : WebIDLBaseVisitor() { + var type: String = "" + var name: String = "" + var value: String? = null - override fun defaultResult() : Constant = Constant(name, type, value) + override fun defaultResult(): Constant = Constant(name, type, value) override fun visitConst_(ctx: WebIDLParser.Const_Context): Constant { name = getName(ctx) @@ -250,27 +259,43 @@ class ConstantVisitor(val attributes : List) : WebIDLBaseVisi } } -class DefinitionVisitor(val extendedAttributes: List, val namespace : String) : WebIDLBaseVisitor() { - private var type : DefinitionType = DefinitionType.INTERFACE +class DefinitionVisitor(val extendedAttributes: List, val namespace: String) : WebIDLBaseVisitor() { + private var type: DefinitionType = DefinitionType.INTERFACE private var name = "" private val memberAttributes = ArrayList() private val operations = ArrayList() private val attributes = ArrayList() - private var readOnly : Boolean = false + private var readOnly: Boolean = false private val inherited = ArrayList() private var typedefType: String? = null - private var implements : String? = null + private var implements: String? = null private val constants = ArrayList() + private var partial = false + private var callback = false - override fun defaultResult(): Definition = when(type) { - DefinitionType.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants) - DefinitionType.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, true) + override fun defaultResult(): Definition = when (type) { + DefinitionType.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback) + DefinitionType.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, true, partial, callback) DefinitionType.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "") DefinitionType.TYPEDEF -> TypedefDefinition(typedefType ?: "", namespace, name) DefinitionType.ENUM -> EnumDefinition(namespace, name) } - override fun visitInterface_(ctx: Interface_Context) : Definition { + override fun visitCallbackRestOrInterface(ctx: WebIDLParser.CallbackRestOrInterfaceContext): Definition { + callback = true + return visitChildren(ctx) + } + + override fun visitCallbackRest(ctx: WebIDLParser.CallbackRestContext): Definition { + name = getName(ctx) + with(OperationVisitor(memberAttributes.toList())) { + operations.add(visit(ctx)) + } + memberAttributes.clear() + return defaultResult() + } + + override fun visitInterface_(ctx: Interface_Context): Definition { name = getName(ctx) visitChildren(ctx) return defaultResult() @@ -278,12 +303,14 @@ class DefinitionVisitor(val extendedAttributes: List, val nam override fun visitPartialInterface(ctx: WebIDLParser.PartialInterfaceContext): Definition { name = getName(ctx) + partial = true visitChildren(ctx) return defaultResult() } override fun visitTypedef(ctx: WebIDLParser.TypedefContext): Definition { - if (name != "") { // TODO temporary workaround for local typedefs + if (name != "") { + // TODO temporary workaround for local typedefs return defaultResult() } @@ -293,7 +320,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam typedefType = ctx.accept(object : WebIDLBaseVisitor() { private var foundType = "" - override fun defaultResult() : String = foundType + override fun defaultResult(): String = foundType override fun visitType(ctx: WebIDLParser.TypeContext): String { foundType = TypeVisitor().visit(ctx) @@ -320,17 +347,17 @@ class DefinitionVisitor(val extendedAttributes: List, val nam override fun visitDictionaryMember(ctx: DictionaryMemberContext): Definition { val name = ctx.children - ?.filter {it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL} + ?.filter { it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL } ?.first { it.getText() != "" } ?.getText() - val type = TypeVisitor().visit(ctx.children.first {it is TypeContext}) + val type = TypeVisitor().visit(ctx.children.first { it is TypeContext }) val defaultValue = object : WebIDLBaseVisitor() { - private var value : String? = null + private var value: String? = null override fun defaultResult() = value - override fun visitDefaultValue(ctx2: DefaultValueContext) : String? { + override fun visitDefaultValue(ctx2: DefaultValueContext): String? { value = ctx2.getText() return value } @@ -342,7 +369,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } override fun visitImplementsStatement(ctx: ImplementsStatementContext): Definition { - val identifiers = ctx.children.filter {it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL}.map {it.getText()} + val identifiers = ctx.children.filter { it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL }.map { it.getText() } if (identifiers.size() >= 2) { type = DefinitionType.EXTENSION_INTERFACE @@ -354,7 +381,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam return defaultResult() } - override fun visitOperation(ctx: OperationContext) : Definition { + override fun visitOperation(ctx: OperationContext): Definition { with(OperationVisitor(memberAttributes.toList())) { operations.add(visit(ctx)) } @@ -393,7 +420,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam return defaultResult() } - override fun visitExtendedAttribute(ctx: ExtendedAttributeContext) : Definition { + override fun visitExtendedAttribute(ctx: ExtendedAttributeContext): Definition { val att = with(ExtendedAttributeParser()) { visit(ctx) } @@ -404,9 +431,9 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } -private fun getName(ctx: ParserRuleContext) = ctx.children.first {it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL}.getText() +private fun getName(ctx: ParserRuleContext) = ctx.children.first { it is TerminalNode && it.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL }.getText() -fun parseIDL(reader : CharStream) : Repository { +fun parseIDL(reader: CharStream): Repository { val ll = WebIDLLexer(reader) val pp = WebIDLParser(CommonTokenStream(ll)) @@ -438,24 +465,27 @@ fun parseIDL(reader : CharStream) : Repository { }) return Repository( - declarations.filterIsInstance().filter {it.name.isEmpty().not()}.groupBy { it.name }.mapValues { it.getValue().reduce(::merge) }, + declarations.filterIsInstance().filter { it.name.isEmpty().not() }.groupBy { it.name }.mapValues { it.getValue().reduce(::merge) }, declarations.filterIsInstance().groupBy { it.name }.mapValues { it.getValue().first() }, - declarations.filterIsInstance().groupBy { it.name }.mapValues { it.getValue().map {it.implements} }, - declarations.filterIsInstance().groupBy { it.name }.mapValues { it.getValue().reduce {a, b -> a} } + declarations.filterIsInstance().groupBy { it.name }.mapValues { it.getValue().map { it.implements } }, + declarations.filterIsInstance().groupBy { it.name }.mapValues { it.getValue().reduce { a, b -> a } } ) } -fun merge(i1 : InterfaceDefinition, i2 : InterfaceDefinition) : InterfaceDefinition { +fun merge(i1: InterfaceDefinition, i2: InterfaceDefinition): InterfaceDefinition { require(i1.name == i2.name) - return InterfaceDefinition(i1.name, i1.namespace, + return InterfaceDefinition(i1.name, + namespace = if (i1.partial) i2.namespace else i1.namespace, extendedAttributes = i1.extendedAttributes merge i2.extendedAttributes, operations = i1.operations merge i2.operations, attributes = i1.attributes merge i2.attributes, superTypes = i1.superTypes merge i2.superTypes, constants = i1.constants merge i2.constants, - dictionary = i1.dictionary || i2.dictionary - ) + dictionary = i1.dictionary || i2.dictionary, + partial = i1.partial && i2.partial, + callback = i1.callback && i2.callback + ) } -fun List.merge(other : List) = (this + other).distinct().toList() +fun List.merge(other: List) = (this + other).distinct().toList() diff --git a/libraries/tools/idl2k/src/main/kotlin/main.kt b/libraries/tools/idl2k/src/main/kotlin/main.kt index 2a88a749aa3..5ad6f7b7c4d 100644 --- a/libraries/tools/idl2k/src/main/kotlin/main.kt +++ b/libraries/tools/idl2k/src/main/kotlin/main.kt @@ -20,7 +20,7 @@ fun main(args: Array) { val fileRepository = parseIDL(ANTLRFileStream(e.getAbsolutePath(), "UTF-8")) Repository( - interfaces = acc.interfaces + fileRepository.interfaces, + interfaces = acc.interfaces.mergeReduce(fileRepository.interfaces, ::merge), typeDefs = acc.typeDefs + fileRepository.typeDefs, externals = acc.externals merge fileRepository.externals, enums = acc.enums + fileRepository.enums @@ -53,6 +53,24 @@ fun main(args: Array) { } } +private fun Map>.reduceValues(reduce : (V, V) -> V = {a, b -> b}) : Map = mapValues { it.value.reduce(reduce) } + +private fun Map.mergeReduce(other : Map, reduce : (V, V) -> V = {a, b -> b}) : Map { + val result = LinkedHashMap(this.size() + other.size()) + result.putAll(this) + other.forEach { e -> + val existing = result[e.key] + + if (existing == null) { + result[e.key] = e.value + } else { + result[e.key] = reduce(e.value, existing) + } + } + + return result +} + private fun Map>.merge(other : Map>) : Map> { val result = LinkedHashMap>(size() + other.size()) this.forEach { diff --git a/libraries/tools/idl2k/src/main/kotlin/model.kt b/libraries/tools/idl2k/src/main/kotlin/model.kt index 1e771703ed9..5785706e5db 100644 --- a/libraries/tools/idl2k/src/main/kotlin/model.kt +++ b/libraries/tools/idl2k/src/main/kotlin/model.kt @@ -34,8 +34,12 @@ val GenerateAttribute.getterNoImpl: Boolean val GenerateAttribute.setterNoImpl: Boolean get() = getterSetterNoImpl && !readOnly +val String.typeSignature: String + get() = if (contains("->")) "()" else this + val GenerateAttribute.signature: String - get() = "$name:$type" + get() = "$name:${type.typeSignature}" +fun GenerateAttribute.dynamicIfUnknownType(allTypes : Set, standardTypes : Set = standardTypes()) = this.copy(type = type.dynamicIfUnknownType(allTypes, standardTypes)) enum class NativeGetterOrSetter { NONE @@ -86,8 +90,11 @@ data class GenerateTraitOrClass( val GenerateFunction.signature: String - get() = arguments.map { it.type }.joinToString(", ", "$name(", ")") + get() = arguments.map { it.type.typeSignature }.joinToString(", ", "$name(", ")") +fun GenerateFunction.dynamicIfUnknownType(allTypes : Set) = standardTypes().let { standardTypes -> + this.copy(returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes), arguments = arguments.map { it.dynamicIfUnknownType(allTypes, standardTypes) }) +} fun InterfaceDefinition.findExtendedAttribute(name: String) = extendedAttributes.firstOrNull { it.call == name } fun InterfaceDefinition?.hasExtendedAttribute(name: String) = this?.findExtendedAttribute(name) ?: null != null diff --git a/libraries/tools/idl2k/src/main/kotlin/render.kt b/libraries/tools/idl2k/src/main/kotlin/render.kt index 34dd86a34cd..de33aedcc0b 100644 --- a/libraries/tools/idl2k/src/main/kotlin/render.kt +++ b/libraries/tools/idl2k/src/main/kotlin/render.kt @@ -2,13 +2,13 @@ package org.jetbrains.idl2k import java.math.BigInteger -private fun O.indent(level : Int) { +private fun O.indent(level: Int) { for (i in 1..level) { append(" ") } } -private fun Appendable.renderAttributeDeclaration(allTypes: Set, arg: GenerateAttribute, override: Boolean, level : Int = 1) { +private fun Appendable.renderAttributeDeclaration(allTypes: Set, arg: GenerateAttribute, override: Boolean, level: Int = 1) { indent(level) if (override) { @@ -19,7 +19,7 @@ private fun Appendable.renderAttributeDeclaration(allTypes: Set, arg: Ge append(" ") append(arg.name) append(": ") - append(arg.type.dynamicIfUnknownType(allTypes)) + append(arg.type) if (arg.initializer != null) { append(" = ") append(arg.initializer.replaceWrongConstants(arg.type)) @@ -50,7 +50,7 @@ private fun Appendable.renderArgumentsDeclaration(allTypes: Set, args: L } append(it.name.replaceKeywords()) append(": ") - append(it.type.dynamicIfUnknownType(allTypes)) + append(it.type) if (!omitDefaults && it.initializer != null && it.initializer != "") { append(" = ") append(it.initializer.replaceWrongConstants(it.type)) @@ -58,7 +58,7 @@ private fun Appendable.renderArgumentsDeclaration(allTypes: Set, args: L } }.joinTo(this, ", ") -private fun renderCall(call: GenerateFunctionCall) = "${call.name.replaceKeywords()}(${call.arguments.map {it.replaceKeywords()}.join(", ")})" +private fun renderCall(call: GenerateFunctionCall) = "${call.name.replaceKeywords()}(${call.arguments.map { it.replaceKeywords() }.join(", ")})" private fun Appendable.renderFunctionDeclaration(allTypes: Set, f: GenerateFunction, override: Boolean) { indent(1) @@ -77,11 +77,11 @@ private fun Appendable.renderFunctionDeclaration(allTypes: Set, f: Gener } append("fun ${f.name.replaceKeywords()}(") renderArgumentsDeclaration(allTypes, f.arguments, override) - appendln("): ${f.returnType.dynamicIfUnknownType(allTypes)} = noImpl") + appendln("): ${f.returnType} = noImpl") } -fun Appendable.render(allTypes: Map, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation : Boolean = false) { - append("native ") +fun Appendable.render(allTypes: Map, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) { + append("native public ") if (markerAnnotation) { append("marker ") } @@ -93,7 +93,7 @@ fun Appendable.render(allTypes: Map, typeNamesToUn append(iface.name) if (iface.constructor != null && iface.constructor.arguments.isNotEmpty()) { append("(") - renderArgumentsDeclaration(allTypes.keySet(), iface.constructor.arguments, false) + renderArgumentsDeclaration(allTypes.keySet(), iface.constructor.arguments.map { it.dynamicIfUnknownType(allTypes.keySet()) }, false) append(")") } @@ -116,10 +116,10 @@ fun Appendable.render(allTypes: Map, typeNamesToUn val superFunctions = allSuperTypes.flatMap { it.memberFunctions }.distinct() val superSignatures = superAttributes.map { it.signature } merge superFunctions.map { it.signature } - iface.memberAttributes.filter { it !in superAttributes }.forEach { arg -> + iface.memberAttributes.filter { it !in superAttributes }.map { it.dynamicIfUnknownType(allTypes.keySet()) }.groupBy { it.signature }.reduceValues().values().forEach { arg -> renderAttributeDeclaration(allTypes.keySet(), arg, arg.signature in superSignatures) } - iface.memberFunctions.filter { it !in superFunctions }.forEach { + iface.memberFunctions.filter { it !in superFunctions }.map { it.dynamicIfUnknownType(allTypes.keySet()) }.groupBy { it.signature }.reduceValues(::betterFunction).values().forEach { renderFunctionDeclaration(allTypes.keySet(), it, it.signature in superSignatures) } if (iface.constants.isNotEmpty()) { @@ -137,27 +137,38 @@ fun Appendable.render(allTypes: Map, typeNamesToUn appendln() } -fun List>.toMultiMap() : Map> = groupBy { it.first }.mapValues { it.value.map { it.second } } +fun betterFunction(f1: GenerateFunction, f2: GenerateFunction): GenerateFunction = + f1.copy( + arguments = f1.arguments + .zip(f2.arguments) + .map { it.first.copy(type = it.map { it.type }.betterType(), name = it.map { it.name }.betterName()) } + ) -fun Appendable.render(namespace : String, ifaces: List, typedefs : Iterable) { +private fun Pair.map(block: (F) -> T) = block(first) to block(second) +private fun Pair.betterType() = if (listOf("dynamic", "Any", "Any").any { first.contains(it) }) first else second +private fun Pair.betterName() = if (((0..9).map { it.toString() } + listOf("arg")).none { first.toLowerCase().contains(it) }) first else second + +fun List>.toMultiMap(): Map> = groupBy { it.first }.mapValues { it.value.map { it.second } } + +fun Appendable.render(namespace: String, ifaces: List, typedefs: Iterable) { val declaredTypes = ifaces.toMap { it.name } val anonymousUnionTypes = collectUnionTypes(declaredTypes) val anonymousUnionTypeTraits = generateUnionTypeTraits(anonymousUnionTypes) val anonymousUnionsMap = anonymousUnionTypeTraits.toMap { it.name } - val typedefsToBeGenerated = typedefs.filter {it.types.startsWith("Union<")} - .filter {it.namespace == namespace} + val typedefsToBeGenerated = typedefs.filter { it.types.startsWith("Union<") } + .filter { it.namespace == namespace } .map { NamedValue(it.name, UnionType(namespace, splitUnionType(it.types))) } - .filter { it.value.memberTypes.all { type -> type in declaredTypes} } + .filter { it.value.memberTypes.all { type -> type in declaredTypes } } val typedefsMarkerTraits = typedefsToBeGenerated.groupBy { it.name }.mapValues { mapUnionType(it.value.first().value).copy(name = it.key) } // TODO better name, extract duplication val typeNamesToUnions = anonymousUnionTypes.flatMap { unionType -> unionType.memberTypes.map { unionMember -> unionMember to unionType.name } }.toMultiMap() - typedefsToBeGenerated.flatMap { typedef -> typedef.value.memberTypes.map { unionMember -> unionMember to typedef.name } }.toMultiMap() + typedefsToBeGenerated.flatMap { typedef -> typedef.value.memberTypes.map { unionMember -> unionMember to typedef.name } }.toMultiMap() val allTypes = declaredTypes + anonymousUnionsMap + typedefsMarkerTraits - declaredTypes.values().filter {it.namespace == namespace}.forEach { + declaredTypes.values().filter { it.namespace == namespace }.forEach { render(allTypes, typeNamesToUnions, it) } diff --git a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt index b7c9b673e3a..28f9cc5ebee 100644 --- a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt +++ b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt @@ -28,7 +28,7 @@ private val typeMapper = mapOf( "short" to "Short", "long" to "Int", "double" to "Double", - "any" to "Any", + "any" to "Any?", "DOMTimeStamp" to "Number", "object" to "dynamic", // TODO map to Any? "EventHandler" to "(Event) -> Unit", @@ -55,15 +55,24 @@ fun allSuperTypesImpl(roots: List, all: Map, standardTypes: Set = typeMapper.values().toSet()): String = - if (this.endsWith("?")) this.substring(0, length() - 1).dynamicIfUnknownType(allTypes, standardTypes).ensureNullable() - else if (this.startsWith("Union<")) UnionType("", splitUnionType(this)).name.dynamicIfUnknownType(allTypes, standardTypes) // TODO check it is required here - else if (this in allTypes || this in standardTypes) this else "dynamic" +fun standardTypes() = typeMapper.values().map {it.dropNullable()}.toSet() +fun String.dynamicIfUnknownType(allTypes: Set, standardTypes: Set = standardTypes()): String = when { + startsWith("Union<") -> UnionType("", splitUnionType(this)).name.dynamicIfUnknownType(allTypes, standardTypes).copyNullabilityFrom(this) + endsWith("?") -> this.dropNullable().dynamicIfUnknownType(allTypes, standardTypes).ensureNullable() + contains("->") -> { + val (parameters, returnType) = this.split("->".toRegex()).map {it.trim()}.filter { it != "" } + + "(${parameters.removeSurrounding("(", ")").split(',').map {it.dynamicIfUnknownType(allTypes, standardTypes)}.join(",")}) -> ${returnType.dynamicIfUnknownType(allTypes, standardTypes)}" + } + this in allTypes -> this + this in standardTypes -> this + else -> "dynamic" +} private fun mapType(repository: Repository, type: String): String = when { type in typeMapper -> typeMapper[type]!! - type.endsWith("?") -> mapType(repository, type.substring(0, type.length() - 1)).ensureNullable() + type.endsWith("?") -> mapType(repository, type.dropNullable()).ensureNullable() type.endsWith("...") -> mapType(repository, type.substring(0, type.length() - 3)) type.endsWith("[]") -> "Array<${mapType(repository, type.substring(0, type.length() - 2))}>" type.startsWith("unrestricted") -> mapType(repository, type.substring(12))