IDL2K rework types

Fixes #KT-8015
This commit is contained in:
Sergey Mashkov
2015-06-12 21:11:37 +03:00
parent 387291cbf7
commit 68183a74fa
7 changed files with 228 additions and 208 deletions
+12 -35
View File
@@ -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<Generat
NativeGetterOrSetter.SETTER -> 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<UnionType>): List<GenerateTraitOrClass> = allUnionTypes.map(::mapUnionType)
fun generateUnionTypeTraits(allUnionTypes: Sequence<UnionType>): Sequence<GenerateTraitOrClass> = allUnionTypes.map(::mapUnionType)
fun mapDefinitions(repository: Repository, definitions: Iterable<InterfaceDefinition>) =
definitions.filter { "NoInterfaceObject" !in it.extendedAttributes.map { it.call } }.map { generateTrait(repository, it) }
@@ -199,13 +175,14 @@ fun generateUnions(ifaces: List<GenerateTraitOrClass>, typedefs: Iterable<Typede
val anonymousUnionTypeTraits = generateUnionTypeTraits(anonymousUnionTypes)
val anonymousUnionsMap = anonymousUnionTypeTraits.toMap { it.name }
val typedefsToBeGenerated = typedefs.filter { it.types.startsWith("Union<") }
.map { NamedValue(it.name, UnionType(it.namespace, splitUnionType(it.types))) }
.filter { it.value.memberTypes.all { type -> 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<SimpleType>().map { unionMember -> unionMember.type to unionType.name } }.toMultiMap() merge
typedefsToBeGenerated.flatMap { typedef -> typedef.value.memberTypes.filterIsInstance<SimpleType>().map { unionMember -> unionMember.type to typedef.name } }.toMultiMap()
return GenerateUnionTypes(
typeNamesToUnionsMap = typeNamesToUnions,
+52 -48
View File
@@ -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<Attribute>)
data class Operation(val name: String, val returnType: String, val parameters: List<Attribute>, val attributes: List<ExtendedAttribute>, 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<Attribute>, val attributes: List<ExtendedAttribute>, 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<List<Attribute>>() {
class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor<List<Attribute>>() {
private val arguments = ArrayList<Attribute>()
override fun defaultResult(): List<Attribute> = arguments
override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): List<Attribute> {
val attributeVisitor = AttributeVisitor()
val attributeVisitor = AttributeVisitor(namespace = namespace)
attributeVisitor.visit(ctx)
val parameter = attributeVisitor.visitChildren(ctx)
@@ -81,7 +79,7 @@ class ExtendedAttributeArgumentsParser : WebIDLBaseVisitor<List<Attribute>>() {
// [Constructor(any, Int)]
// [Constructor(Int arg, String arg2 = "a")]
// [name = Constructor]
class ExtendedAttributeParser : WebIDLBaseVisitor<ExtendedAttribute>() {
class ExtendedAttributeParser(private val namespace: String) : WebIDLBaseVisitor<ExtendedAttribute>() {
private var name: String? = null
private var call: String = ""
private val arguments = ArrayList<Attribute>()
@@ -96,7 +94,7 @@ class ExtendedAttributeParser : WebIDLBaseVisitor<ExtendedAttribute>() {
}
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<ExtendedAttribute>() {
object : WebIDLBaseVisitor<Unit>() {
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<ExtendedAttribute>() {
}
}
class UnionTypeVisitor : WebIDLBaseVisitor<List<String>>() {
val list = ArrayList<String>()
class UnionTypeVisitor(val namespace: String) : WebIDLBaseVisitor<List<Type>>() {
val list = ArrayList<Type>()
override fun defaultResult() = list
override fun visitUnionMemberType(ctx: WebIDLParser.UnionMemberTypeContext): List<String> {
list.add(TypeVisitor().visitChildren(ctx))
override fun visitUnionMemberType(ctx: WebIDLParser.UnionMemberTypeContext): List<Type> {
list.add(TypeVisitor(namespace).visitChildren(ctx))
return list
}
}
class TypeVisitor : WebIDLBaseVisitor<String>() {
private var type = ""
class TypeVisitor(val namespace: String) : WebIDLBaseVisitor<Type>() {
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<ExtendedAttribute>, private val static: Boolean) : WebIDLBaseVisitor<Operation>() {
class OperationVisitor(private val attributes: List<ExtendedAttribute>, private val static: Boolean, private val namespace: String) : WebIDLBaseVisitor<Operation>() {
private var name: String = ""
private var returnType: String = ""
private var returnType: Type = UnitType
private val parameters = ArrayList<Attribute>()
private val exts = ArrayList<ExtendedAttribute>()
@@ -178,12 +182,12 @@ class OperationVisitor(private val attributes: List<ExtendedAttribute>, 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<ExtendedAttribute>, private
}
}
class AttributeVisitor(private val readOnly: Boolean = false, private val static: Boolean = false) : WebIDLBaseVisitor<Attribute>() {
private var type: String = ""
class AttributeVisitor(private val readOnly: Boolean = false, private val static: Boolean = false, private val namespace: String) : WebIDLBaseVisitor<Attribute>() {
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<Constant>() {
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<Constant>() {
}
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<ExtendedAttribute>, val nam
private var readOnly: Boolean = false
private var static: Boolean = false
private val inherited = ArrayList<String>()
private var typedefType: String? = null
private var typedefType: Type? = null
private var implements: String? = null
private val constants = ArrayList<Constant>()
private var partial = false
@@ -277,7 +281,7 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, 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<ExtendedAttribute>, 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<ExtendedAttribute>, val nam
kind = DefinitionKind.TYPEDEF
name = getName(ctx)
typedefType = ctx.accept(object : WebIDLBaseVisitor<String>() {
private var foundType = ""
typedefType = ctx.accept(object : WebIDLBaseVisitor<Type>() {
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<ExtendedAttribute>, 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<String?>() {
private var value: String? = null
@@ -398,7 +402,7 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, 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<ExtendedAttribute>, 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<ExtendedAttribute>, 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<Definition>, var namespace: St
}
override fun visitExtendedAttribute(ctx: ExtendedAttributeContext?) {
val att = with(ExtendedAttributeParser()) {
val att = with(ExtendedAttributeParser(namespace)) {
visit(ctx)
}
@@ -14,7 +14,7 @@ fun main(args: Array<String>) {
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<String>) {
)
}
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
+9 -17
View File
@@ -27,21 +27,24 @@ data class Repository(
val enums: Map<String, EnumDefinition>
)
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<String>, standardTypes : Set<String> = standardTypes()) = copy(type = type.dynamicIfUnknownType(allTypes, standardTypes))
fun List<GenerateAttribute>.dynamicIfUnknownType(allTypes : Set<String>, standardTypes : Set<String> = standardTypes()) = map { it.dynamicIfUnknownType(allTypes, standardTypes) }
fun GenerateAttribute.dynamicIfUnknownType(allTypes : Set<String>, standardTypes : Set<Type> = standardTypes()) = copy(type = type.dynamicIfUnknownType(allTypes, standardTypes))
fun List<GenerateAttribute>.dynamicIfUnknownType(allTypes : Set<String>, standardTypes : Set<Type> = 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<String>) {
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<String>)
data class GenerateFunction(
val name: String,
val returnType: String,
val returnType: Type,
val arguments: List<GenerateAttribute>,
val nativeGetterOrSetter: NativeGetterOrSetter,
val static: Boolean
@@ -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<GenerateAttribute>,
}
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<String, GenerateTraitOrClass>, typeNamesToUnions: Map<String, List<String>>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) {
@@ -177,7 +177,7 @@ fun betterFunction(f1: GenerateFunction, f2: GenerateFunction): GenerateFunction
)
private fun <F, T> Pair<F, F>.map(block: (F) -> T) = block(first) to block(second)
private fun Pair<String, String>.betterType() = if (listOf("dynamic", "Any").any { first.contains(it) }) first else second
private fun Pair<Type, Type>.betterType() = if (first is DynamicType || first is AnyType) first else second
private fun Pair<String, String>.betterName() = if (((0..9).map { it.toString() } + listOf("arg")).none { first.toLowerCase().contains(it) }) first else second
fun <K, V> List<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = groupBy { it.first }.mapValues { it.value.map { it.second } }
@@ -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<GenerateTraitOrClass>, all: Map<String, Genera
}
}
data class FunctionType(val parameterTypes : List<Attribute>, 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<String>, standardTypes: Set<String> = 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<String>, standardTypes: Set<Type> = 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<dynamic>"
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> -> [A, B, C]
// Union<A, Union<B>, C> -> [A, B, C]
// Union<Union<Union<A, B>>, 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<String, GenerateTraitOrClass>) =
allTypes.values().asSequence()
@@ -145,16 +118,16 @@ private fun collectUnionTypes(allTypes: Map<String, GenerateTraitOrClass>) =
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<UnionType>()
.map { it.dropNullable() }
.filter { it.memberTypes.all { unionMember -> unionMember is SimpleType && unionMember.type in allTypes } }
.distinct()
.map { it.copy(namespace = guessPackage(it.memberTypes.filterIsInstance<SimpleType>().map { it.type }, allTypes), types = it.memberTypes) }
private fun guessPackage(types : List<String>, allTypes: Map<String, GenerateTraitOrClass>) =
types.map { allTypes[it] }
.map { it?.namespace }
.filterNotNull()
.filter { it != "" }
.filter { it.isNotEmpty() }
.distinct()
.minBy { it.split('.').size() } ?: ""
@@ -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<Attribute>, 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<Type>, override val nullable: Boolean, val memberTypes: Set<Type> = 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: Type> 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: Type> T.withNullabilityImpl(nullable: Boolean): T = if (this.nullable == nullable) this else copyWithNullability(nullable)
fun <T: Type> T.withNullability(nullable: Boolean): T = withNullabilityImpl(this.nullable or nullable)
fun <T: Type> T.toNullable(): T = withNullabilityImpl(true)
fun <T: Type> T.dropNullable(): T = withNullabilityImpl(false)