Introduce better typing for on[event-name] attributes from idl
see https://upsource.jetbrains.com/kotlin/review/KOTLIN-CR-2548
This commit is contained in:
@@ -91,4 +91,71 @@ val kotlinBuiltinInterfaces = mapOf(
|
||||
primaryConstructor = null,
|
||||
secondaryConstructors = emptyList()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val specifyEventMapper = mapOf<String, String>(
|
||||
"onbeforeunload" to "BeforeUnloadEvent",
|
||||
|
||||
"ondrag" to "DragEvent",
|
||||
"ondragend" to "DragEvent",
|
||||
"ondragenter" to "DragEvent",
|
||||
"ondragexit" to "DragEvent",
|
||||
"ondragleave" to "DragEvent",
|
||||
"ondragover" to "DragEvent",
|
||||
"ondragstart" to "DragEvent",
|
||||
"ondrop" to "DragEvent",
|
||||
|
||||
"onfetch" to "FetchEvent",
|
||||
|
||||
"onblur" to "FocusEvent",
|
||||
"onfocus" to "FocusEvent",
|
||||
|
||||
"onhashchange" to "HashChangeEvent",
|
||||
|
||||
"oninput" to "InputEvent",
|
||||
|
||||
"onkeydown" to "KeyboardEvent",
|
||||
"onkeypress" to "KeyboardEvent",
|
||||
"onkeyup" to "KeyboardEvent",
|
||||
|
||||
"onmessage" to "MessageEvent",
|
||||
|
||||
"onclick" to "MouseEvent",
|
||||
"oncontextmenu" to "MouseEvent",
|
||||
"ondblclick" to "MouseEvent",
|
||||
"onmousedown" to "MouseEvent",
|
||||
"onmouseenter" to "MouseEvent",
|
||||
"onmouseleave" to "MouseEvent",
|
||||
"onmousemove" to "MouseEvent",
|
||||
"onmouseout" to "MouseEvent",
|
||||
"onmouseover" to "MouseEvent",
|
||||
"onmouseup" to "MouseEvent",
|
||||
|
||||
"onnotificationclick" to "NotificationEvent",
|
||||
"onnotificationclose" to "NotificationEvent",
|
||||
|
||||
"onpagehide" to "PageTransitionEvent",
|
||||
"onpageshow" to "PageTransitionEvent",
|
||||
|
||||
"ongotpointercapture" to "PointerEvent",
|
||||
"onlostpointercapture" to "PointerEvent",
|
||||
"onpointercancel" to "PointerEvent",
|
||||
"onpointerdown" to "PointerEvent",
|
||||
"onpointerenter" to "PointerEvent",
|
||||
"onpointerleave" to "PointerEvent",
|
||||
"onpointermove" to "PointerEvent",
|
||||
"onpointerout" to "PointerEvent",
|
||||
"onpointerover" to "PointerEvent",
|
||||
"onpointerup" to "PointerEvent",
|
||||
|
||||
"onpopstate" to "PopStateEvent",
|
||||
|
||||
"onloadstart" to "ProgressEvent",
|
||||
"onprogress" to "ProgressEvent",
|
||||
|
||||
"onunhandledrejection" to "PromiseRejectionEvent",
|
||||
|
||||
"onstorage" to "StorageEvent",
|
||||
|
||||
"onwheel" to "WheelEvent"
|
||||
)
|
||||
|
||||
@@ -86,26 +86,26 @@ fun generateFunctions(repository: Repository, function: Operation): List<Generat
|
||||
return listOf(realFunction, getterOrSetterFunction, functionWithCallbackOrNull).filterNotNull()
|
||||
}
|
||||
|
||||
fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Attribute, nullableAttributes: Boolean): GenerateAttribute =
|
||||
GenerateAttribute(attribute.name,
|
||||
type = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it },
|
||||
initializer =
|
||||
if (putNoImpl && !attribute.static) {
|
||||
mapLiteral(attribute.defaultValue, mapType(repository, attribute.type), repository.enums)
|
||||
}
|
||||
else if (attribute.defaultValue != null) {
|
||||
"definedExternally"
|
||||
}
|
||||
else {
|
||||
null
|
||||
},
|
||||
getterSetterNoImpl = putNoImpl,
|
||||
kind = if (attribute.readOnly) AttributeKind.VAL else AttributeKind.VAR,
|
||||
override = false,
|
||||
vararg = attribute.vararg,
|
||||
static = attribute.static,
|
||||
required = attribute.required
|
||||
)
|
||||
fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Attribute, nullableAttributes: Boolean): GenerateAttribute {
|
||||
val mappedType = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it }
|
||||
return GenerateAttribute(attribute.name,
|
||||
type = generalizeType(attribute.name, mappedType),
|
||||
initializer =
|
||||
if (putNoImpl && !attribute.static) {
|
||||
mapLiteral(attribute.defaultValue, mapType(repository, attribute.type), repository.enums)
|
||||
} else if (attribute.defaultValue != null) {
|
||||
"definedExternally"
|
||||
} else {
|
||||
null
|
||||
},
|
||||
getterSetterNoImpl = putNoImpl,
|
||||
kind = if (attribute.readOnly) AttributeKind.VAL else AttributeKind.VAR,
|
||||
override = false,
|
||||
vararg = attribute.vararg,
|
||||
static = attribute.static,
|
||||
required = attribute.required
|
||||
)
|
||||
}
|
||||
|
||||
private fun InterfaceDefinition.superTypes(repository: Repository) = superTypes.map { repository.interfaces[it] }.filterNotNull()
|
||||
private fun resolveDefinitionKind(repository: Repository, iface: InterfaceDefinition, constructors: List<ExtendedAttribute> = iface.findConstructors()): GenerateDefinitionKind =
|
||||
@@ -259,6 +259,25 @@ private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun specifyType(name: String, type: Type): Type {
|
||||
if ((type is SimpleType) && (type.type == "Event")) {
|
||||
specifyEventMapper[name]?.let {
|
||||
return type.copy(type = it)
|
||||
}
|
||||
}
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
private fun generalizeType(name: String, type: Type): Type {
|
||||
if ((type is FunctionType) && (type.parameterTypes.size == 1)) {
|
||||
val paramAttribute = type.parameterTypes[0]
|
||||
return type.copy(parameterTypes = listOf(paramAttribute.copy(type = specifyType(name, paramAttribute.type))))
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
fun implementInterfaces(declarations: List<GenerateTraitOrClass>) {
|
||||
val unimplementedMemberMap = getUnimplementedMembers(declarations)
|
||||
val nonAbstractDeclarations = declarations.filter { it.kind == GenerateDefinitionKind.CLASS }
|
||||
@@ -266,7 +285,7 @@ fun implementInterfaces(declarations: List<GenerateTraitOrClass>) {
|
||||
val unimplementedMembers = unimplementedMemberMap[declaration.name] ?: continue
|
||||
|
||||
for (attribute in unimplementedMembers.attributes) {
|
||||
declaration.memberAttributes += attribute.copy(override = true)
|
||||
declaration.memberAttributes += attribute.copy(override = true, type = generalizeType(attribute.name, attribute.type))
|
||||
}
|
||||
for (function in unimplementedMembers.functions) {
|
||||
declaration.memberFunctions += function.copy(override = true)
|
||||
|
||||
@@ -19,42 +19,49 @@ package org.jetbrains.idl2k
|
||||
import java.util.*
|
||||
|
||||
private val typeMapper = mapOf(
|
||||
"unsignedlong" to SimpleType("Int", false),
|
||||
"unsignedlonglong" to SimpleType("Int", false),
|
||||
"longlong" to SimpleType("Int", 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
|
||||
"unsignedlong" to SimpleType("Int", false),
|
||||
"unsignedlonglong" to SimpleType("Int", false),
|
||||
"longlong" to SimpleType("Int", 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
|
||||
)
|
||||
|
||||
|
||||
fun GenerateTraitOrClass.allSuperTypes(all: Map<String, GenerateTraitOrClass>) = LinkedHashSet<GenerateTraitOrClass>().let { result -> allSuperTypesImpl(listOf(this), all, result); result.toList() }
|
||||
fun GenerateTraitOrClass.allSuperTypes(all: Map<String, GenerateTraitOrClass>) =
|
||||
LinkedHashSet<GenerateTraitOrClass>().let { result -> allSuperTypesImpl(listOf(this), all, result); result.toList() }
|
||||
|
||||
tailrec fun allSuperTypesImpl(roots: List<GenerateTraitOrClass>, all: Map<String, GenerateTraitOrClass>, result: MutableSet<GenerateTraitOrClass>) {
|
||||
tailrec fun allSuperTypesImpl(
|
||||
roots: List<GenerateTraitOrClass>,
|
||||
all: Map<String, GenerateTraitOrClass>,
|
||||
result: MutableSet<GenerateTraitOrClass>
|
||||
) {
|
||||
if (roots.isNotEmpty()) {
|
||||
allSuperTypesImpl(roots.flatMap { it.superTypes }.map { all[it] ?: all[it.substringBefore("<")] }.filterNotNull().filter { result.add(it) }, all, result)
|
||||
allSuperTypesImpl(roots.flatMap { it.superTypes }.map {
|
||||
all[it] ?: all[it.substringBefore("<")]
|
||||
}.filterNotNull().filter { result.add(it) }, all, result)
|
||||
}
|
||||
}
|
||||
|
||||
fun standardTypes() = typeMapper.values.map {it.dropNullable()}.toSet()
|
||||
fun standardTypes() = typeMapper.values.map { it.dropNullable() }.toSet()
|
||||
fun Type.dynamicIfUnknownType(allTypes: Set<String>, standardTypes: Set<Type> = standardTypes()): Type = when {
|
||||
this is DynamicType || this is UnitType -> this
|
||||
|
||||
@@ -62,7 +69,16 @@ fun Type.dynamicIfUnknownType(allTypes: Set<String>, standardTypes: Set<Type> =
|
||||
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)) })
|
||||
this is FunctionType -> copy(
|
||||
returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes),
|
||||
parameterTypes = parameterTypes.map {
|
||||
it.copy(
|
||||
type = it.type.dynamicIfUnknownType(
|
||||
allTypes,
|
||||
standardTypes
|
||||
)
|
||||
)
|
||||
})
|
||||
this is PromiseType ->
|
||||
copy(valueType = valueType.dynamicIfUnknownType(allTypes, standardTypes))
|
||||
|
||||
@@ -84,11 +100,15 @@ internal fun mapType(repository: Repository, type: Type): Type = when (type) {
|
||||
}
|
||||
is PromiseType -> type.copy(valueType = mapType(repository, type.valueType))
|
||||
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 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)) }
|
||||
// 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)) }
|
||||
)
|
||||
|
||||
is AnyType,
|
||||
@@ -100,30 +120,39 @@ private fun mapTypedef(repository: Repository, type: SimpleType): Type {
|
||||
val typedef = repository.typeDefs[type.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 && 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 GenerateFunction?.allTypes() =
|
||||
if (this != null) sequenceOf(returnType) + arguments.asSequence().map { it.type } else emptySequence()
|
||||
|
||||
internal fun collectUnionTypes(allTypes: Map<String, GenerateTraitOrClass>) =
|
||||
allTypes.values.asSequence()
|
||||
.flatMap {
|
||||
it.secondaryConstructors.asSequence().flatMap { it.constructor.allTypes() } +
|
||||
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<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) }
|
||||
it.memberAttributes.asSequence().map { it.type } +
|
||||
it.memberFunctions.asSequence().flatMap { it.allTypes() }
|
||||
}
|
||||
.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] }
|
||||
private fun guessPackage(types: List<String>, allTypes: Map<String, GenerateTraitOrClass>) =
|
||||
types.map { allTypes[it] }
|
||||
.map { it?.namespace }
|
||||
.filterNotNull()
|
||||
.filter { it.isNotEmpty() }
|
||||
|
||||
Reference in New Issue
Block a user