IDL2K rework types
Fixes #KT-8015
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user