diff --git a/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launch.kt b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launch.kt index a77f39e0add..ec27fa7baae 100644 --- a/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launch.kt +++ b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launch.kt @@ -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") ) diff --git a/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launchWasm.kt b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launchWasm.kt index 9cbc943b5dc..dd9869cfab2 100644 --- a/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launchWasm.kt +++ b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/launchWasm.kt @@ -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 diff --git a/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/wasm/convertToModel.kt b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/wasm/convertToModel.kt new file mode 100644 index 00000000000..965e1dd2add --- /dev/null +++ b/libraries/tools/dukat/src/main/kotlin/org/jetbrains/kotlin/tools/dukat/wasm/convertToModel.kt @@ -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, +) { + + 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 { + 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 { + val functionBody: MutableList = 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 { + 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 { + 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? { + 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().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() + IDLReferenceVisitor { declaration, packageName -> + declaration.resolveName()?.let { + typeMap[it] = packageName ?: ROOT_PACKAGENAME + } + }.lowerSourceSetDeclaration(this) + + return SourceSetModel( + listOf(""), + sources = files.map { IdlFileConverter(it, typeMap).convert() } + ) +} \ No newline at end of file