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:
Shagen Ogandzhanian
2018-12-14 13:13:27 +01:00
parent 8c0ad321e2
commit 714b4f25c0
8 changed files with 285 additions and 170 deletions
@@ -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"
)
+40 -21
View File
@@ -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() }