[Wasm] Dukat: use IDL conversion API directly without dukat CLI

Copy unmodified convertToModel.kt from dukat
This commit is contained in:
Svyatoslav Kuzmich
2023-03-04 13:33:08 +01:00
committed by Space Team
parent 1208a26fc4
commit 2a10cf3de2
3 changed files with 930 additions and 8 deletions
@@ -28,7 +28,7 @@ private fun readCopyrightNoticeFromProfile(copyrightProfile: File): String {
}
}
private fun getHeader(): String {
internal fun getHeader(): String {
val copyrightNotice = readCopyrightNoticeFromProfile(
File("../../../.idea/copyright/apache.xml")
)
@@ -5,23 +5,66 @@
package org.jetbrains.kotlin.tools.dukat
import org.jetbrains.dukat.astModel.SourceSetModel
import org.jetbrains.dukat.astModel.modifiers.VisibilityModifierModel
import org.jetbrains.dukat.commonLowerings.AddExplicitGettersAndSetters
import org.jetbrains.dukat.idlLowerings.*
import org.jetbrains.dukat.idlParser.parseIDL
import org.jetbrains.dukat.idlReferenceResolver.DirectoryReferencesResolver
import org.jetbrains.dukat.model.commonLowerings.*
import org.jetbrains.dukat.translatorString.compileUnits
import org.jetbrains.dukat.translatorString.translateSourceSet
import org.jetbrains.kotlin.tools.dukat.wasm.convertToWasmModel
import java.io.File
import kotlin.text.Regex
fun main() {
val outputDirectory = "../../stdlib/wasm/src/org.w3c/"
launch(
outputDirectory = outputDirectory,
dynamicAsType = true,
useStaticGetters = true
)
val input = "../../stdlib/js/idl/org.w3c.dom.idl"
val sourceSet = translateIdlToSourceSet(input)
compileUnits(translateSourceSet(sourceSet), outputDirectory)
File(outputDirectory).walk().forEach {
if (it.isFile && it.name.endsWith(".kt")) {
it.writeText(postProcessIdlBindings(it.readText()))
it.writeText(getHeader() + postProcessIdlBindings(it.readText()))
}
}
}
fun translateIdlToSourceSet(fileName: String): SourceSetModel {
val translationContext = TranslationContext()
return parseIDL(fileName, DirectoryReferencesResolver())
.resolvePartials()
.addConstructors()
.resolveTypedefs()
.specifyEventHandlerTypes()
.specifyDefaultValues()
.resolveImplementsStatements()
.resolveMixins()
.addItemArrayLike()
.resolveTypes()
.markAbstractOrOpen()
.addMissingMembers()
.addOverloadsForCallbacks()
.convertToWasmModel()
.lower(
ModelContextAwareLowering(translationContext),
LowerOverrides(translationContext),
EscapeIdentificators(),
AddExplicitGettersAndSetters(),
)
.lower(ReplaceDynamics()) // Wasm-specific
.addKDocs()
.relocateDeclarations()
.resolveTopLevelVisibility(alwaysPublic())
.addImportsForUsedPackages()
.omitStdLib()
}
private fun alwaysPublic(): VisibilityModifierResolver = object : VisibilityModifierResolver {
override fun resolve(): VisibilityModifierModel = VisibilityModifierModel.PUBLIC
}
// TODO: Backport to dukat
fun postProcessIdlBindings(source: String): String {
return source
@@ -0,0 +1,879 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.dukat.wasm
import org.jetbrains.dukat.astCommon.IdentifierEntity
import org.jetbrains.dukat.astCommon.NameEntity
import org.jetbrains.dukat.astCommon.QualifierEntity
import org.jetbrains.dukat.astCommon.SimpleCommentEntity
import org.jetbrains.dukat.astCommon.appendLeft
import org.jetbrains.dukat.astCommon.rightMost
import org.jetbrains.dukat.astCommon.toNameEntity
import org.jetbrains.dukat.astModel.AnnotationModel
import org.jetbrains.dukat.astModel.ClassLikeReferenceModel
import org.jetbrains.dukat.astModel.ClassModel
import org.jetbrains.dukat.astModel.ConstructorModel
import org.jetbrains.dukat.astModel.FunctionModel
import org.jetbrains.dukat.astModel.FunctionTypeModel
import org.jetbrains.dukat.astModel.HeritageModel
import org.jetbrains.dukat.astModel.ImportModel
import org.jetbrains.dukat.astModel.InterfaceModel
import org.jetbrains.dukat.astModel.MemberModel
import org.jetbrains.dukat.astModel.MethodModel
import org.jetbrains.dukat.astModel.ModuleModel
import org.jetbrains.dukat.astModel.ObjectModel
import org.jetbrains.dukat.astModel.ParameterModel
import org.jetbrains.dukat.astModel.PropertyModel
import org.jetbrains.dukat.astModel.SourceFileModel
import org.jetbrains.dukat.astModel.SourceSetModel
import org.jetbrains.dukat.astModel.TopLevelModel
import org.jetbrains.dukat.astModel.TypeModel
import org.jetbrains.dukat.astModel.TypeParameterModel
import org.jetbrains.dukat.astModel.TypeValueModel
import org.jetbrains.dukat.astModel.VariableModel
import org.jetbrains.dukat.astModel.Variance
import org.jetbrains.dukat.astModel.expressions.literals.StringLiteralExpressionModel
import org.jetbrains.dukat.astModel.modifiers.VisibilityModifierModel
import org.jetbrains.dukat.astModel.statements.AssignmentStatementModel
import org.jetbrains.dukat.astModel.statements.BlockStatementModel
import org.jetbrains.dukat.astModel.statements.ExpressionStatementModel
import org.jetbrains.dukat.astModel.statements.ReturnStatementModel
import org.jetbrains.dukat.astModel.statements.StatementModel
import org.jetbrains.dukat.astModel.LambdaParameterModel
import org.jetbrains.dukat.astModel.expressions.*
import org.jetbrains.dukat.astModel.modifiers.InheritanceModifierModel
import org.jetbrains.dukat.idlDeclarations.*
import org.jetbrains.dukat.idlLowerings.IDLLowering
import org.jetbrains.dukat.panic.raiseConcern
import org.jetbrains.dukat.stdlib.TSLIBROOT
import org.jetbrains.dukat.stdlib.isDynamic
import org.jetbrains.dukat.stdlib.isTsStdlibPrefixed
import org.jetbrains.dukat.translator.ROOT_PACKAGENAME
import java.io.File
private fun IDLDeclaration.resolveName(): String? {
return when (this) {
is IDLDictionaryDeclaration -> name
is IDLEnumDeclaration -> name
is IDLInterfaceDeclaration -> name
is IDLTypedefDeclaration -> name
is IDLSingleTypeDeclaration -> name
else -> null
}
}
private val INLINE_ONLY_ANNOTATION = AnnotationModel(
name = IdentifierEntity("kotlin.internal.InlineOnly"),
params = listOf()
)
private class IdlFileConverter(
private val fileDeclaration: IDLFileDeclaration,
private val typeMap: Map<String, NameEntity?>,
) {
private companion object {
val stdLibTypes = setOf(
"Any",
"Array",
"Boolean",
"String",
"Int",
"Float",
"Double",
"Short",
"Number",
"dynamic",
"Promise",
"Unit"
)
val toStdMap = mapOf(
"ByteString" to "String",
"CSSOMString" to "String",
"DOMError" to "dynamic",
"DOMString" to "String",
"FrozenArray" to "Array",
"Promise" to "Promise",
"USVString" to "String",
"\$Array" to "Array",
"\$dynamic" to "dynamic",
"any" to "Any",
"boolean" to "Boolean",
"byte" to "Byte",
"double" to "Double",
"float" to "Float",
"long" to "Int",
"longlong" to "Int",
"object" to "dynamic",
"octet" to "Byte",
"record" to "dynamic",
"sequence" to "Array",
"short" to "Short",
"unrestricteddouble" to "Double",
"unrestrictedfloat" to "Float",
"unsignedlong" to "Int",
"unsignedlonglong" to "Number",
"unsignedshort" to "Short",
"void" to "Unit"
)
}
private val staticConverterNames = setOf(
"Boolean", "Byte", "Short", "Char", "Int", "Long", "Float", "Double", "String" )
private fun NameEntity.staticConverter(): String? {
return when (this) {
is QualifierEntity -> if (isTsStdlibPrefixed() && staticConverterNames.contains(right.value)) "get${right.value}" else null
is IdentifierEntity -> if (staticConverterNames.contains(value)) "get$value" else null
}
}
private fun createGetterForDynamic(expression: ExpressionModel, type: TypeModel, key: String): ExpressionModel {
val identifierExpression = IdentifierExpressionModel(IdentifierEntity(key))
return if (!true /* Use static getters */) {
IndexExpressionModel(expression, identifierExpression)
} else {
val converter = IdentifierEntity((type as? TypeValueModel)?.value?.staticConverter() ?: "getAny")
PropertyAccessExpressionModel(
left = expression,
right = CallExpressionModel(
expression = IdentifierExpressionModel(converter),
arguments = listOf(identifierExpression),
)
)
}
}
private fun String.stdFqName(): NameEntity? {
val name = toStdMap[this] ?: this
return if (stdLibTypes.contains(name)) {
QualifierEntity(TSLIBROOT, IdentifierEntity(name))
} else {
null
}
}
private fun IDLDeclaration.toFqName(): NameEntity? {
val name = resolveName() ?: return null
return name.stdFqName() ?: typeMap[name]?.appendLeft(IdentifierEntity(name))
}
private fun String.toFqName(): NameEntity? {
return stdFqName() ?: fileDeclaration.packageName?.appendLeft(IdentifierEntity(this))
}
private fun IDLSingleTypeDeclaration.convertToModel(): TypeValueModel {
val resolvedName = toStdMap[name] ?: name
val typeModel = TypeValueModel(
value = IdentifierEntity(resolvedName),
params = listOfNotNull(typeParameter?.convertToModel())
.map { TypeParameterModel(it, listOf()) }
.map {
if (name == "FrozenArray") {
it.copy(variance = Variance.COVARIANT)
} else {
it
}
},
metaDescription = comment,
fqName = toFqName()
)
return typeModel.copy(
nullable = when (typeModel.value) {
IdentifierEntity("dynamic") -> false
IdentifierEntity("Any") -> true
else -> nullable
}
)
}
private fun IDLFunctionTypeDeclaration.convertToModel(): FunctionTypeModel {
val returnTypeModel = if (returnType.name == "any") {
TypeValueModel(IdentifierEntity("dynamic"), listOf(), null, null)
} else {
returnType.convertToModel()
}
return FunctionTypeModel(
parameters = arguments.filterNot { it.variadic }.map { it.convertToLambdaParameterModel() },
type = returnTypeModel,
metaDescription = comment,
nullable = nullable
)
}
private fun IDLTypeDeclaration.convertToModel(): TypeModel {
return when (this) {
is IDLSingleTypeDeclaration -> convertToModel()
is IDLFunctionTypeDeclaration -> convertToModel()
//there shouldn't be any UnionTypeDeclarations at this stage
else -> raiseConcern("unprocessed type declaration: ${this}") {
TypeValueModel(
IdentifierEntity("IMPOSSIBLE"),
listOf(),
null,
null
)
}
}
}
private fun IDLArgumentDeclaration.convertToLambdaParameterModel(): LambdaParameterModel {
//TODO: Check whether it makes sense to have nullable names in idl
return LambdaParameterModel(
name = if (name.isEmpty()) {
null
} else {
name
},
type = type.convertToModel(),
explicitlyDeclaredType = true
)
}
private fun IDLArgumentDeclaration.convertToParameterModel(): ParameterModel {
return ParameterModel(
name = name,
type = type.convertToModel(),
initializer = if (optional || defaultValue != null) {
ExpressionStatementModel(
IdentifierExpressionModel(
IdentifierEntity("definedExternally")
)
)
} else {
null
},
vararg = variadic,
modifier = null
)
}
private fun IDLSetterDeclaration.processAsTopLevel(ownerName: NameEntity): FunctionModel {
return FunctionModel(
name = IdentifierEntity("set"),
parameters = listOf(key.convertToParameterModel(), value.convertToParameterModel()),
type = TypeValueModel(
value = IdentifierEntity("Unit"),
params = listOf(),
metaDescription = null,
fqName = "Unit".stdFqName()
),
typeParameters = listOf(),
annotations = mutableListOf(
AnnotationModel(
name = IdentifierEntity("Suppress"),
params = listOf(
IdentifierEntity("INVISIBLE_REFERENCE"),
IdentifierEntity("INVISIBLE_MEMBER")
)
),
INLINE_ONLY_ANNOTATION
),
export = false,
inline = true,
operator = true,
extend = ClassLikeReferenceModel(
name = ownerName,
typeParameters = listOf()
),
body = BlockStatementModel(listOf(
AssignmentStatementModel(
IndexExpressionModel(
CallExpressionModel(
IdentifierExpressionModel(IdentifierEntity("asDynamic")),
listOf()
),
IdentifierExpressionModel(
IdentifierEntity(key.name)
)
),
IdentifierExpressionModel(IdentifierEntity(value.name))
)
)),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = true
)
}
private fun IDLGetterDeclaration.processAsTopLevel(ownerName: NameEntity): FunctionModel {
return FunctionModel(
name = IdentifierEntity("get"),
parameters = listOf(key.convertToParameterModel()),
type = valueType.toNullableIfNotPrimitive().convertToModel(),
typeParameters = listOf(),
annotations = mutableListOf(
AnnotationModel(
name = IdentifierEntity("Suppress"),
params = listOf(
IdentifierEntity("INVISIBLE_REFERENCE"),
IdentifierEntity("INVISIBLE_MEMBER")
)
),
INLINE_ONLY_ANNOTATION
),
export = false,
inline = true,
operator = true,
extend = ClassLikeReferenceModel(
name = ownerName,
typeParameters = listOf()
),
body = BlockStatementModel(listOf(
ReturnStatementModel(
createGetterForDynamic(
expression = CallExpressionModel(
IdentifierExpressionModel(
IdentifierEntity("asDynamic")
),
listOf()
),
type = valueType.toNullableIfNotPrimitive().convertToModel(),
key = key.name,
)
)
)),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = true
)
}
private fun IDLInterfaceDeclaration.convertToModel(): List<TopLevelModel> {
if (mixin) {
return listOf()
}
val (staticAttributes, dynamicAttributes) = attributes.partition { it.static }
val (staticOperations, dynamicOperations) = operations.partition { it.static }
val dynamicMemberModels = (
constructors +
dynamicAttributes + dynamicOperations +
getters.filterNot { it.name == "get" } +
setters.filterNot { it.name == "set" }
).mapNotNull {
it.convertToModel()
}.distinct()
val staticMemberModels = (staticAttributes + staticOperations).mapNotNull {
it.convertToModel()
}.distinct()
val companionObjectModel = if (staticMemberModels.isNotEmpty()) {
ObjectModel(
name = IdentifierEntity(""),
members = staticMemberModels,
parentEntities = listOf(),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = true
)
} else {
null
}
val parentModels = (parents + unions).map {
HeritageModel(
it.convertToModel(),
listOf(),
null
)
}
val annotationModels = listOfNotNull(
if (companionObjectModel != null) {
AnnotationModel(
IdentifierEntity("Suppress"),
listOf(IdentifierEntity("NESTED_CLASS_IN_EXTERNAL_INTERFACE"))
)
} else {
null
}
).toMutableList()
val declaration = if (
kind == InterfaceKind.INTERFACE) {
InterfaceModel(
name = IdentifierEntity(name),
members = dynamicMemberModels,
companionObject = companionObjectModel,
typeParameters = listOf(),
parentEntities = parentModels,
comment = null,
annotations = annotationModels,
external = true,
visibilityModifier = VisibilityModifierModel.DEFAULT
)
} else {
ClassModel(
name = IdentifierEntity(name),
members = dynamicMemberModels,
companionObject = companionObjectModel,
typeParameters = listOf(),
parentEntities = parentModels,
primaryConstructor = if (primaryConstructor != null) {
primaryConstructor!!.convertToModel() as ConstructorModel
} else {
null
},
annotations = mutableListOf(),
comment = null,
external = true,
inheritanceModifier = if (kind == InterfaceKind.ABSTRACT_CLASS) {
InheritanceModifierModel.ABSTRACT
} else {
InheritanceModifierModel.OPEN
},
visibilityModifier = VisibilityModifierModel.DEFAULT
)
}
val getterModels = getters.map { it.processAsTopLevel(declaration.name) }
val setterModels = setters.map { it.processAsTopLevel(declaration.name) }
return listOf(declaration) + getterModels + setterModels
}
private fun IDLDictionaryMemberDeclaration.convertToParameterModel(): ParameterModel {
val type = type.toNullable().changeComment(null).convertToModel()
return ParameterModel(
name = name,
type = type,
initializer = if (defaultValue != null && !required) {
val defaultValueModel = IdentifierExpressionModel(
IdentifierEntity(defaultValue!!)
)
if (defaultValue != "undefined" && type is TypeValueModel && type.value.isDynamic) {
ExpressionStatementModel(
PropertyAccessExpressionModel(
defaultValueModel,
CallExpressionModel(
IdentifierExpressionModel(
IdentifierEntity("unsafeCast")
),
listOf(),
typeParameters = listOf(type)
)
)
)
} else {
ExpressionStatementModel(defaultValueModel)
}
} else {
null
},
vararg = false,
modifier = null
)
}
private fun IDLDictionaryMemberDeclaration.convertToAssignmentStatementModel(): AssignmentStatementModel {
return AssignmentStatementModel(
IndexExpressionModel(
IdentifierExpressionModel(
IdentifierEntity("o")
),
StringLiteralExpressionModel(name)
),
IdentifierExpressionModel(
IdentifierEntity(name)
)
)
}
fun IDLDictionaryDeclaration.generateFunctionBody(): List<StatementModel> {
val functionBody: MutableList<StatementModel> = mutableListOf(
VariableModel(
name = IdentifierEntity("o"),
immutable = true,
inline = false,
external = false,
get = null,
set = null,
extend = null,
comment = null,
explicitlyDeclaredType = false,
typeParameters = listOf(),
annotations = mutableListOf(),
visibilityModifier = VisibilityModifierModel.DEFAULT,
type = TypeValueModel(
value = IdentifierEntity("dynamic"),
params = listOf(),
fqName = null,
metaDescription = null,
),
initializer = CallExpressionModel(
IdentifierExpressionModel(
IdentifierEntity("js")
),
listOf(
StringLiteralExpressionModel(
"({})"
)
)
)
)
)
functionBody.addAll(members.map { it.convertToAssignmentStatementModel() })
functionBody.add(
ReturnStatementModel(
AsExpressionModel(
expression = IdentifierExpressionModel(
IdentifierEntity("o")
),
type = TypeValueModel(
value = IdentifierEntity(name),
params = listOf(),
metaDescription = null,
fqName = toFqName()
)
)
)
)
return functionBody
}
private fun IDLDictionaryDeclaration.convertToModel(): List<TopLevelModel> {
val declaration = InterfaceModel(
name = IdentifierEntity(name),
members = members.filterNot { it.inherited }.mapNotNull { it.convertToModel() },
companionObject = null,
typeParameters = listOf(),
parentEntities = (parents + unions).map {
HeritageModel(
it.convertToModel(),
listOf(),
null
)
},
comment = null,
annotations = mutableListOf(),
external = true,
visibilityModifier = VisibilityModifierModel.DEFAULT
)
val generatedFunction = FunctionModel(
name = IdentifierEntity(name),
parameters = members.map { it.convertToParameterModel() },
type = TypeValueModel(
value = IdentifierEntity(name),
params = listOf(),
metaDescription = null,
fqName = toFqName()
),
typeParameters = listOf(),
annotations = mutableListOf(
AnnotationModel(
name = IdentifierEntity("Suppress"),
params = listOf(
IdentifierEntity("INVISIBLE_REFERENCE"),
IdentifierEntity("INVISIBLE_MEMBER")
)
),
INLINE_ONLY_ANNOTATION
),
export = false,
inline = true,
operator = false,
extend = null,
body = BlockStatementModel(generateFunctionBody()),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = true
)
return listOf(declaration, generatedFunction)
}
private fun IDLEnumDeclaration.convertToModel(): List<TopLevelModel> {
val declaration = InterfaceModel(
name = IdentifierEntity(name),
members = listOf(),
companionObject = ObjectModel(
name = IdentifierEntity(""),
members = listOf(),
parentEntities = listOf(),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = false
),
typeParameters = listOf(),
parentEntities = unions.map {
HeritageModel(
it.convertToModel(),
listOf(),
null
)
},
comment = SimpleCommentEntity(
"please, don't implement this interface!"
),
annotations = mutableListOf(
AnnotationModel(
IdentifierEntity("JsName"),
listOf(IdentifierEntity("null"))
),
AnnotationModel(
IdentifierEntity("Suppress"),
listOf(IdentifierEntity("NESTED_CLASS_IN_EXTERNAL_INTERFACE"))
)
),
external = true,
visibilityModifier = VisibilityModifierModel.DEFAULT
)
val generatedVariables = members.map { memberName ->
val processedName = processEnumMember(memberName)
VariableModel(
name = IdentifierEntity(processEnumMember(memberName)),
type = TypeValueModel(
value = declaration.name,
params = listOf(),
metaDescription = null,
fqName = processedName.toFqName()
),
annotations = mutableListOf(),
immutable = true,
inline = true,
external = true,
initializer = null,
get = ExpressionStatementModel(
PropertyAccessExpressionModel(
PropertyAccessExpressionModel(
StringLiteralExpressionModel(
memberName.removeSurrounding("\"")
),
CallExpressionModel(
IdentifierExpressionModel(IdentifierEntity("asDynamic")),
listOf()
)
),
CallExpressionModel(
IdentifierExpressionModel(
IdentifierEntity("unsafeCast")
),
listOf(),
typeParameters = listOf(
TypeValueModel(
value = IdentifierEntity(name),
params = listOf(),
metaDescription = null,
fqName = null
)
)
)
)
),
set = null,
typeParameters = listOf(),
extend = ClassLikeReferenceModel(
name = QualifierEntity(
IdentifierEntity(name),
IdentifierEntity("Companion")
),
typeParameters = listOf()
),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
explicitlyDeclaredType = true
)
}
return listOf(declaration) + generatedVariables
}
private fun IDLNamespaceDeclaration.convertToModel(): TopLevelModel {
return ObjectModel(
name = IdentifierEntity(name),
members = attributes.mapNotNull { it.convertToModel() } +
operations.mapNotNull { it.convertToModel() },
parentEntities = listOf(),
visibilityModifier = VisibilityModifierModel.DEFAULT,
comment = null,
external = true
)
}
private fun IDLUnionDeclaration.convertToModel(): TopLevelModel {
return InterfaceModel(
name = IdentifierEntity(name),
members = listOf(),
companionObject = null,
typeParameters = listOf(),
parentEntities = unions.map {
HeritageModel(
it.convertToModel(),
listOf(),
null
)
},
comment = null,
annotations = mutableListOf(),
external = true,
visibilityModifier = VisibilityModifierModel.DEFAULT
)
}
private fun IDLTopLevelDeclaration.convertToModel(): List<TopLevelModel>? {
return when (this) {
is IDLInterfaceDeclaration -> convertToModel()
is IDLDictionaryDeclaration -> convertToModel()
is IDLEnumDeclaration -> convertToModel()
is IDLNamespaceDeclaration -> listOf(convertToModel())
is IDLTypedefDeclaration -> null
is IDLImplementsStatementDeclaration -> null
is IDLIncludesStatementDeclaration -> null
is IDLUnionDeclaration -> listOf(convertToModel())
else -> raiseConcern("unprocessed top level declaration: ${this}") { null }
}
}
private fun IDLMemberDeclaration.convertToModel(): MemberModel? {
return when (this) {
is IDLAttributeDeclaration -> PropertyModel(
name = IdentifierEntity(name),
type = type.convertToModel(),
typeParameters = listOf(),
static = false,
override = null,
immutable = readOnly,
initializer = null,
getter = type.nullable,
setter = type.nullable && !readOnly,
open = open,
explicitlyDeclaredType = true,
lateinit = false
)
is IDLOperationDeclaration -> MethodModel(
name = IdentifierEntity(name),
parameters = arguments.map { it.convertToParameterModel() },
type = returnType.convertToModel(),
typeParameters = listOf(),
static = false,
override = null,
operator = false,
annotations = listOf(),
open = false,
body = null
)
is IDLConstructorDeclaration -> ConstructorModel(
parameters = arguments.map { it.convertToParameterModel() },
typeParameters = listOf()
)
is IDLDictionaryMemberDeclaration -> PropertyModel(
name = IdentifierEntity(name),
type = type.toNullable().convertToModel(),
typeParameters = listOf(),
static = false,
override = null,
immutable = false,
initializer = null,
getter = !required,
setter = !required,
open = false,
explicitlyDeclaredType = true,
lateinit = false
)
is IDLGetterDeclaration -> MethodModel(
name = IdentifierEntity(name),
parameters = listOf(key.convertToParameterModel()),
type = valueType.convertToModel(),
typeParameters = listOf(),
static = false,
override = null,
operator = false,
annotations = listOf(),
open = false,
body = null
)
is IDLSetterDeclaration -> MethodModel(
name = IdentifierEntity(name),
parameters = listOf(key.convertToParameterModel(), value.convertToParameterModel()),
type = TypeValueModel(
value = IdentifierEntity("Unit"),
params = listOf(),
metaDescription = null,
fqName = "Unit".stdFqName()
),
typeParameters = listOf(),
static = false,
override = null,
operator = false,
annotations = listOf(),
open = false,
body = null
)
else -> raiseConcern("unprocessed member declaration: ${this}") { null }
}
}
fun convert(): SourceFileModel {
val modelsExceptEnumsAndGenerated = fileDeclaration.declarations.filterNot {
it is IDLEnumDeclaration || (it is IDLInterfaceDeclaration && it.generated)
}.mapNotNull { it.convertToModel() }.flatten()
val enumModels =
fileDeclaration.declarations.filterIsInstance<IDLEnumDeclaration>().map { it.convertToModel() }.flatten()
val generatedModels = fileDeclaration.declarations.filter {
it is IDLInterfaceDeclaration && it.generated
}.mapNotNull { it.convertToModel() }.flatten()
val module = ModuleModel(
name = fileDeclaration.packageName ?: ROOT_PACKAGENAME,
shortName = fileDeclaration.packageName?.rightMost() ?: ROOT_PACKAGENAME,
declarations = modelsExceptEnumsAndGenerated + generatedModels + enumModels,
annotations = mutableListOf(),
submodules = listOf(),
imports = mutableListOf(
ImportModel("kotlin.js.*".toNameEntity()),
ImportModel("org.khronos.webgl.*".toNameEntity()) // for typed arrays
),
comment = null
)
return SourceFileModel(
name = null,
fileName = File(fileDeclaration.fileName).normalize().absolutePath,
root = module,
referencedFiles = fileDeclaration.referencedFiles
)
}
}
private class IDLReferenceVisitor(private val visit: (IDLDeclaration, NameEntity?) -> Unit) : IDLLowering {
override fun lowerTopLevelDeclaration(
declaration: IDLTopLevelDeclaration,
owner: IDLFileDeclaration
): IDLTopLevelDeclaration {
visit(declaration, owner.packageName)
return super.lowerTopLevelDeclaration(declaration, owner)
}
}
fun IDLSourceSetDeclaration.convertToWasmModel(): SourceSetModel {
val typeMap = mutableMapOf<String, NameEntity?>()
IDLReferenceVisitor { declaration, packageName ->
declaration.resolveName()?.let {
typeMap[it] = packageName ?: ROOT_PACKAGENAME
}
}.lowerSourceSetDeclaration(this)
return SourceSetModel(
listOf("<IRRELEVANT>"),
sources = files.map { IdlFileConverter(it, typeMap).convert() }
)
}