From 6dd69ea3d6c241435da007c938c63a4285f5b91d Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Fri, 21 Dec 2018 14:13:43 +0100 Subject: [PATCH] Support named constructors in idl-backed code generation this resolves https://youtrack.jetbrains.com/issue/KT-27361 --- .../stdlib/js/src/org.w3c/org.w3c.dom.kt | 303 ++++++++++++++++++ .../tools/idl2k/src/main/kotlin/config.kt | 14 +- libraries/tools/idl2k/src/main/kotlin/gen.kt | 70 ++-- .../tools/idl2k/src/main/kotlin/model.kt | 35 +- .../tools/idl2k/src/main/kotlin/render.kt | 27 +- .../idl2k/src/main/kotlin/typeMappings.kt | 42 +-- 6 files changed, 406 insertions(+), 85 deletions(-) diff --git a/libraries/stdlib/js/src/org.w3c/org.w3c.dom.kt b/libraries/stdlib/js/src/org.w3c/org.w3c.dom.kt index 708cfe93442..5f06f02b7de 100644 --- a/libraries/stdlib/js/src/org.w3c/org.w3c.dom.kt +++ b/libraries/stdlib/js/src/org.w3c/org.w3c.dom.kt @@ -647,6 +647,108 @@ public external abstract class HTMLImageElement : HTMLElement, TexImageSource, H open val y: Int } +/** + * Exposes the JavaScript [Image](https://developer.mozilla.org/en/docs/Web/API/Image) to Kotlin + */ +public external open class Image(width: Int = definedExternally, height: Int = definedExternally) : HTMLImageElement { + override val children: HTMLCollection + override val firstElementChild: Element? + override val lastElementChild: Element? + override val childElementCount: Int + override val previousElementSibling: Element? + override val nextElementSibling: Element? + override val assignedSlot: HTMLSlotElement? + override val style: CSSStyleDeclaration + override var ongotpointercapture: ((PointerEvent) -> dynamic)? + override var onlostpointercapture: ((PointerEvent) -> dynamic)? + override var onpointerdown: ((PointerEvent) -> dynamic)? + override var onpointermove: ((PointerEvent) -> dynamic)? + override var onpointerup: ((PointerEvent) -> dynamic)? + override var onpointercancel: ((PointerEvent) -> dynamic)? + override var onpointerover: ((PointerEvent) -> dynamic)? + override var onpointerout: ((PointerEvent) -> dynamic)? + override var onpointerenter: ((PointerEvent) -> dynamic)? + override var onpointerleave: ((PointerEvent) -> dynamic)? + override var onabort: ((Event) -> dynamic)? + override var onblur: ((FocusEvent) -> dynamic)? + override var oncancel: ((Event) -> dynamic)? + override var oncanplay: ((Event) -> dynamic)? + override var oncanplaythrough: ((Event) -> dynamic)? + override var onchange: ((Event) -> dynamic)? + override var onclick: ((MouseEvent) -> dynamic)? + override var onclose: ((Event) -> dynamic)? + override var oncontextmenu: ((MouseEvent) -> dynamic)? + override var oncuechange: ((Event) -> dynamic)? + override var ondblclick: ((MouseEvent) -> dynamic)? + override var ondrag: ((DragEvent) -> dynamic)? + override var ondragend: ((DragEvent) -> dynamic)? + override var ondragenter: ((DragEvent) -> dynamic)? + override var ondragexit: ((DragEvent) -> dynamic)? + override var ondragleave: ((DragEvent) -> dynamic)? + override var ondragover: ((DragEvent) -> dynamic)? + override var ondragstart: ((DragEvent) -> dynamic)? + override var ondrop: ((DragEvent) -> dynamic)? + override var ondurationchange: ((Event) -> dynamic)? + override var onemptied: ((Event) -> dynamic)? + override var onended: ((Event) -> dynamic)? + override var onerror: ((dynamic, String, Int, Int, Any?) -> dynamic)? + override var onfocus: ((FocusEvent) -> dynamic)? + override var oninput: ((InputEvent) -> dynamic)? + override var oninvalid: ((Event) -> dynamic)? + override var onkeydown: ((KeyboardEvent) -> dynamic)? + override var onkeypress: ((KeyboardEvent) -> dynamic)? + override var onkeyup: ((KeyboardEvent) -> dynamic)? + override var onload: ((Event) -> dynamic)? + override var onloadeddata: ((Event) -> dynamic)? + override var onloadedmetadata: ((Event) -> dynamic)? + override var onloadend: ((Event) -> dynamic)? + override var onloadstart: ((ProgressEvent) -> dynamic)? + override var onmousedown: ((MouseEvent) -> dynamic)? + override var onmouseenter: ((MouseEvent) -> dynamic)? + override var onmouseleave: ((MouseEvent) -> dynamic)? + override var onmousemove: ((MouseEvent) -> dynamic)? + override var onmouseout: ((MouseEvent) -> dynamic)? + override var onmouseover: ((MouseEvent) -> dynamic)? + override var onmouseup: ((MouseEvent) -> dynamic)? + override var onwheel: ((WheelEvent) -> dynamic)? + override var onpause: ((Event) -> dynamic)? + override var onplay: ((Event) -> dynamic)? + override var onplaying: ((Event) -> dynamic)? + override var onprogress: ((ProgressEvent) -> dynamic)? + override var onratechange: ((Event) -> dynamic)? + override var onreset: ((Event) -> dynamic)? + override var onresize: ((Event) -> dynamic)? + override var onscroll: ((Event) -> dynamic)? + override var onseeked: ((Event) -> dynamic)? + override var onseeking: ((Event) -> dynamic)? + override var onselect: ((Event) -> dynamic)? + override var onshow: ((Event) -> dynamic)? + override var onstalled: ((Event) -> dynamic)? + override var onsubmit: ((Event) -> dynamic)? + override var onsuspend: ((Event) -> dynamic)? + override var ontimeupdate: ((Event) -> dynamic)? + override var ontoggle: ((Event) -> dynamic)? + override var onvolumechange: ((Event) -> dynamic)? + override var onwaiting: ((Event) -> dynamic)? + override var oncopy: ((ClipboardEvent) -> dynamic)? + override var oncut: ((ClipboardEvent) -> dynamic)? + override var onpaste: ((ClipboardEvent) -> dynamic)? + override var contentEditable: String + override val isContentEditable: Boolean + override fun prepend(vararg nodes: dynamic): Unit + override fun append(vararg nodes: dynamic): Unit + override fun querySelector(selectors: String): Element? + override fun querySelectorAll(selectors: String): NodeList + override fun before(vararg nodes: dynamic): Unit + override fun after(vararg nodes: dynamic): Unit + override fun replaceWith(vararg nodes: dynamic): Unit + override fun remove(): Unit + override fun getBoxQuads(options: BoxQuadOptions /* = definedExternally */): Array + override fun convertQuadFromNode(quad: dynamic, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertRectFromNode(rect: DOMRectReadOnly, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertPointFromNode(point: DOMPointInit, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMPoint +} + /** * Exposes the JavaScript [HTMLIFrameElement](https://developer.mozilla.org/en/docs/Web/API/HTMLIFrameElement) to Kotlin */ @@ -745,6 +847,105 @@ public external abstract class HTMLVideoElement : HTMLMediaElement, TexImageSour public external abstract class HTMLAudioElement : HTMLMediaElement { } +public external open class Audio(src: String = definedExternally) : HTMLAudioElement { + override val children: HTMLCollection + override val firstElementChild: Element? + override val lastElementChild: Element? + override val childElementCount: Int + override val previousElementSibling: Element? + override val nextElementSibling: Element? + override val assignedSlot: HTMLSlotElement? + override val style: CSSStyleDeclaration + override var ongotpointercapture: ((PointerEvent) -> dynamic)? + override var onlostpointercapture: ((PointerEvent) -> dynamic)? + override var onpointerdown: ((PointerEvent) -> dynamic)? + override var onpointermove: ((PointerEvent) -> dynamic)? + override var onpointerup: ((PointerEvent) -> dynamic)? + override var onpointercancel: ((PointerEvent) -> dynamic)? + override var onpointerover: ((PointerEvent) -> dynamic)? + override var onpointerout: ((PointerEvent) -> dynamic)? + override var onpointerenter: ((PointerEvent) -> dynamic)? + override var onpointerleave: ((PointerEvent) -> dynamic)? + override var onabort: ((Event) -> dynamic)? + override var onblur: ((FocusEvent) -> dynamic)? + override var oncancel: ((Event) -> dynamic)? + override var oncanplay: ((Event) -> dynamic)? + override var oncanplaythrough: ((Event) -> dynamic)? + override var onchange: ((Event) -> dynamic)? + override var onclick: ((MouseEvent) -> dynamic)? + override var onclose: ((Event) -> dynamic)? + override var oncontextmenu: ((MouseEvent) -> dynamic)? + override var oncuechange: ((Event) -> dynamic)? + override var ondblclick: ((MouseEvent) -> dynamic)? + override var ondrag: ((DragEvent) -> dynamic)? + override var ondragend: ((DragEvent) -> dynamic)? + override var ondragenter: ((DragEvent) -> dynamic)? + override var ondragexit: ((DragEvent) -> dynamic)? + override var ondragleave: ((DragEvent) -> dynamic)? + override var ondragover: ((DragEvent) -> dynamic)? + override var ondragstart: ((DragEvent) -> dynamic)? + override var ondrop: ((DragEvent) -> dynamic)? + override var ondurationchange: ((Event) -> dynamic)? + override var onemptied: ((Event) -> dynamic)? + override var onended: ((Event) -> dynamic)? + override var onerror: ((dynamic, String, Int, Int, Any?) -> dynamic)? + override var onfocus: ((FocusEvent) -> dynamic)? + override var oninput: ((InputEvent) -> dynamic)? + override var oninvalid: ((Event) -> dynamic)? + override var onkeydown: ((KeyboardEvent) -> dynamic)? + override var onkeypress: ((KeyboardEvent) -> dynamic)? + override var onkeyup: ((KeyboardEvent) -> dynamic)? + override var onload: ((Event) -> dynamic)? + override var onloadeddata: ((Event) -> dynamic)? + override var onloadedmetadata: ((Event) -> dynamic)? + override var onloadend: ((Event) -> dynamic)? + override var onloadstart: ((ProgressEvent) -> dynamic)? + override var onmousedown: ((MouseEvent) -> dynamic)? + override var onmouseenter: ((MouseEvent) -> dynamic)? + override var onmouseleave: ((MouseEvent) -> dynamic)? + override var onmousemove: ((MouseEvent) -> dynamic)? + override var onmouseout: ((MouseEvent) -> dynamic)? + override var onmouseover: ((MouseEvent) -> dynamic)? + override var onmouseup: ((MouseEvent) -> dynamic)? + override var onwheel: ((WheelEvent) -> dynamic)? + override var onpause: ((Event) -> dynamic)? + override var onplay: ((Event) -> dynamic)? + override var onplaying: ((Event) -> dynamic)? + override var onprogress: ((ProgressEvent) -> dynamic)? + override var onratechange: ((Event) -> dynamic)? + override var onreset: ((Event) -> dynamic)? + override var onresize: ((Event) -> dynamic)? + override var onscroll: ((Event) -> dynamic)? + override var onseeked: ((Event) -> dynamic)? + override var onseeking: ((Event) -> dynamic)? + override var onselect: ((Event) -> dynamic)? + override var onshow: ((Event) -> dynamic)? + override var onstalled: ((Event) -> dynamic)? + override var onsubmit: ((Event) -> dynamic)? + override var onsuspend: ((Event) -> dynamic)? + override var ontimeupdate: ((Event) -> dynamic)? + override var ontoggle: ((Event) -> dynamic)? + override var onvolumechange: ((Event) -> dynamic)? + override var onwaiting: ((Event) -> dynamic)? + override var oncopy: ((ClipboardEvent) -> dynamic)? + override var oncut: ((ClipboardEvent) -> dynamic)? + override var onpaste: ((ClipboardEvent) -> dynamic)? + override var contentEditable: String + override val isContentEditable: Boolean + override fun prepend(vararg nodes: dynamic): Unit + override fun append(vararg nodes: dynamic): Unit + override fun querySelector(selectors: String): Element? + override fun querySelectorAll(selectors: String): NodeList + override fun before(vararg nodes: dynamic): Unit + override fun after(vararg nodes: dynamic): Unit + override fun replaceWith(vararg nodes: dynamic): Unit + override fun remove(): Unit + override fun getBoxQuads(options: BoxQuadOptions /* = definedExternally */): Array + override fun convertQuadFromNode(quad: dynamic, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertRectFromNode(rect: DOMRectReadOnly, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertPointFromNode(point: DOMPointInit, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMPoint +} + /** * Exposes the JavaScript [HTMLTrackElement](https://developer.mozilla.org/en/docs/Web/API/HTMLTrackElement) to Kotlin */ @@ -1260,6 +1461,108 @@ public external abstract class HTMLOptionElement : HTMLElement, UnionHTMLOptGrou open val index: Int } +/** + * Exposes the JavaScript [Option](https://developer.mozilla.org/en/docs/Web/API/Option) to Kotlin + */ +public external open class Option(text: String = definedExternally, value: String = definedExternally, defaultSelected: Boolean = definedExternally, selected: Boolean = definedExternally) : HTMLOptionElement { + override val children: HTMLCollection + override val firstElementChild: Element? + override val lastElementChild: Element? + override val childElementCount: Int + override val previousElementSibling: Element? + override val nextElementSibling: Element? + override val assignedSlot: HTMLSlotElement? + override val style: CSSStyleDeclaration + override var ongotpointercapture: ((PointerEvent) -> dynamic)? + override var onlostpointercapture: ((PointerEvent) -> dynamic)? + override var onpointerdown: ((PointerEvent) -> dynamic)? + override var onpointermove: ((PointerEvent) -> dynamic)? + override var onpointerup: ((PointerEvent) -> dynamic)? + override var onpointercancel: ((PointerEvent) -> dynamic)? + override var onpointerover: ((PointerEvent) -> dynamic)? + override var onpointerout: ((PointerEvent) -> dynamic)? + override var onpointerenter: ((PointerEvent) -> dynamic)? + override var onpointerleave: ((PointerEvent) -> dynamic)? + override var onabort: ((Event) -> dynamic)? + override var onblur: ((FocusEvent) -> dynamic)? + override var oncancel: ((Event) -> dynamic)? + override var oncanplay: ((Event) -> dynamic)? + override var oncanplaythrough: ((Event) -> dynamic)? + override var onchange: ((Event) -> dynamic)? + override var onclick: ((MouseEvent) -> dynamic)? + override var onclose: ((Event) -> dynamic)? + override var oncontextmenu: ((MouseEvent) -> dynamic)? + override var oncuechange: ((Event) -> dynamic)? + override var ondblclick: ((MouseEvent) -> dynamic)? + override var ondrag: ((DragEvent) -> dynamic)? + override var ondragend: ((DragEvent) -> dynamic)? + override var ondragenter: ((DragEvent) -> dynamic)? + override var ondragexit: ((DragEvent) -> dynamic)? + override var ondragleave: ((DragEvent) -> dynamic)? + override var ondragover: ((DragEvent) -> dynamic)? + override var ondragstart: ((DragEvent) -> dynamic)? + override var ondrop: ((DragEvent) -> dynamic)? + override var ondurationchange: ((Event) -> dynamic)? + override var onemptied: ((Event) -> dynamic)? + override var onended: ((Event) -> dynamic)? + override var onerror: ((dynamic, String, Int, Int, Any?) -> dynamic)? + override var onfocus: ((FocusEvent) -> dynamic)? + override var oninput: ((InputEvent) -> dynamic)? + override var oninvalid: ((Event) -> dynamic)? + override var onkeydown: ((KeyboardEvent) -> dynamic)? + override var onkeypress: ((KeyboardEvent) -> dynamic)? + override var onkeyup: ((KeyboardEvent) -> dynamic)? + override var onload: ((Event) -> dynamic)? + override var onloadeddata: ((Event) -> dynamic)? + override var onloadedmetadata: ((Event) -> dynamic)? + override var onloadend: ((Event) -> dynamic)? + override var onloadstart: ((ProgressEvent) -> dynamic)? + override var onmousedown: ((MouseEvent) -> dynamic)? + override var onmouseenter: ((MouseEvent) -> dynamic)? + override var onmouseleave: ((MouseEvent) -> dynamic)? + override var onmousemove: ((MouseEvent) -> dynamic)? + override var onmouseout: ((MouseEvent) -> dynamic)? + override var onmouseover: ((MouseEvent) -> dynamic)? + override var onmouseup: ((MouseEvent) -> dynamic)? + override var onwheel: ((WheelEvent) -> dynamic)? + override var onpause: ((Event) -> dynamic)? + override var onplay: ((Event) -> dynamic)? + override var onplaying: ((Event) -> dynamic)? + override var onprogress: ((ProgressEvent) -> dynamic)? + override var onratechange: ((Event) -> dynamic)? + override var onreset: ((Event) -> dynamic)? + override var onresize: ((Event) -> dynamic)? + override var onscroll: ((Event) -> dynamic)? + override var onseeked: ((Event) -> dynamic)? + override var onseeking: ((Event) -> dynamic)? + override var onselect: ((Event) -> dynamic)? + override var onshow: ((Event) -> dynamic)? + override var onstalled: ((Event) -> dynamic)? + override var onsubmit: ((Event) -> dynamic)? + override var onsuspend: ((Event) -> dynamic)? + override var ontimeupdate: ((Event) -> dynamic)? + override var ontoggle: ((Event) -> dynamic)? + override var onvolumechange: ((Event) -> dynamic)? + override var onwaiting: ((Event) -> dynamic)? + override var oncopy: ((ClipboardEvent) -> dynamic)? + override var oncut: ((ClipboardEvent) -> dynamic)? + override var onpaste: ((ClipboardEvent) -> dynamic)? + override var contentEditable: String + override val isContentEditable: Boolean + override fun prepend(vararg nodes: dynamic): Unit + override fun append(vararg nodes: dynamic): Unit + override fun querySelector(selectors: String): Element? + override fun querySelectorAll(selectors: String): NodeList + override fun before(vararg nodes: dynamic): Unit + override fun after(vararg nodes: dynamic): Unit + override fun replaceWith(vararg nodes: dynamic): Unit + override fun remove(): Unit + override fun getBoxQuads(options: BoxQuadOptions /* = definedExternally */): Array + override fun convertQuadFromNode(quad: dynamic, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertRectFromNode(rect: DOMRectReadOnly, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMQuad + override fun convertPointFromNode(point: DOMPointInit, from: dynamic, options: ConvertCoordinateOptions /* = definedExternally */): DOMPoint +} + /** * Exposes the JavaScript [HTMLTextAreaElement](https://developer.mozilla.org/en/docs/Web/API/HTMLTextAreaElement) to Kotlin */ diff --git a/libraries/tools/idl2k/src/main/kotlin/config.kt b/libraries/tools/idl2k/src/main/kotlin/config.kt index d1d2365b476..fedb97e7f50 100644 --- a/libraries/tools/idl2k/src/main/kotlin/config.kt +++ b/libraries/tools/idl2k/src/main/kotlin/config.kt @@ -85,15 +85,15 @@ val inheritanceExclude = mapOf( ) val kotlinBuiltinInterfaces = mapOf( - "ItemArrayLike" to GenerateTraitOrClass("ItemArrayLike", "org.w3c.dom", GenerateDefinitionKind.INTERFACE, emptyList(), - memberAttributes = mutableListOf(GenerateAttribute("length", SimpleType("Int", false), null, false, AttributeKind.VAL, false, false, false, false)), - memberFunctions = mutableListOf(GenerateFunction("item", DynamicType, listOf( + "ItemArrayLike" to GenerateClass("ItemArrayLike", "org.w3c.dom", GenerateDefinitionKind.INTERFACE, emptyList(), + memberAttributes = mutableListOf(GenerateAttribute("length", SimpleType("Int", false), null, false, AttributeKind.VAL, false, false, false, false)), + memberFunctions = mutableListOf(GenerateFunction("item", DynamicType, listOf( GenerateAttribute("index", SimpleType("Int", false), null, false, AttributeKind.ARGUMENT, false, false, false, false) ), NativeGetterOrSetter.NONE, false, false)), - constants = emptyList(), - generateBuilderFunction = false, - primaryConstructor = null, - secondaryConstructors = emptyList() + constants = emptyList(), + generateBuilderFunction = false, + primaryConstructor = null, + secondaryConstructors = emptyList() ) ) diff --git a/libraries/tools/idl2k/src/main/kotlin/gen.kt b/libraries/tools/idl2k/src/main/kotlin/gen.kt index 72c9d70cf7c..f52329840ad 100644 --- a/libraries/tools/idl2k/src/main/kotlin/gen.kt +++ b/libraries/tools/idl2k/src/main/kotlin/gen.kt @@ -107,15 +107,23 @@ fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Att ) } + +private fun InterfaceDefinition.isInterface() : Boolean { + return when { + dictionary -> true + extendedAttributes.any { it.call == "NoInterfaceObject" } -> true + callback -> true + else -> false + } +} + private fun InterfaceDefinition.superTypes(repository: Repository) = superTypes.map { repository.interfaces[it] }.filterNotNull() private fun resolveDefinitionKind(repository: Repository, iface: InterfaceDefinition, constructors: List = iface.findConstructors()): GenerateDefinitionKind = when { - iface.dictionary -> GenerateDefinitionKind.INTERFACE - iface.extendedAttributes.any { it.call == "NoInterfaceObject" } -> GenerateDefinitionKind.INTERFACE + iface.isInterface() -> GenerateDefinitionKind.INTERFACE constructors.isNotEmpty() || iface.superTypes(repository).any { resolveDefinitionKind(repository, it) == GenerateDefinitionKind.CLASS } -> { GenerateDefinitionKind.CLASS } - iface.callback -> GenerateDefinitionKind.INTERFACE else -> GenerateDefinitionKind.ABSTRACT_CLASS } @@ -125,7 +133,7 @@ private fun InterfaceDefinition.mapOperations(repository: Repository) = operatio private fun Constant.mapConstant(repository : Repository) = GenerateAttribute(name, mapType(repository, type), null, false, AttributeKind.VAL, false, false, true, false) private val EMPTY_CONSTRUCTOR = ExtendedAttribute(null, "Constructor", emptyList()) -fun generateTrait(repository: Repository, iface: InterfaceDefinition): GenerateTraitOrClass { +fun generateTrait(repository: Repository, iface: InterfaceDefinition): List { val superClasses = iface.superTypes .mapNotNull { repository.interfaces[it] } .filter { @@ -161,19 +169,38 @@ fun generateTrait(repository: Repository, iface: InterfaceDefinition): GenerateT ConstructorWithSuperTypeCall(constructorAsFunction, secondaryConstructor) } - val result = GenerateTraitOrClass(iface.name, iface.namespace, entityKind, (iface.superTypes + extensions.map { it.name }).distinct(), - memberAttributes = iface.mapAttributes(repository).toMutableList(), - memberFunctions = iface.mapOperations(repository).toMutableList(), - constants = (iface.constants.map { it.mapConstant(repository) } + extensions.flatMap { it.constants.map { it.mapConstant(repository) } }.distinct().toList()), - primaryConstructor = primaryConstructorWithCall, - secondaryConstructors = secondaryConstructorsWithCall, - generateBuilderFunction = iface.dictionary - ) + val memberAttributes = iface.mapAttributes(repository) + val memberFunctions = iface.mapOperations(repository) - return markAsArrayLikeIfApplicable(result) + val extensionsNames = extensions.map { it.name } + + val namedConstructors = + iface.findExtendedAttributes("NamedConstructor").map { namedConstructor -> + GenerateClass( + name = namedConstructor.call, + namespace = iface.namespace, + kind = GenerateDefinitionKind.CLASS, + superTypes = listOf(iface.name) + extensionsNames, + memberAttributes = memberAttributes.toMutableList(), + memberFunctions = memberFunctions.toMutableList(), + constants = emptyList(), + primaryConstructor = ConstructorWithSuperTypeCall(generateConstructorAsFunction(repository, namedConstructor), namedConstructor), + secondaryConstructors = emptyList(), + generateBuilderFunction = false + ) + } + + return (listOf(GenerateClass(iface.name, iface.namespace, entityKind, (iface.superTypes + extensionsNames).distinct(), + memberAttributes = memberAttributes.toMutableList(), + memberFunctions = memberFunctions.toMutableList(), + constants = (iface.constants.map { it.mapConstant(repository) } + extensions.flatMap { it.constants.map { it.mapConstant(repository) } }.distinct().toList()), + primaryConstructor = primaryConstructorWithCall, + secondaryConstructors = secondaryConstructorsWithCall, + generateBuilderFunction = iface.dictionary + )) + namedConstructors).map(::markAsArrayLikeIfApplicable) } -fun markAsArrayLikeIfApplicable(iface: GenerateTraitOrClass): GenerateTraitOrClass { +fun markAsArrayLikeIfApplicable(iface: GenerateClass): GenerateClass { fun isInt(type: Type) = type is SimpleType && type.type == "Int" val lengthProperty = iface.memberAttributes.singleOrNull { it.name == "length" && isInt(it.type) } @@ -191,7 +218,7 @@ fun generateConstructorAsFunction(repository: Repository, constructor: ExtendedA nativeGetterOrSetter = NativeGetterOrSetter.NONE) -fun mapUnionType(it: UnionType) = GenerateTraitOrClass( +fun mapUnionType(it: UnionType) = GenerateClass( name = it.name, namespace = it.namespace, kind = GenerateDefinitionKind.INTERFACE, @@ -204,12 +231,12 @@ fun mapUnionType(it: UnionType) = GenerateTraitOrClass( generateBuilderFunction = false ) -fun generateUnionTypeTraits(allUnionTypes: Sequence): Sequence = allUnionTypes.map(::mapUnionType) +fun generateUnionTypeTraits(allUnionTypes: Sequence): Sequence = allUnionTypes.map(::mapUnionType) fun mapDefinitions(repository: Repository, definitions: Iterable) = - definitions.map { generateTrait(repository, it) } + definitions.flatMap { generateTrait(repository, it) } -fun generateUnions(ifaces: List, typedefs: Iterable): GenerateUnionTypes { +fun generateUnions(ifaces: List, typedefs: Iterable): GenerateUnionTypes { val declaredTypes = ifaces.associateBy { it.name } val anonymousUnionTypes = collectUnionTypes(declaredTypes) @@ -259,8 +286,7 @@ private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums } } - -private fun specifyType(name: String, type: Type, context: String): Type { + private fun specifyType(name: String, type: Type, context: String): Type { if ((type is SimpleType) && (type.type == "Event")) { (eventSpecifierMapper[name] ?: eventSpecifierMapperWithContext[EventMapKey(name, context)])?.let { @@ -279,7 +305,7 @@ private fun generalizeType(name: String, type: Type, context: String): Type { return type } -fun implementInterfaces(declarations: List) : List { +fun implementInterfaces(declarations: List) : List { val unimplementedMemberMap = getUnimplementedMembers(declarations) val nonAbstractDeclarations = declarations.filter { it.kind == GenerateDefinitionKind.CLASS } for (declaration in nonAbstractDeclarations) { @@ -301,7 +327,7 @@ fun implementInterfaces(declarations: List) : List): Map { +private fun getUnimplementedMembers(declarations: List): Map { val declarationMap = declarations.associate { it.name to it } val unimplementedMemberCache = mutableMapOf() diff --git a/libraries/tools/idl2k/src/main/kotlin/model.kt b/libraries/tools/idl2k/src/main/kotlin/model.kt index 532883fe078..a51f75d4a0a 100644 --- a/libraries/tools/idl2k/src/main/kotlin/model.kt +++ b/libraries/tools/idl2k/src/main/kotlin/model.kt @@ -63,7 +63,6 @@ enum class GenerateDefinitionKind { ABSTRACT_CLASS } -data class GenerateFunctionCall(val name: String, val arguments: List) data class GenerateFunction( val name: String, val returnType: Type, @@ -74,17 +73,18 @@ data class GenerateFunction( ) data class ConstructorWithSuperTypeCall(val constructor: GenerateFunction, val constructorAttribute: ExtendedAttribute) -data class GenerateTraitOrClass( - val name: String, - val namespace: String, - val kind: GenerateDefinitionKind, - val superTypes: List, - val memberAttributes: MutableList, - val memberFunctions: MutableList, - val constants: List, - val primaryConstructor: ConstructorWithSuperTypeCall?, - val secondaryConstructors: List, - val generateBuilderFunction: Boolean + +data class GenerateClass( + val name: String, + val namespace: String, + val kind: GenerateDefinitionKind, + val superTypes: List, + val memberAttributes: MutableList, + val memberFunctions: MutableList, + val constants: List, + val primaryConstructor: ConstructorWithSuperTypeCall?, + val secondaryConstructors: List, + val generateBuilderFunction: Boolean ) val GenerateFunction.signature: String @@ -94,12 +94,11 @@ fun GenerateFunction.dynamicIfUnknownType(allTypes : Set) = standardType copy(returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes), arguments = arguments.map { it.dynamicIfUnknownType(allTypes, standardTypes) }) } -fun InterfaceDefinition.findExtendedAttributes(name: String) = extendedAttributes.filter { it.call == name } -fun InterfaceDefinition?.hasExtendedAttribute(name: String) = this?.findExtendedAttributes(name)?.isNotEmpty() ?: false -fun InterfaceDefinition.findConstructors() = findExtendedAttributes("Constructor") +fun InterfaceDefinition.findExtendedAttributes(name: String) = extendedAttributes.filter { it.name == name } +fun InterfaceDefinition.findConstructors() = extendedAttributes.filter { it.call == "Constructor" } data class GenerateUnionTypes( - val typeNamesToUnionsMap: Map>, - val anonymousUnionsMap: Map, - val typedefsMarkersMap: Map + val typeNamesToUnionsMap: Map>, + val anonymousUnionsMap: Map, + val typedefsMarkersMap: Map ) diff --git a/libraries/tools/idl2k/src/main/kotlin/render.kt b/libraries/tools/idl2k/src/main/kotlin/render.kt index f7bdb04f9d0..791c1f8c4f5 100644 --- a/libraries/tools/idl2k/src/main/kotlin/render.kt +++ b/libraries/tools/idl2k/src/main/kotlin/render.kt @@ -100,8 +100,6 @@ private fun Appendable.renderArgumentsDeclaration(args: List, StringBuilder().apply { renderAttributeDeclaration(it, if (it.override) MemberModality.OVERRIDE else MemberModality.FINAL, omitDefaults) } } -private fun renderCall(call: GenerateFunctionCall) = "${call.name.replaceKeywords()}(${call.arguments.joinToString(separator = ", ", transform = String::replaceKeywords)})" - private fun Appendable.renderFunctionDeclaration(owner: String, f: GenerateFunction, override: Boolean, commented: Boolean, level: Int = 1) { indent(commented, level) @@ -147,7 +145,8 @@ private fun GenerateFunction.isCommented(parent: String) = private fun GenerateAttribute.isRequiredFunctionArgument(owner: String, functionName: String) = "$owner.$functionName.$name" in requiredArguments private fun GenerateFunction.fixRequiredArguments(parent: String) = copy(arguments = arguments.map { arg -> arg.copy(initializer = if (arg.isRequiredFunctionArgument(parent, name)) null else arg.initializer) }) -fun Appendable.render(allTypes: Map, enums: List, typeNamesToUnions: Map>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false, mdnCache: MDNDocumentationCache? = null) { +fun Appendable.render(allTypes: Map, enums: List, typeNamesToUnions: Map>, iface: GenerateClass, markerAnnotation: Boolean = false, mdnCache: MDNDocumentationCache? = null) { + val url = "https://developer.mozilla.org/en/docs/Web/API/${iface.name}" if (mdnCache?.checkInCache(url) == true) { appendln("/**") @@ -182,6 +181,7 @@ fun Appendable.render(allTypes: Map, enums: List, enums: List MemberModality.FINAL } + val skipAttributeDeclaration = modality == MemberModality.OVERRIDE + && attribute.kindNotChanged(superAttributesByName) + && (iface.kind == GenerateDefinitionKind.INTERFACE || attribute.hasSuperImplementation(allSuperTypes)) + if (attribute.name in superAttributesByName && attribute.signature !in superSignatures) { System.err.println("Property ${iface.name}.${attribute.name} has different type in super type(s) so will not be generated: ") for ((superTypeName, attributes) in allSuperTypes.map { it.name to it.memberAttributes.filter { it.name == attribute.name }.distinct() }) { @@ -225,10 +229,7 @@ fun Appendable.render(allTypes: Map, enums: List, enums: List, enums: List>) = superAttributesByName[name].orEmpty().all { it.kind == kind } -private fun GenerateAttribute.hasSuperImplementation(allSuperTypes: List) = allSuperTypes.any { st -> st.kind != GenerateDefinitionKind.INTERFACE && st.memberAttributes.any { it.signature == signature } } +private fun GenerateAttribute.hasSuperImplementation(allSuperTypes: List) = allSuperTypes.any { st -> st.kind != GenerateDefinitionKind.INTERFACE && st.memberAttributes.any { it.signature == signature } } private fun GenerateAttribute.hasNoDefaultValue() = this.initializer == null && (this.type.nullable || this.type == DynamicType) && !this.required -fun Appendable.renderBuilderFunction(dictionary: GenerateTraitOrClass, allSuperTypes: List, allTypes: Set) { +fun Appendable.renderBuilderFunction(dictionary: GenerateClass, allSuperTypes: List, allTypes: Set) { val fields = (dictionary.memberAttributes + allSuperTypes.flatMap { it.memberAttributes }) .distinctBy { it.signature } .map { it.copy(kind = AttributeKind.ARGUMENT) } @@ -397,10 +399,11 @@ fun Appendable.render(enumDefinition: EnumDefinition) { appendln() } -fun Appendable.render(namespace: String, ifaces: List, unions: GenerateUnionTypes, enums: List, mdnCache: MDNDocumentationCache) { +fun Appendable.render(namespace: String, ifaces: List, unions: GenerateUnionTypes, enums: List, mdnCache: MDNDocumentationCache) { val declaredTypes = ifaces.associateBy { it.name } val allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap + declaredTypes.values.filter { it.namespace == namespace }.forEach { render(allTypes, enums, unions.typeNamesToUnionsMap, it, mdnCache = mdnCache) } diff --git a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt index 58a326065d4..7bebab90827 100644 --- a/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt +++ b/libraries/tools/idl2k/src/main/kotlin/typeMappings.kt @@ -46,14 +46,9 @@ private val typeMapper = mapOf( ) -fun GenerateTraitOrClass.allSuperTypes(all: Map) = - LinkedHashSet().let { result -> allSuperTypesImpl(listOf(this), all, result); result.toList() } +fun GenerateClass.allSuperTypes(all: Map) = LinkedHashSet().let { result -> allSuperTypesImpl(listOf(this), all, result); result.toList() } -tailrec fun allSuperTypesImpl( - roots: List, - all: Map, - result: MutableSet -) { +tailrec fun allSuperTypesImpl(roots: List, all: Map, result: MutableSet) { if (roots.isNotEmpty()) { allSuperTypesImpl(roots.flatMap { it.superTypes }.map { all[it] ?: all[it.substringBefore("<")] @@ -132,27 +127,22 @@ private fun mapTypedef(repository: Repository, type: SimpleType): Type { private fun GenerateFunction?.allTypes() = if (this != null) sequenceOf(returnType) + arguments.asSequence().map { it.type } else emptySequence() -internal fun collectUnionTypes(allTypes: Map) = - allTypes.values.asSequence() - .flatMap { - it.secondaryConstructors.asSequence().flatMap { it.constructor.allTypes() } + +internal fun collectUnionTypes(allTypes: Map) = + allTypes.values.asSequence() + .flatMap { + it.secondaryConstructors.asSequence().flatMap { it.constructor.allTypes() } + sequenceOf(it.primaryConstructor).filterNotNull().flatMap { it.constructor.allTypes() } + - it.memberAttributes.asSequence().map { it.type } + - it.memberFunctions.asSequence().flatMap { it.allTypes() } - } - .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 - ) - } + it.memberAttributes.asSequence().map { it.type } + + it.memberFunctions.asSequence().flatMap { it.allTypes() } + } + .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] } +private fun guessPackage(types : List, allTypes: Map) = + types.map { allTypes[it] } .map { it?.namespace } .filterNotNull() .filter { it.isNotEmpty() }