All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2021 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.ir.backend.js.transformers.irToJs

import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier
import org.jetbrains.kotlin.js.backend.ast.*

class JsIrProgramFragment(val packageFqn: String) {
    val nameBindings = mutableMapOf()
    val declarations = JsCompositeBlock()
    val exports = JsCompositeBlock()
    val importedModules = mutableListOf()
    val imports = mutableMapOf()
    var dts: String? = null
    val classes = mutableMapOf()
    val initializers = JsCompositeBlock()
    var mainFunction: JsStatement? = null
    var testFunInvocation: JsStatement? = null
    var suiteFn: JsName? = null
    val definitions = mutableSetOf()
    val polyfills = JsCompositeBlock()
}

class JsIrModule(
    val moduleName: String,
    val externalModuleName: String,
    val fragments: List
) {
    fun makeModuleHeader(): JsIrModuleHeader {
        val nameBindings = mutableMapOf()
        val definitions = mutableSetOf()
        var hasJsExports = false
        for (fragment in fragments) {
            hasJsExports = hasJsExports || !fragment.exports.isEmpty
            for ((tag, name) in fragment.nameBindings.entries) {
                nameBindings[tag] = name.toString()
            }
            definitions += fragment.definitions
        }
        return JsIrModuleHeader(moduleName, externalModuleName, definitions, nameBindings, hasJsExports, this)
    }
}

class JsIrModuleHeader(
    val moduleName: String,
    val externalModuleName: String,
    val definitions: Set,
    val nameBindings: Map,
    val hasJsExports: Boolean,
    var associatedModule: JsIrModule?
) {
    val externalNames: Set by lazy { nameBindings.keys - definitions }
}

class JsIrProgram(val modules: List) {
    val mainModule = modules.last()
    val otherModules = modules.dropLast(1)

    fun crossModuleDependencies(relativeRequirePath: Boolean): Map {
        val resolver = CrossModuleDependenciesResolver(modules.map { it.makeModuleHeader() })
        val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
        return crossModuleReferences.entries.associate {
            val module = it.key.associatedModule ?: error("Internal error: module ${it.key.moduleName} is not loaded")
            it.value.initJsImportsForModule(module)
            module to it.value
        }
    }
}

class CrossModuleDependenciesResolver(private val headers: List) {
    fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map {
        val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferecenceBuilder(it, relativeRequirePath) }
        val definitionModule = mutableMapOf()

        val mainModuleHeader = headers.last()
        val otherModuleHeaders = headers.dropLast(1)
        headerToBuilder[mainModuleHeader]!!.transitiveJsExportFrom = otherModuleHeaders

        for (header in headers) {
            val builder = headerToBuilder[header]!!
            for (definition in header.definitions) {
                require(definition !in definitionModule) { "Duplicate definition: $definition" }
                definitionModule[definition] = builder
            }
        }

        for (header in headers) {
            val builder = headerToBuilder[header]!!
            for (tag in header.externalNames) {
                val fromModuleBuilder = definitionModule[tag] ?: continue // TODO error?

                builder.imports += CrossModuleRef(fromModuleBuilder, tag)
                fromModuleBuilder.exports += tag
            }
        }

        return headers.associateWith { headerToBuilder[it]!!.buildCrossModuleRefs() }
    }
}

private fun String.prettyTag() = takeWhile { c -> c != '|' }

private class CrossModuleRef(val module: JsIrModuleCrossModuleReferecenceBuilder, val tag: String)

private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHeader, val relativeRequirePath: Boolean) {
    val imports = mutableListOf()
    val exports = mutableSetOf()
    var transitiveJsExportFrom = emptyList()

    private lateinit var exportNames: Map // tag -> index

    private fun buildExportNames() {
        var index = 0
        exportNames = exports.sorted().associateWith { index++.toJsIdentifier() }
    }

    fun buildCrossModuleRefs(): CrossModuleReferences {
        buildExportNames()
        val importedModules = mutableMapOf()

        fun import(moduleHeader: JsIrModuleHeader): JsName {
            return importedModules.getOrPut(moduleHeader) {
                val jsModuleName = JsName(moduleHeader.moduleName, false)
                JsImportedModule(moduleHeader.externalModuleName, jsModuleName, null, relativeRequirePath)
            }.internalName
        }

        val resultImports = imports.associate { crossModuleRef ->
            val tag = crossModuleRef.tag
            require(crossModuleRef.module::exportNames.isInitialized) {
                // This situation appears in case of a dependent module redefine a symbol (function) from their dependency
                "Cross module dependency resolution failed due to symbol '${tag.prettyTag()}' redefinition"
            }
            val exportedAs = crossModuleRef.module.exportNames[tag]!!
            val moduleName = import(crossModuleRef.module.header)

            tag to CrossModuleImport(exportedAs, moduleName)
        }

        val transitiveExport = transitiveJsExportFrom.mapNotNull {
            if (it.hasJsExports) import(it) else null
        }
        return CrossModuleReferences(importedModules.values.toList(), transitiveExport, exportNames, resultImports)
    }
}

class CrossModuleImport(val exportedAs: String, val moduleExporter: JsName)

class CrossModuleReferences(
    val importedModules: List, // additional Kotlin imported modules
    val transitiveJsExportFrom: List, // the list of modules which provide their js exports for transitive export
    val exports: Map, // tag -> index
    val imports: Map, // tag -> import statement
) {
    // built from imports
    var jsImports = emptyMap() // tag -> import statement
        private set

    fun initJsImportsForModule(module: JsIrModule) {
        val tagToName = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to it.value }
        jsImports = imports.entries.associate {
            val importedAs = tagToName[it.key] ?: error("Internal error: cannot find imported name for symbol ${it.key.prettyTag()}")
            val exportRef = JsNameRef(it.value.exportedAs, ReservedJsNames.makeCrossModuleNameRef(it.value.moduleExporter))
            it.key to JsVars.JsVar(importedAs, exportRef)
        }
    }

    companion object {
        val Empty = CrossModuleReferences(listOf(), emptyList(), emptyMap(), emptyMap())
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy