From 68183a74faacd6503d386022ab877c0784d55c7f Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Fri, 12 Jun 2015 21:11:37 +0300 Subject: [PATCH] IDL2K rework types Fixes #KT-8015 --- libraries/tools/idl2k/src/main/kotlin/gen.kt | 47 ++--- libraries/tools/idl2k/src/main/kotlin/idl.kt | 100 +++++----- libraries/tools/idl2k/src/main/kotlin/main.kt | 4 +- .../tools/idl2k/src/main/kotlin/model.kt | 26 +-- .../tools/idl2k/src/main/kotlin/render.kt | 14 +- .../idl2k/src/main/kotlin/typeMappings.kt | 173 ++++++++---------- .../tools/idl2k/src/main/kotlin/types.kt | 72 ++++++++ 7 files changed, 228 insertions(+), 208 deletions(-) create mode 100644 libraries/tools/idl2k/src/main/kotlin/types.kt diff --git a/libraries/tools/idl2k/src/main/kotlin/gen.kt b/libraries/tools/idl2k/src/main/kotlin/gen.kt index 9242c2af0cd..f773fc631d3 100644 --- a/libraries/tools/idl2k/src/main/kotlin/gen.kt +++ b/libraries/tools/idl2k/src/main/kotlin/gen.kt @@ -10,32 +10,11 @@ private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet() } } -fun String.isNullable() = endsWith(")?") || (endsWith("?") && !contains("->")) - -fun String.ensureNullable() = when { - this == "dynamic" -> this - isNullable() -> this - contains("->") -> "($this)?" - else -> "$this?" -} - -fun String.dropNullable() = when { - endsWith(")?") -> this.removeSuffix("?").removeSurrounding("(", ")") - contains("->") -> this - endsWith("?") -> this.removeSuffix("?") - else -> this -} - -fun String.copyNullabilityFrom(type: String) = when { - type.isNullable() -> 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( name = functionName, - returnType = mapType(repository, function.returnType).let { mapped -> if (nativeGetterOrSetter == NativeGetterOrSetter.GETTER) mapped.ensureNullable() else mapped }, + returnType = mapType(repository, function.returnType).let { mapped -> if (nativeGetterOrSetter == NativeGetterOrSetter.GETTER) mapped.toNullable() else mapped }, arguments = function.parameters.map { GenerateAttribute( name = it.name, @@ -61,16 +40,13 @@ fun generateFunctions(repository: Repository, function: Operation): List generateFunction(repository, function, "set") } val callbackArgumentsAsLambdas = function.parameters.map { - val interfaceType = repository.interfaces[it.type.dropNullable()] + val parameterType = mapType(repository, it.type) as? SimpleType + val interfaceType = repository.interfaces[parameterType?.type] when { interfaceType == null -> it interfaceType.operations.size() != 1 -> it interfaceType.callback -> interfaceType.operations.single().let { callbackFunction -> - it.copy(type = callbackFunction.parameters - .map { it.copy(type = mapType(repository, it.type)) } - .map { it.formatFunctionTypePart() } - .join(", ", "(", ") -> ${mapType(repository, callbackFunction.returnType)}") - .copyNullabilityFrom(it.type)) + it.copy(type = FunctionType(callbackFunction.parameters.map { it.copy(type = mapType(repository, it.type)) }, mapType(repository, callbackFunction.returnType), parameterType?.nullable ?: false)) } else -> it } @@ -161,7 +137,7 @@ fun generateTrait(repository: Repository, iface: InterfaceDefinition): GenerateT fun generateConstructorAsFunction(repository: Repository, constructor: ExtendedAttribute) = generateFunction( repository, - Operation("", "Unit", constructor.arguments, emptyList(), false), + Operation("", UnitType, constructor.arguments, emptyList(), false), functionName = "", nativeGetterOrSetter = NativeGetterOrSetter.NONE) @@ -187,7 +163,7 @@ fun mapUnionType(it: UnionType) = GenerateTraitOrClass( secondaryConstructors = emptyList() ) -fun generateUnionTypeTraits(allUnionTypes: Iterable): List = allUnionTypes.map(::mapUnionType) +fun generateUnionTypeTraits(allUnionTypes: Sequence): Sequence = allUnionTypes.map(::mapUnionType) fun mapDefinitions(repository: Repository, definitions: Iterable) = definitions.filter { "NoInterfaceObject" !in it.extendedAttributes.map { it.call } }.map { generateTrait(repository, it) } @@ -199,13 +175,14 @@ fun generateUnions(ifaces: List, typedefs: Iterable type in declaredTypes } } + val typedefsToBeGenerated = typedefs.filter { it.types is UnionType } + .map { NamedValue(it.name, it.types as UnionType) } + .filter { it.value.memberTypes.all { type -> type is SimpleType && type.type in declaredTypes } } + val typedefsMarkersMap = typedefsToBeGenerated.groupBy { it.name }.mapValues { mapUnionType(it.value.first().value).copy(name = it.key) } - val typeNamesToUnions = anonymousUnionTypes.flatMap { unionType -> unionType.memberTypes.map { unionMember -> unionMember to unionType.name } }.toMultiMap() merge - typedefsToBeGenerated.flatMap { typedef -> typedef.value.memberTypes.map { unionMember -> unionMember to typedef.name } }.toMultiMap() + val typeNamesToUnions = anonymousUnionTypes.toList().flatMap { unionType -> unionType.memberTypes.filterIsInstance().map { unionMember -> unionMember.type to unionType.name } }.toMultiMap() merge + typedefsToBeGenerated.flatMap { typedef -> typedef.value.memberTypes.filterIsInstance().map { unionMember -> unionMember.type to typedef.name } }.toMultiMap() return GenerateUnionTypes( typeNamesToUnionsMap = typeNamesToUnions, diff --git a/libraries/tools/idl2k/src/main/kotlin/idl.kt b/libraries/tools/idl2k/src/main/kotlin/idl.kt index 227ab43b5a0..9ac857f2bf0 100644 --- a/libraries/tools/idl2k/src/main/kotlin/idl.kt +++ b/libraries/tools/idl2k/src/main/kotlin/idl.kt @@ -28,11 +28,9 @@ import org.antlr.webidl.WebIDLParser.* 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, val static: Boolean) -data class Attribute(val name: String, val type: String, val readOnly: Boolean = true, val defaultValue: String? = null, val vararg: Boolean, val static: Boolean) -data class Constant(val name: String, val type: String, val value: String?) - -fun Attribute.formatFunctionTypePart() = if (vararg) "vararg $type" else type +data class Operation(val name: String, val returnType: Type, val parameters: List, val attributes: List, val static: Boolean) +data class Attribute(val name: String, val type: Type, val readOnly: Boolean = true, val defaultValue: String? = null, val vararg: Boolean, val static: Boolean) +data class Constant(val name: String, val type: Type, val value: String?) enum class DefinitionKind { INTERFACE, @@ -43,7 +41,7 @@ enum class DefinitionKind { } interface Definition -data class TypedefDefinition(val types: String, val namespace: String, val name: String) : Definition +data class TypedefDefinition(val types: Type, val namespace: String, val name: String) : Definition data class InterfaceDefinition( val name: String, val namespace: String, @@ -60,13 +58,13 @@ data class InterfaceDefinition( 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>() { +class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor>() { private val arguments = ArrayList() override fun defaultResult(): List = arguments override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): List { - val attributeVisitor = AttributeVisitor() + val attributeVisitor = AttributeVisitor(namespace = namespace) attributeVisitor.visit(ctx) val parameter = attributeVisitor.visitChildren(ctx) @@ -81,7 +79,7 @@ class ExtendedAttributeArgumentsParser : WebIDLBaseVisitor>() { // [Constructor(any, Int)] // [Constructor(Int arg, String arg2 = "a")] // [name = Constructor] -class ExtendedAttributeParser : WebIDLBaseVisitor() { +class ExtendedAttributeParser(private val namespace: String) : WebIDLBaseVisitor() { private var name: String? = null private var call: String = "" private val arguments = ArrayList() @@ -96,7 +94,7 @@ class ExtendedAttributeParser : WebIDLBaseVisitor() { } override fun visitArgumentList(ctx: WebIDLParser.ArgumentListContext): ExtendedAttribute { - arguments.addAll(ExtendedAttributeArgumentsParser().visitChildren(ctx)) + arguments.addAll(ExtendedAttributeArgumentsParser(namespace).visitChildren(ctx)) return defaultResult() } @@ -104,7 +102,7 @@ class ExtendedAttributeParser : WebIDLBaseVisitor() { object : WebIDLBaseVisitor() { override fun visitTerminal(node: TerminalNode) { if (node.getSymbol().getType() == WebIDLLexer.IDENTIFIER_WEBIDL) { - arguments.add(Attribute(node.getText(), "any", true, vararg = false, static = false)) + arguments.add(Attribute(node.getText(), AnyType(), true, vararg = false, static = false)) } } }.visitChildren(ctx) @@ -118,47 +116,53 @@ class ExtendedAttributeParser : WebIDLBaseVisitor() { } } -class UnionTypeVisitor : WebIDLBaseVisitor>() { - val list = ArrayList() +class UnionTypeVisitor(val namespace: String) : WebIDLBaseVisitor>() { + val list = ArrayList() override fun defaultResult() = list - override fun visitUnionMemberType(ctx: WebIDLParser.UnionMemberTypeContext): List { - list.add(TypeVisitor().visitChildren(ctx)) + override fun visitUnionMemberType(ctx: WebIDLParser.UnionMemberTypeContext): List { + list.add(TypeVisitor(namespace).visitChildren(ctx)) return list } } -class TypeVisitor : WebIDLBaseVisitor() { - private var type = "" +class TypeVisitor(val namespace: String) : WebIDLBaseVisitor() { + private var type: Type = AnyType() override fun defaultResult() = type - override fun visitNonAnyType(ctx: WebIDLParser.NonAnyTypeContext): String { - type = ctx.getText() + override fun visitNonAnyType(ctx: WebIDLParser.NonAnyTypeContext): Type { + type = SimpleType(ctx.getText(), false) return type } - override fun visitUnionType(ctx: WebIDLParser.UnionTypeContext): String { - type = "Union<" + UnionTypeVisitor().visitChildren(ctx).joinToString(",") + ">" + override fun visitUnionType(ctx: WebIDLParser.UnionTypeContext): Type { + type = UnionType(namespace, UnionTypeVisitor(namespace).visitChildren(ctx), false) return type } - override fun visitTypeSuffix(ctx: TypeSuffixContext): String { - type += ctx.getText() + override fun visitTypeSuffix(ctx: TypeSuffixContext): Type { + when (ctx.getText()?.trim()) { + "?" -> type = type.toNullable() + "[]" -> type = ArrayType(type, false) + "[]?" -> type = ArrayType(type, true) + "?[]" -> type = ArrayType(type.toNullable(), false) + } + return type } - override fun visitTerminal(node: TerminalNode): String { - type = node.getText() + override fun visitTerminal(node: TerminalNode): Type { + type = SimpleType(node.getText(), false) return type } } -class OperationVisitor(private val attributes: List, private val static: Boolean) : WebIDLBaseVisitor() { +class OperationVisitor(private val attributes: List, private val static: Boolean, private val namespace: String) : WebIDLBaseVisitor() { private var name: String = "" - private var returnType: String = "" + private var returnType: Type = UnitType private val parameters = ArrayList() private val exts = ArrayList() @@ -178,12 +182,12 @@ class OperationVisitor(private val attributes: List, private } override fun visitReturnType(ctx: ReturnTypeContext): Operation { - returnType = TypeVisitor().visit(ctx) + returnType = TypeVisitor(namespace).visit(ctx) return defaultResult() } override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): Operation { - val attributeVisitor = AttributeVisitor(static = false) + val attributeVisitor = AttributeVisitor(static = false, namespace = namespace) attributeVisitor.visit(ctx) val parameter = attributeVisitor.visitChildren(ctx) @@ -193,8 +197,8 @@ class OperationVisitor(private val attributes: List, private } } -class AttributeVisitor(private val readOnly: Boolean = false, private val static: Boolean = false) : WebIDLBaseVisitor() { - private var type: String = "" +class AttributeVisitor(private val readOnly: Boolean = false, private val static: Boolean = false, private val namespace: String) : WebIDLBaseVisitor() { + private var type: Type = AnyType(true) private var name: String = "" private var defaultValue: String? = null private var vararg: Boolean = false @@ -202,7 +206,7 @@ class AttributeVisitor(private val readOnly: Boolean = false, private val static override fun defaultResult(): Attribute = Attribute(name, type, readOnly, defaultValue, vararg, static) override fun visitType(ctx: WebIDLParser.TypeContext): Attribute { - type = TypeVisitor().visit(ctx) + type = TypeVisitor(namespace).visit(ctx) return defaultResult() } @@ -235,7 +239,7 @@ class AttributeVisitor(private val readOnly: Boolean = false, private val static } class ConstantVisitor : WebIDLBaseVisitor() { - private var type: String = "" + private var type: Type = AnyType(false) private var name: String = "" private var value: String? = null @@ -248,7 +252,7 @@ class ConstantVisitor : WebIDLBaseVisitor() { } override fun visitConstType(ctx: WebIDLParser.ConstTypeContext): Constant { - type = ctx.getText() + type = SimpleType(ctx.getText(), false) return defaultResult() } @@ -267,7 +271,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam private var readOnly: Boolean = false private var static: Boolean = false private val inherited = ArrayList() - private var typedefType: String? = null + private var typedefType: Type? = null private var implements: String? = null private val constants = ArrayList() private var partial = false @@ -277,7 +281,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam DefinitionKind.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback) DefinitionKind.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, true, partial, callback) DefinitionKind.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "") - DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: "", namespace, name) + DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: AnyType(true), namespace, name) DefinitionKind.ENUM -> EnumDefinition(namespace, name) } @@ -290,8 +294,8 @@ class DefinitionVisitor(val extendedAttributes: List, val nam kind = DefinitionKind.TYPEDEF name = getName(ctx) - val function = OperationVisitor(memberAttributes.toList(), static).visit(ctx) - typedefType = "(${function.parameters.map { it.formatFunctionTypePart() }.join(", ")}) -> ${function.returnType}" + val function = OperationVisitor(memberAttributes.toList(), static, namespace).visit(ctx) + typedefType = FunctionType(function.parameters, function.returnType, false) memberAttributes.clear() return defaultResult() @@ -328,13 +332,13 @@ class DefinitionVisitor(val extendedAttributes: List, val nam kind = DefinitionKind.TYPEDEF name = getName(ctx) - typedefType = ctx.accept(object : WebIDLBaseVisitor() { - private var foundType = "" + typedefType = ctx.accept(object : WebIDLBaseVisitor() { + private var foundType: Type = AnyType(false) - override fun defaultResult(): String = foundType + override fun defaultResult(): Type = foundType - override fun visitType(ctx: WebIDLParser.TypeContext): String { - foundType = TypeVisitor().visit(ctx) + override fun visitType(ctx: WebIDLParser.TypeContext): Type { + foundType = TypeVisitor(namespace).visit(ctx) return defaultResult() } }) @@ -362,7 +366,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam .firstOrNull { it.getText() != "" } ?.getText() - val type = TypeVisitor().visit(ctx.children.first { it is TypeContext }) + val type = TypeVisitor(namespace).visit(ctx.children.first { it is TypeContext }) val defaultValue = object : WebIDLBaseVisitor() { private var value: String? = null @@ -398,7 +402,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } private fun visitOperationImpl(ctx: ParserRuleContext) { - operations.add(OperationVisitor(memberAttributes.toList(), static).visit(ctx)) + operations.add(OperationVisitor(memberAttributes.toList(), static, namespace).visit(ctx)) memberAttributes.clear() } @@ -436,7 +440,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } override fun visitAttributeRest(ctx: WebIDLParser.AttributeRestContext): Definition { - with(AttributeVisitor(readOnly, static)) { + with(AttributeVisitor(readOnly, static, namespace)) { visit(ctx) this@DefinitionVisitor.attributes.add(visitChildren(ctx)) } @@ -452,7 +456,7 @@ class DefinitionVisitor(val extendedAttributes: List, val nam } override fun visitExtendedAttribute(ctx: ExtendedAttributeContext): Definition { - memberAttributes.add(ExtendedAttributeParser().visit(ctx)) + memberAttributes.add(ExtendedAttributeParser(namespace).visit(ctx)) return defaultResult() } @@ -468,7 +472,7 @@ class ModuleVisitor(val declarations: MutableList, var namespace: St } override fun visitExtendedAttribute(ctx: ExtendedAttributeContext?) { - val att = with(ExtendedAttributeParser()) { + val att = with(ExtendedAttributeParser(namespace)) { visit(ctx) } diff --git a/libraries/tools/idl2k/src/main/kotlin/main.kt b/libraries/tools/idl2k/src/main/kotlin/main.kt index 00f1b9f6624..28ea796d196 100644 --- a/libraries/tools/idl2k/src/main/kotlin/main.kt +++ b/libraries/tools/idl2k/src/main/kotlin/main.kt @@ -14,7 +14,7 @@ fun main(args: Array) { return } - val repository = srcDir.walkTopDown().filter { it.isDirectory() || it.extension == "idl" }.asSequence().filter { it.isFile() }.fold(Repository(emptyMap(), emptyMap(), emptyMap(), emptyMap())) { acc, e -> + val repositoryPre = srcDir.walkTopDown().filter { it.isDirectory() || it.extension == "idl" }.asSequence().filter { it.isFile() }.fold(Repository(emptyMap(), emptyMap(), emptyMap(), emptyMap())) { acc, e -> val fileRepository = parseIDL(ANTLRFileStream(e.getAbsolutePath(), "UTF-8")) Repository( @@ -25,6 +25,8 @@ fun main(args: Array) { ) } + val repository = repositoryPre.copy(typeDefs = repositoryPre.typeDefs.mapValues { it.value.copy(mapType(repositoryPre, it.value.types)) }) + val definitions = mapDefinitions(repository, repository.interfaces.values()).map { if (it.name in relocations) { // we need this to get interfaces listed in the relocations in valid package diff --git a/libraries/tools/idl2k/src/main/kotlin/model.kt b/libraries/tools/idl2k/src/main/kotlin/model.kt index 976af17dc85..96b5056f3bd 100644 --- a/libraries/tools/idl2k/src/main/kotlin/model.kt +++ b/libraries/tools/idl2k/src/main/kotlin/model.kt @@ -27,21 +27,24 @@ data class Repository( val enums: Map ) -data class GenerateAttribute(val name: String, val type: String, val initializer: String?, val getterSetterNoImpl: Boolean, val readOnly: Boolean, val override: Boolean, var vararg: Boolean, val static: Boolean) +data class GenerateAttribute(val name: String, val type: Type, val initializer: String?, val getterSetterNoImpl: Boolean, val readOnly: Boolean, val override: Boolean, var vararg: Boolean, val static: Boolean) val GenerateAttribute.getterNoImpl: Boolean get() = getterSetterNoImpl val GenerateAttribute.setterNoImpl: Boolean get() = getterSetterNoImpl && !readOnly -val String.typeSignature: String - get() = if (contains("->")) "Function${FunctionType(this).arity}" else this +val Type.typeSignature: String + get() = when { + this is FunctionType -> "Function$arity" + else -> this.toString() + } val GenerateAttribute.signature: String get() = "$name:${type.typeSignature}" -fun GenerateAttribute.dynamicIfUnknownType(allTypes : Set, standardTypes : Set = standardTypes()) = copy(type = type.dynamicIfUnknownType(allTypes, standardTypes)) -fun List.dynamicIfUnknownType(allTypes : Set, standardTypes : Set = standardTypes()) = map { it.dynamicIfUnknownType(allTypes, standardTypes) } +fun GenerateAttribute.dynamicIfUnknownType(allTypes : Set, standardTypes : Set = standardTypes()) = copy(type = type.dynamicIfUnknownType(allTypes, standardTypes)) +fun List.dynamicIfUnknownType(allTypes : Set, standardTypes : Set = standardTypes()) = map { it.dynamicIfUnknownType(allTypes, standardTypes) } enum class NativeGetterOrSetter { NONE, @@ -54,21 +57,10 @@ enum class GenerateDefinitionKind { CLASS } -class UnionType(val namespace: String, types: Collection) { - val memberTypes = HashSet(types) - val name = "Union${this.memberTypes.sort().joinToString("Or")}" - - fun contains(type: String) = type in memberTypes - - override fun equals(other: Any?): Boolean = other is UnionType && name == other.name - override fun hashCode(): Int = name.hashCode() - override fun toString(): String = name -} - data class GenerateFunctionCall(val name: String, val arguments: List) data class GenerateFunction( val name: String, - val returnType: String, + val returnType: Type, val arguments: List, val nativeGetterOrSetter: NativeGetterOrSetter, val static: Boolean diff --git a/libraries/tools/idl2k/src/main/kotlin/render.kt b/libraries/tools/idl2k/src/main/kotlin/render.kt index f50cbf7ff5c..c3015f3e1f0 100644 --- a/libraries/tools/idl2k/src/main/kotlin/render.kt +++ b/libraries/tools/idl2k/src/main/kotlin/render.kt @@ -19,7 +19,7 @@ private fun Appendable.renderAttributeDeclaration(arg: GenerateAttribute, overri append(" ") append(arg.name) append(": ") - append(arg.type) + append(arg.type.render()) if (arg.initializer != null) { append(" = ") append(arg.initializer.replaceWrongConstants(arg.type)) @@ -39,9 +39,9 @@ private fun Appendable.renderAttributeDeclaration(arg: GenerateAttribute, overri private val keywords = setOf("interface") private fun String.parse() = if (this.startsWith("0x")) BigInteger(this.substring(2), 16) else BigInteger(this) -private fun String.replaceWrongConstants(type: String) = when { - this == "noImpl" || type == "Int" && parse() > BigInteger.valueOf(Int.MAX_VALUE.toLong()) -> "noImpl" - type == "Double" && this.matches("[0-9]+".toRegex()) -> "${this}.0" +private fun String.replaceWrongConstants(type: Type) = when { + this == "noImpl" || type is SimpleType && type.type == "Int" && parse() > BigInteger.valueOf(Int.MAX_VALUE.toLong()) -> "noImpl" + type is SimpleType && type.type == "Double" && this.matches("[0-9]+".toRegex()) -> "${this}.0" else -> this } private fun String.replaceKeywords() = if (this in keywords) this + "_" else this @@ -54,7 +54,7 @@ private fun Appendable.renderArgumentsDeclaration(args: List, } append(it.name.replaceKeywords()) append(": ") - append(it.type) + append(it.type.render()) if (!omitDefaults && it.initializer != null && it.initializer != "") { append(" = ") append(it.initializer.replaceWrongConstants(it.type)) @@ -82,7 +82,7 @@ private fun Appendable.renderFunctionDeclaration(f: GenerateFunction, override: } append("fun ${f.name.replaceKeywords()}(") renderArgumentsDeclaration(f.arguments, override) - appendln("): ${f.returnType} = noImpl") + appendln("): ${f.returnType.render()} = noImpl") } fun Appendable.render(allTypes: Map, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) { @@ -177,7 +177,7 @@ fun betterFunction(f1: GenerateFunction, f2: GenerateFunction): GenerateFunction ) private fun Pair.map(block: (F) -> T) = block(first) to block(second) -private fun Pair.betterType() = if (listOf("dynamic", "Any").any { first.contains(it) }) first else second +private fun Pair.betterType() = if (first is DynamicType || first is AnyType) 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 } } diff --git a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt index d87fc42678b..3baa8c8b9a7 100644 --- a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt +++ b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt @@ -19,30 +19,30 @@ package org.jetbrains.idl2k import java.util.* private val typeMapper = mapOf( - "unsignedlong" to "Int", - "unsignedlonglong" to "Long", - "longlong" to "Long", - "unsignedshort" to "Short", - "unsignedbyte" to "Byte", - "octet" to "Byte", - "void" to "Unit", - "boolean" to "Boolean", - "byte" to "Byte", - "short" to "Short", - "long" to "Int", - "float" to "Float", - "double" to "Double", - "any" to "Any?", - "DOMTimeStamp" to "Number", - "object" to "dynamic", // TODO map to Any? - "WindowProxy" to "Window", - "USVString" to "String", - "DOMString" to "String", - "ByteString" to "String", - "DOMError" to "dynamic", - "Elements" to "dynamic", - "Date" to "Date", - "" to "dynamic" + "unsignedlong" to SimpleType("Int", false), + "unsignedlonglong" to SimpleType("Long", false), + "longlong" to SimpleType("Long", false), + "unsignedshort" to SimpleType("Short", false), + "unsignedbyte" to SimpleType("Byte", false), + "octet" to SimpleType("Byte", false), + "void" to UnitType, + "boolean" to SimpleType("Boolean", false), + "byte" to SimpleType("Byte", false), + "short" to SimpleType("Short", false), + "long" to SimpleType("Int", false), + "float" to SimpleType("Float", false), + "double" to SimpleType("Double", false), + "any" to AnyType(true), + "DOMTimeStamp" to SimpleType("Number", false), + "object" to DynamicType, // TODO map to Any? + "WindowProxy" to SimpleType("Window", false), + "USVString" to SimpleType("String", false), + "DOMString" to SimpleType("String", false), + "ByteString" to SimpleType("String", false), + "DOMError" to DynamicType, + "Elements" to DynamicType, + "Date" to SimpleType("Date", false), + "" to DynamicType ) @@ -55,87 +55,60 @@ fun allSuperTypesImpl(roots: List, all: Map, val returnType : String) - -val FunctionType.arity : Int - get() = parameterTypes.size() -val FunctionType.text : String - get() = "(${parameterTypes.map { it.formatFunctionTypePart() }.join(", ")}) -> $returnType" - -fun FunctionType(text : String) : FunctionType { - val (parameters, returnType) = text.split("->".toRegex()).map { it.trim() }.filter { it != "" } - - return FunctionType( - parameterTypes = parameters.removeSurrounding("(", ")").split(',') - .map { it.trim() } - .filter { !it.isEmpty() } - .map { Attribute(name = "", type = it.removePrefix("vararg "), vararg = it.startsWith("vararg "), static = false) } - .toList(), - returnType = returnType - ) -} - fun standardTypes() = typeMapper.values().map {it.dropNullable()}.toSet() -fun String.dynamicIfUnknownType(allTypes: Set, standardTypes: Set = standardTypes()): String = when { - this in allTypes -> this - this in standardTypes -> this - startsWith("Array<") -> "Array<" + removePrefix("Array<").removeSuffix(">").dynamicIfUnknownType(allTypes, standardTypes) + ">" - startsWith("Union<") -> UnionType("", splitUnionType(this)).name.dynamicIfUnknownType(allTypes, standardTypes).copyNullabilityFrom(this) - isNullable() -> dropNullable().dynamicIfUnknownType(allTypes, standardTypes).ensureNullable() - contains("->") -> { - val function = FunctionType(this) +fun Type.dynamicIfUnknownType(allTypes: Set, standardTypes: Set = standardTypes()): Type = when { + this is DynamicType, this is UnitType -> this - function.copy( - returnType = function.returnType.dynamicIfUnknownType(allTypes, standardTypes), - parameterTypes = function.parameterTypes.map { it.copy(type = it.type.dynamicIfUnknownType(allTypes, standardTypes)) } - ).text - } - else -> "dynamic" + this is SimpleType && this.type in allTypes -> this + this.dropNullable() in standardTypes -> this + this is ArrayType -> copy(memberType = this.memberType.dynamicIfUnknownType(allTypes, standardTypes)) + this is UnionType -> if (this.name !in allTypes) DynamicType else this + this is FunctionType -> copy(returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes), parameterTypes = parameterTypes.map { it.copy(type = it.type.dynamicIfUnknownType(allTypes, standardTypes)) }) + + else -> DynamicType } -private fun String.dynamicIfAnyType() = if (this == "Any?") "dynamic" else this +private fun Type.dynamicIfAnyType(): Type = if (this is AnyType && this.nullable) DynamicType else this -private fun mapType(repository: Repository, type: String): String = - when { - type in typeMapper -> typeMapper[type]!! - type.isNullable() -> 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)) - type.startsWith("sequence<") -> "Array<${mapType(repository, type.removePrefix("sequence<").removeSuffix(">").trim())}>" - type.startsWith("sequence") -> "Array" - type.startsWith("Union<") -> "Union<" + splitUnionType(type).map { mapType(repository, it) }.join(",") + ">" - type in repository.typeDefs -> mapTypedef(repository, type) - type in repository.enums -> "String" - type.contains("->") -> { - val function = FunctionType(type) +private fun mapType(repository: Repository, type: Type): Type = when (type) { + is SimpleType -> { + val typeName = type.type + when { + typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable) + typeName.endsWith("?") -> mapType(repository, SimpleType(typeName.removeSuffix("?"), false)).toNullable() + typeName.endsWith("[]") -> ArrayType(memberType = mapType(repository, SimpleType(typeName.removeSuffix("[]"), false)), nullable = type.nullable) + typeName.startsWith("unrestricted") -> mapType(repository, SimpleType(typeName.removePrefix("unrestricted"), false)) + typeName.startsWith("sequence<") -> ArrayType(mapType(repository, SimpleType(typeName.removePrefix("sequence<").removeSuffix(">").trim(), false)), type.nullable) + typeName == "sequence" -> ArrayType(DynamicType, type.nullable) + typeName in repository.interfaces -> type + typeName in repository.typeDefs -> mapTypedef(repository, type) + typeName in repository.enums -> SimpleType("String", type.nullable) + typeName.startsWith("Promise<") -> DynamicType - // TODO: Remove takeWhile { !vararg } when we have varargs supported. See KT-3115 - function.copy( - returnType = mapType(repository, function.returnType).dynamicIfAnyType(), - parameterTypes = function.parameterTypes.takeWhile { !it.vararg }.map { it.copy(type = mapType(repository, it.type)) } - ).text + else -> type } - type.startsWith("Promise<") -> "dynamic" - repository.interfaces[type].hasExtendedAttribute("NoInterfaceObject") -> "dynamic" - else -> type } - -private fun mapTypedef(repository: Repository, type: String): String { - val typedef = repository.typeDefs[type]!! - - return if (!typedef.types.startsWith("Union<")) mapType(repository, typedef.types) - else if (splitUnionType(typedef.types).size() == 1) mapType(repository, splitUnionType(typedef.types).first()) - else typedef.name + is ArrayType -> type.copy(memberType = mapType(repository, type.memberType)) + is UnionType -> UnionType(type.namespace, type.memberTypes.map { mt -> mapType(repository, mt) }, type.nullable).toSingleTypeIfPossible() + is FunctionType -> type.copy( + // TODO: Remove takeWhile { !vararg } when we have varargs supported. See KT-3115 + returnType = mapType(repository, type.returnType).dynamicIfAnyType(), + parameterTypes = type.parameterTypes.takeWhile { !it.vararg }.map { it.copy(type = mapType(repository, it.type)) } + ) + else -> type } -// Union -> [A, B, C] -// Union, C> -> [A, B, C] -// Union>, C> -> [A, B, C] -private fun splitUnionType(unionType: String) = - unionType.replace("Union<".toRegex(), "").replace("[>]+".toRegex(), "").split("\\s*,\\s*".toRegex()).distinct().map {it.replace("\\?$".toRegex(), "")} +private fun mapTypedef(repository: Repository, type: SimpleType): Type { + val typedef = repository.typeDefs[type.type]!! -private fun GenerateFunction.allTypes() = sequenceOf(returnType) + arguments.asSequence().map { it.type } + return when { + typedef.types is UnionType && typedef.types.memberTypes.size() == 1 -> mapType(repository, typedef.types.memberTypes.single().withNullability(type.nullable)) + typedef.types is UnionType -> SimpleType(typedef.name, type.nullable) + else -> mapType(repository, typedef.types.withNullability(type.nullable)) + } +} + +private fun GenerateFunction?.allTypes() = if (this != null) sequenceOf(returnType) + arguments.asSequence().map { it.type } else emptySequence() private fun collectUnionTypes(allTypes: Map) = allTypes.values().asSequence() @@ -145,16 +118,16 @@ private fun collectUnionTypes(allTypes: Map) = it.memberAttributes.asSequence().map { it.type } + it.memberFunctions.asSequence().flatMap { it.allTypes() } } - .filter { it.startsWith("Union<") } - .map { splitUnionType(it) } - .filter { it.all { unionMember -> unionMember in allTypes } } - .toSet() - .map { UnionType(guessPackage(it, allTypes), it) } + .filterIsInstance() + .map { it.dropNullable() } + .filter { it.memberTypes.all { unionMember -> unionMember is SimpleType && unionMember.type in allTypes } } + .distinct() + .map { it.copy(namespace = guessPackage(it.memberTypes.filterIsInstance().map { it.type }, allTypes), types = it.memberTypes) } private fun guessPackage(types : List, allTypes: Map) = types.map { allTypes[it] } .map { it?.namespace } .filterNotNull() - .filter { it != "" } + .filter { it.isNotEmpty() } .distinct() .minBy { it.split('.').size() } ?: "" diff --git a/libraries/tools/idl2k/src/main/kotlin/types.kt b/libraries/tools/idl2k/src/main/kotlin/types.kt new file mode 100644 index 00000000000..9043db5dd69 --- /dev/null +++ b/libraries/tools/idl2k/src/main/kotlin/types.kt @@ -0,0 +1,72 @@ +package org.jetbrains.idl2k + +import java.util.* + +interface Type { + val nullable: Boolean + fun render(): String + + protected fun String.withSuffix(): String = if (nullable) "$this?" else this +} + +data object UnitType : Type { + override val nullable: Boolean + get() = false + + override fun render() = "Unit" +} +data object DynamicType : Type { + override val nullable: Boolean + get() = false + + override fun render() = "dynamic" +} +data class AnyType(override val nullable: Boolean = true) : Type { + override fun render() = "Any".withSuffix() +} +data class SimpleType(val type: String, override val nullable: Boolean) : Type { + override fun render() = type.withSuffix() +} +data class FunctionType(val parameterTypes : List, val returnType : Type, override val nullable: Boolean) : Type { + override fun render() = if (nullable) "(${renderImpl()})?" else renderImpl() + private fun renderImpl() = "(${parameterTypes.map { it.type.render() }.join(", ")}) -> ${returnType.render()}" +} + +val FunctionType.arity : Int + get() = parameterTypes.size() + +data class UnionType(val namespace: String, types: Collection, override val nullable: Boolean, val memberTypes: Set = LinkedHashSet(types.sortBy { it.toString() })) : Type { + val name = "Union${this.memberTypes.map { it.render() }.joinToString("Or")}" + + fun contains(type: Type) = type in memberTypes + override fun equals(other: Any?): Boolean = other is UnionType && memberTypes == other.memberTypes + override fun hashCode(): Int = memberTypes.hashCode() + override fun toString(): String = memberTypes.map { it.toString() }.join(", ", "Union<", ">") + + override fun render(): String = name.withSuffix() +} + +fun UnionType.toSingleTypeIfPossible() = if (this.memberTypes.size() == 1) this.memberTypes.single().withNullability(nullable) else this + +data class ArrayType(val memberType: Type, override val nullable: Boolean) : Type { + override fun render(): String = "Array<${memberType.render()}>".withSuffix() +} + +suppress("UNCHECKED_CAST") +private fun T.copyWithNullability(nullable: Boolean): T = when (this) { + is UnitType -> UnitType + is DynamicType -> this + + is AnyType -> this.copy(nullable = nullable) + is SimpleType -> this.copy(nullable = nullable) + is FunctionType -> this.copy(nullable = nullable) + is UnionType -> this.copy(types = this.memberTypes, nullable = nullable) + is ArrayType -> this.copy(nullable = nullable) + else -> throw UnsupportedOperationException() +} as T + + +private fun T.withNullabilityImpl(nullable: Boolean): T = if (this.nullable == nullable) this else copyWithNullability(nullable) +fun T.withNullability(nullable: Boolean): T = withNullabilityImpl(this.nullable or nullable) +fun T.toNullable(): T = withNullabilityImpl(true) +fun T.dropNullable(): T = withNullabilityImpl(false)