[JS IR] bring back old ir2js to work alongside with the new one
Should be removed when we're sure the new one is OK
This commit is contained in:
committed by
teamcityserver
parent
1d42b6cb3f
commit
94ed6a1dbd
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.generateWrappedModuleBody
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.generateWrappedModuleBody
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstDeserializer
|
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstDeserializer
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||||
@@ -88,7 +89,7 @@ fun compileWithIC(
|
|||||||
|
|
||||||
jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, allModules)
|
jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, allModules)
|
||||||
|
|
||||||
val transformer = IrModuleToJsTransformer(
|
val transformer = IrModuleToJsTransformerTmp(
|
||||||
context,
|
context,
|
||||||
mainArguments,
|
mainArguments,
|
||||||
fullJs = generateFullJs,
|
fullJs = generateFullJs,
|
||||||
|
|||||||
+325
-173
@@ -5,17 +5,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||||
|
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
||||||
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
|
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelToJsStatements
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.export.ExportedModule
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.export.toTypeScript
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstDeserializer
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstSerializer
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
import org.jetbrains.kotlin.ir.util.isInterface
|
import org.jetbrains.kotlin.ir.util.isInterface
|
||||||
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
|
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
|
||||||
@@ -28,9 +29,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
|
|||||||
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||||
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
|
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
|
||||||
import org.jetbrains.kotlin.js.util.TextOutputImpl
|
import org.jetbrains.kotlin.js.util.TextOutputImpl
|
||||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstSerializer
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class IrModuleToJsTransformer(
|
class IrModuleToJsTransformer(
|
||||||
@@ -47,201 +46,318 @@ class IrModuleToJsTransformer(
|
|||||||
) {
|
) {
|
||||||
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
||||||
|
|
||||||
private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
|
||||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
|
||||||
|
|
||||||
fun generateBinaryAst(files: Iterable<IrFile>): Map<String, ByteArray> {
|
|
||||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
|
||||||
|
|
||||||
val exportData = files.associate { file ->
|
|
||||||
file to exportModelGenerator.generateExport(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
|
||||||
|
|
||||||
return generateBinaryAst(files, exportData)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateModule(modules: Iterable<IrModuleFragment>): CompilerResult {
|
fun generateModule(modules: Iterable<IrModuleFragment>): CompilerResult {
|
||||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
val additionalPackages = with(backendContext) {
|
||||||
|
externalPackageFragment.values + listOf(
|
||||||
val exportData = modules.associate { module ->
|
bodilessBuiltInsPackageFragment,
|
||||||
module to module.files.associate { file ->
|
) + packageLevelJsModules
|
||||||
file to exportModelGenerator.generateExport(file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val dts = wrapTypeScript(mainModuleName, moduleKind, exportData.values.flatMap { it.values.flatMap { it } }.toTypeScript(moduleKind))
|
val moduleKind: ModuleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||||
|
val exportedModule = ExportModelGenerator(backendContext, generateNamespacesForPackages = true).generateExport(modules, moduleKind = moduleKind)
|
||||||
|
val dts = exportedModule.toTypeScript()
|
||||||
|
|
||||||
modules.forEach { module ->
|
modules.forEach { module ->
|
||||||
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsCode = if (fullJs) generateWrappedModuleBody(mainModuleName, moduleKind, generateProgramFragments(modules, exportData)) else null
|
modules.forEach { module ->
|
||||||
|
namer.merge(module.files, additionalPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsCode = if (fullJs) generateWrappedModuleBody(modules, exportedModule, namer) else null
|
||||||
|
|
||||||
val dceJsCode = if (dceJs) {
|
val dceJsCode = if (dceJs) {
|
||||||
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
|
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
|
||||||
|
// Use a fresh namer for DCE so that we could compare the result with DCE-driven
|
||||||
generateWrappedModuleBody(mainModuleName, moduleKind, generateProgramFragments(modules, exportData))
|
// TODO: is this mode relevant for scripting? If yes, refactor so that the external name tables are used here when needed.
|
||||||
|
val namer = NameTables(emptyList(), context = backendContext)
|
||||||
|
namer.merge(modules.flatMap { it.files }, additionalPackages)
|
||||||
|
generateWrappedModuleBody(modules, exportedModule, namer)
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
return CompilerResult(jsCode, dceJsCode, dts)
|
return CompilerResult(jsCode, dceJsCode, dts)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateBinaryAst(
|
private fun generateWrappedModuleBody(modules: Iterable<IrModuleFragment>, exportedModule: ExportedModule, namer: NameTables): CompilationOutputs {
|
||||||
files: Iterable<IrFile>,
|
if (multiModule) {
|
||||||
exportData: Map<IrFile, List<ExportedDeclaration>>,
|
|
||||||
): Map<String, ByteArray> {
|
|
||||||
|
|
||||||
val serializer = JsIrAstSerializer()
|
val refInfo = buildCrossModuleReferenceInfo(modules)
|
||||||
|
|
||||||
val result = mutableMapOf<String, ByteArray>()
|
val rM = modules.reversed()
|
||||||
files.forEach { f ->
|
|
||||||
val exports = exportData[f]!! // TODO
|
val main = rM.first()
|
||||||
val fragment = generateProgramFragment(f, exports)
|
val others = rM.drop(1)
|
||||||
val output = ByteArrayOutputStream()
|
|
||||||
serializer.serialize(fragment, output)
|
val mainModule = generateWrappedModuleBody2(
|
||||||
val binaryAst = output.toByteArray()
|
listOf(main),
|
||||||
result[f.fileEntry.name] = binaryAst
|
others,
|
||||||
|
exportedModule,
|
||||||
|
namer,
|
||||||
|
refInfo,
|
||||||
|
generateMainCall = true
|
||||||
|
)
|
||||||
|
|
||||||
|
val dependencies = others.mapIndexed { index, module ->
|
||||||
|
val moduleName = module.externalModuleName()
|
||||||
|
|
||||||
|
val exportedDeclarations = ExportModelGenerator(backendContext, generateNamespacesForPackages = true).let { module.files.flatMap { file -> it.generateExport(file) } }
|
||||||
|
|
||||||
|
moduleName to generateWrappedModuleBody2(
|
||||||
|
listOf(module),
|
||||||
|
others.drop(index + 1),
|
||||||
|
ExportedModule(moduleName, exportedModule.moduleKind, exportedDeclarations),
|
||||||
|
namer,
|
||||||
|
refInfo,
|
||||||
|
generateMainCall = false
|
||||||
|
)
|
||||||
|
}.reversed()
|
||||||
|
|
||||||
|
return CompilationOutputs(mainModule.jsCode, mainModule.jsProgram, mainModule.sourceMap, dependencies)
|
||||||
|
} else {
|
||||||
|
return generateWrappedModuleBody2(
|
||||||
|
modules,
|
||||||
|
emptyList(),
|
||||||
|
exportedModule,
|
||||||
|
namer,
|
||||||
|
EmptyCrossModuleReferenceInfo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateWrappedModuleBody2(
|
||||||
private fun generateProgramFragments(
|
|
||||||
modules: Iterable<IrModuleFragment>,
|
modules: Iterable<IrModuleFragment>,
|
||||||
exportData: Map<IrModuleFragment, Map<IrFile, List<ExportedDeclaration>>>,
|
dependencies: Iterable<IrModuleFragment>,
|
||||||
): List<List<JsIrProgramFragment>> {
|
exportedModule: ExportedModule,
|
||||||
|
namer: NameTables,
|
||||||
val fragments = mutableMapOf<IrFile, JsIrProgramFragment>()
|
refInfo: CrossModuleReferenceInfo,
|
||||||
modules.forEach { m ->
|
generateMainCall: Boolean = true
|
||||||
m.files.forEach { f ->
|
): CompilationOutputs {
|
||||||
val exports = exportData[m]!![f]!! // TODO
|
|
||||||
fragments[f] = generateProgramFragment(f, exports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// // TODO remove serialization -> deserialization work
|
|
||||||
// val serialized = mutableListOf<Pair<IrFile, ByteArray>>()
|
|
||||||
//
|
|
||||||
// val serializer = JsIrAstSerializer()
|
|
||||||
// fragments.entries.forEach { (file, fragment) ->
|
|
||||||
// val output = ByteArrayOutputStream()
|
|
||||||
// serializer.serialize(fragment, output)
|
|
||||||
// val binaryAst = output.toByteArray()
|
|
||||||
// serialized += file to binaryAst
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val restoredMap = mutableMapOf<IrFile, JsIrProgramFragment>()
|
|
||||||
//
|
|
||||||
// val deserializer = JsIrAstDeserializer()
|
|
||||||
//
|
|
||||||
// serialized.forEach { (file, binaryAst) ->
|
|
||||||
// restoredMap[file] = deserializer.deserialize(ByteArrayInputStream(binaryAst))
|
|
||||||
// }
|
|
||||||
|
|
||||||
return modules.map { it.files.map { fragments[it]!! } }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
|
||||||
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
|
||||||
|
|
||||||
private fun generateProgramFragment(file: IrFile, exports: List<ExportedDeclaration>): JsIrProgramFragment {
|
|
||||||
val nameGenerator = JsNameLinkingNamer(backendContext)
|
|
||||||
|
|
||||||
|
val nameGenerator = refInfo.withReferenceTracking(
|
||||||
|
IrNamerImpl(newNameTables = namer, backendContext),
|
||||||
|
modules
|
||||||
|
)
|
||||||
val staticContext = JsStaticContext(
|
val staticContext = JsStaticContext(
|
||||||
backendContext = backendContext,
|
backendContext = backendContext,
|
||||||
irNamer = nameGenerator,
|
irNamer = nameGenerator,
|
||||||
globalNameScope = namer.globalNames
|
globalNameScope = namer.globalNames
|
||||||
)
|
)
|
||||||
|
|
||||||
val result = JsIrProgramFragment(file.fqName.asString())
|
val (importStatements, importedJsModules) =
|
||||||
|
generateImportStatements(
|
||||||
|
getNameForExternalDeclaration = { staticContext.getNameForStaticDeclaration(it) },
|
||||||
|
declareFreshGlobal = { JsName(sanitizeName(it), false) } // TODO: Declare fresh name
|
||||||
|
)
|
||||||
|
|
||||||
|
val moduleBody = generateModuleBody(modules, staticContext)
|
||||||
|
|
||||||
val internalModuleName = JsName("_", false)
|
val internalModuleName = JsName("_", false)
|
||||||
val globalNames = NameTable<String>(namer.globalNames)
|
val globalNames = NameTable<String>(namer.globalNames)
|
||||||
val exportStatements = ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport(
|
val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) }
|
||||||
ExportedModule(mainModuleName, moduleKind, exports),
|
.generateModuleExport(exportedModule, internalModuleName)
|
||||||
internalModuleName,
|
|
||||||
|
val (crossModuleImports, importedKotlinModules) = generateCrossModuleImports(nameGenerator, modules, dependencies, { JsName(sanitizeName(it), false) })
|
||||||
|
val crossModuleExports = generateCrossModuleExports(modules, refInfo, internalModuleName)
|
||||||
|
|
||||||
|
val program = JsProgram()
|
||||||
|
if (generateScriptModule) {
|
||||||
|
with(program.globalBlock) {
|
||||||
|
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
||||||
|
statements += moduleBody
|
||||||
|
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function").apply {
|
||||||
|
parameters += JsParameter(internalModuleName)
|
||||||
|
parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) }
|
||||||
|
with(body) {
|
||||||
|
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
||||||
|
statements += moduleBody
|
||||||
|
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
||||||
|
if (generateMainCall) {
|
||||||
|
statements += generateCallToMain(modules, staticContext)
|
||||||
|
}
|
||||||
|
statements += JsReturn(internalModuleName.makeRef())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program.globalBlock.statements += ModuleWrapperTranslation.wrap(
|
||||||
|
exportedModule.name,
|
||||||
|
rootFunction,
|
||||||
|
importedJsModules + importedKotlinModules,
|
||||||
|
program,
|
||||||
|
kind = exportedModule.moduleKind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsCode = TextOutputImpl()
|
||||||
|
|
||||||
|
val configuration = backendContext.configuration
|
||||||
|
val sourceMapPrefix = configuration.get(JSConfigurationKeys.SOURCE_MAP_PREFIX, "")
|
||||||
|
val sourceMapsEnabled = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
|
||||||
|
|
||||||
|
val sourceMapBuilder = SourceMap3Builder(null, jsCode, sourceMapPrefix)
|
||||||
|
val sourceMapBuilderConsumer =
|
||||||
|
if (sourceMapsEnabled) {
|
||||||
|
val sourceRoots = configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, emptyList<String>()).map(::File)
|
||||||
|
val generateRelativePathsInSourceMap = sourceMapPrefix.isEmpty() && sourceRoots.isEmpty()
|
||||||
|
val outputDir = if (generateRelativePathsInSourceMap) configuration.get(JSConfigurationKeys.OUTPUT_DIR) else null
|
||||||
|
|
||||||
|
val pathResolver = SourceFilePathResolver(sourceRoots, outputDir)
|
||||||
|
|
||||||
|
val sourceMapContentEmbedding =
|
||||||
|
configuration.get(JSConfigurationKeys.SOURCE_MAP_EMBED_SOURCES, SourceMapSourceEmbedding.INLINING)
|
||||||
|
|
||||||
|
SourceMapBuilderConsumer(
|
||||||
|
File("."),
|
||||||
|
sourceMapBuilder,
|
||||||
|
pathResolver,
|
||||||
|
sourceMapContentEmbedding == SourceMapSourceEmbedding.ALWAYS,
|
||||||
|
sourceMapContentEmbedding != SourceMapSourceEmbedding.NEVER
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
program.accept(JsToStringGenerationVisitor(jsCode, sourceMapBuilderConsumer ?: NoOpSourceLocationConsumer))
|
||||||
|
|
||||||
|
return CompilationOutputs(
|
||||||
|
jsCode.toString(),
|
||||||
|
program,
|
||||||
|
if(sourceMapsEnabled) sourceMapBuilder.build() else null
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
result.exports.statements += exportStatements
|
private fun IrModuleFragment.externalModuleName(): String {
|
||||||
|
return moduleToName[this] ?: sanitizeName(safeName)
|
||||||
|
}
|
||||||
|
|
||||||
if (exports.isNotEmpty()) {
|
private fun generateCrossModuleImports(
|
||||||
result.dts = exports.toTypeScript(moduleKind)
|
namerWithImports: IrNamerWithImports,
|
||||||
}
|
currentModules: Iterable<IrModuleFragment>,
|
||||||
|
allowedDependencies: Iterable<IrModuleFragment>,
|
||||||
|
declareFreshGlobal: (String) -> JsName
|
||||||
|
): Pair<MutableList<JsStatement>, List<JsImportedModule>> {
|
||||||
|
val imports = mutableListOf<JsStatement>()
|
||||||
|
val modules = mutableListOf<JsImportedModule>()
|
||||||
|
|
||||||
val statements = result.declarations.statements
|
namerWithImports.imports().forEach { (module, names) ->
|
||||||
|
check(module in allowedDependencies) {
|
||||||
|
val deps = if (names.size > 10) "[${names.take(10).joinToString()}, ...]" else "$names"
|
||||||
val fileStatements = file.accept(IrFileToJsTransformer(), staticContext).statements
|
"Module ${currentModules.map { it.name.asString() }} depend on module ${module.name.asString()} via $deps"
|
||||||
if (fileStatements.isNotEmpty()) {
|
|
||||||
var startComment = ""
|
|
||||||
|
|
||||||
if (generateRegionComments) {
|
|
||||||
startComment = "region "
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generateRegionComments || generateFilePaths) {
|
val moduleName = declareFreshGlobal(module.safeName)
|
||||||
val originalPath = file.path
|
modules += JsImportedModule(module.externalModuleName(), moduleName, null, relativeRequirePath)
|
||||||
val path = pathPrefixMap.entries
|
|
||||||
.find { (k, _) -> originalPath.startsWith(k) }
|
|
||||||
?.let { (k, v) -> v + originalPath.substring(k.length) }
|
|
||||||
?: originalPath
|
|
||||||
|
|
||||||
startComment += "file: $path"
|
names.forEach {
|
||||||
}
|
imports += JsVars(JsVars.JsVar(JsName(it, false), JsNameRef(it, JsNameRef("\$crossModule\$", moduleName.makeRef()))))
|
||||||
|
|
||||||
if (startComment.isNotEmpty()) {
|
|
||||||
statements.add(JsSingleLineComment(startComment))
|
|
||||||
}
|
|
||||||
|
|
||||||
statements.addAll(fileStatements)
|
|
||||||
if (generateRegionComments) {
|
|
||||||
statements += JsSingleLineComment("endregion")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
staticContext.classModels.entries.forEach { (symbol, model) ->
|
return imports to modules
|
||||||
result.classes[nameGenerator.getNameForClass(symbol.owner)] = model
|
}
|
||||||
|
|
||||||
|
private fun generateCrossModuleExports(
|
||||||
|
modules: Iterable<IrModuleFragment>,
|
||||||
|
refInfo: CrossModuleReferenceInfo,
|
||||||
|
internalModuleName: JsName
|
||||||
|
): List<JsStatement> {
|
||||||
|
return modules.flatMap {
|
||||||
|
refInfo.exports(it).map {
|
||||||
|
jsAssignment(
|
||||||
|
JsNameRef(it, JsNameRef("\$crossModule\$", internalModuleName.makeRef())),
|
||||||
|
JsNameRef(it)
|
||||||
|
).makeStmt()
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
if (!it.isEmpty()) {
|
||||||
|
val createExportBlock = jsAssignment(
|
||||||
|
JsNameRef("\$crossModule\$", internalModuleName.makeRef()),
|
||||||
|
JsAstUtils.or(JsNameRef("\$crossModule\$", internalModuleName.makeRef()), JsObjectLiteral())
|
||||||
|
).makeStmt()
|
||||||
|
return listOf(createExportBlock) + it
|
||||||
|
} else it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
|
||||||
|
val statements = mutableListOf<JsStatement>().also {
|
||||||
|
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
||||||
}
|
}
|
||||||
|
|
||||||
result.initializers.statements += staticContext.initializerBlock.statements
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
|
|
||||||
if (mainArguments != null) {
|
statements.addWithComment("block: pre-declaration", preDeclarationBlock)
|
||||||
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(file)?.let {
|
|
||||||
val jsName = staticContext.getNameForStaticFunction(it)
|
val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||||
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||||
val generateContinuation = it.isLoweredSuspendFunction(backendContext)
|
|
||||||
result.mainFunction = JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, staticContext)).makeStmt()
|
modules.forEach { module ->
|
||||||
|
module.files.forEach {
|
||||||
|
val fileStatements = it.accept(IrFileToJsTransformer(), staticContext).statements
|
||||||
|
if (fileStatements.isNotEmpty()) {
|
||||||
|
var startComment = ""
|
||||||
|
|
||||||
|
if (generateRegionComments) {
|
||||||
|
startComment = "region "
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateRegionComments || generateFilePaths) {
|
||||||
|
val originalPath = it.path
|
||||||
|
val path = pathPrefixMap.entries
|
||||||
|
.find { (k, _) -> originalPath.startsWith(k) }
|
||||||
|
?.let { (k, v) -> v + originalPath.substring(k.length) }
|
||||||
|
?: originalPath
|
||||||
|
|
||||||
|
startComment += "file: $path"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startComment.isNotEmpty()) {
|
||||||
|
statements.add(JsSingleLineComment(startComment))
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.addAll(fileStatements)
|
||||||
|
statements.endRegion()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
backendContext.testFunsPerFile[file]?.let {
|
// sort member forwarding code
|
||||||
result.testFunInvocation = JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt()
|
processClassModels(staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||||
result.suiteFn = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner)
|
|
||||||
|
statements.addWithComment("block: post-declaration", postDeclarationBlock.statements)
|
||||||
|
statements.addWithComment("block: init", staticContext.initializerBlock.statements)
|
||||||
|
|
||||||
|
modules.forEach { module ->
|
||||||
|
val tests = module.files
|
||||||
|
.groupBy({ it.fqName.asString()}) { backendContext.testFunsPerFile[it] }
|
||||||
|
.mapNotNull{ (fqn, testFuns) -> testFuns.filterNotNull().let { if (it.isEmpty()) null else fqn to it } }
|
||||||
|
.associate { it }
|
||||||
|
|
||||||
|
if (tests.isNotEmpty()) {
|
||||||
|
val testFunBody = JsBlock()
|
||||||
|
val testFun = JsFunction(emptyScope, testFunBody, "root test fun")
|
||||||
|
val suiteFunRef = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner).makeRef()
|
||||||
|
|
||||||
|
for ((pkg, testFuns) in tests) {
|
||||||
|
val pkgTestFun = JsFunction(emptyScope, JsBlock(), "test fun for $pkg")
|
||||||
|
pkgTestFun.body.statements += testFuns.map {
|
||||||
|
JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt()
|
||||||
|
}
|
||||||
|
testFun.body.statements +=
|
||||||
|
JsInvocation(suiteFunRef, JsStringLiteral(pkg), JsBooleanLiteral(false), pkgTestFun).makeStmt()
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.startRegion("block: tests")
|
||||||
|
statements += JsInvocation(testFun).makeStmt()
|
||||||
|
statements.endRegion()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return statements
|
||||||
|
|
||||||
result.importedModules += nameGenerator.importedModules
|
|
||||||
|
|
||||||
fun computeTag(declaration: IrDeclaration): String {
|
|
||||||
// TODO proper tags
|
|
||||||
return System.identityHashCode(declaration).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
nameGenerator.nameMap.entries.forEach { (declaration, name) ->
|
|
||||||
val tag = computeTag(declaration)
|
|
||||||
result.nameBindings[tag] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
nameGenerator.imports.entries.forEach { (declaration, importExpression) ->
|
|
||||||
val tag = computeTag(declaration)
|
|
||||||
result.imports[tag] = importExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateMainArguments(
|
private fun generateMainArguments(
|
||||||
@@ -263,6 +379,18 @@ class IrModuleToJsTransformer(
|
|||||||
return listOfNotNull(mainArgumentsArray, continuation)
|
return listOfNotNull(mainArgumentsArray, continuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateCallToMain(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
|
||||||
|
// TODO: Generate calls to main as IR->IR lowering
|
||||||
|
if (mainArguments == null) return emptyList() // in case `NO_MAIN` and `main(..)` exists
|
||||||
|
val mainFunction = JsMainFunctionDetector(backendContext).getMainFunctionOrNull(modules.last())
|
||||||
|
return mainFunction?.let {
|
||||||
|
val jsName = staticContext.getNameForStaticFunction(it)
|
||||||
|
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
||||||
|
val generateContinuation = it.isLoweredSuspendFunction(backendContext)
|
||||||
|
listOf(JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, staticContext)).makeStmt())
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateImportStatements(
|
private fun generateImportStatements(
|
||||||
getNameForExternalDeclaration: (IrDeclarationWithName) -> JsName,
|
getNameForExternalDeclaration: (IrDeclarationWithName) -> JsName,
|
||||||
declareFreshGlobal: (String) -> JsName
|
declareFreshGlobal: (String) -> JsName
|
||||||
@@ -313,29 +441,53 @@ class IrModuleToJsTransformer(
|
|||||||
val importedJsModules = (declarationLevelJsModules + packageLevelJsModules).distinctBy { it.key }
|
val importedJsModules = (declarationLevelJsModules + packageLevelJsModules).distinctBy { it.key }
|
||||||
return Pair(importStatements, importedJsModules)
|
return Pair(importStatements, importedJsModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun MutableList<JsStatement>.startRegion(description: String = "") {
|
||||||
|
if (generateRegionComments) {
|
||||||
|
this += JsSingleLineComment("region $description")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<JsStatement>.endRegion() {
|
||||||
|
if (generateRegionComments) {
|
||||||
|
this += JsSingleLineComment("endregion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<JsStatement>.addWithComment(regionDescription: String = "", block: JsBlock) {
|
||||||
|
startRegion(regionDescription)
|
||||||
|
this += block
|
||||||
|
endRegion()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<JsStatement>.addWithComment(regionDescription: String = "", statements: List<JsStatement>) {
|
||||||
|
if (statements.isEmpty()) return
|
||||||
|
|
||||||
|
startRegion(regionDescription)
|
||||||
|
this += statements
|
||||||
|
endRegion()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateWrappedModuleBody(
|
fun processClassModels(
|
||||||
moduleName: String,
|
classModelMap: Map<IrClassSymbol, JsIrClassModel>,
|
||||||
moduleKind: ModuleKind,
|
preDeclarationBlock: JsBlock,
|
||||||
fragments: List<List<JsIrProgramFragment>>
|
postDeclarationBlock: JsBlock
|
||||||
): CompilationOutputs {
|
) {
|
||||||
val program = Merger(
|
val declarationHandler = object : DFS.AbstractNodeHandler<IrClassSymbol, Unit>() {
|
||||||
moduleName,
|
override fun result() {}
|
||||||
moduleKind,
|
override fun afterChildren(current: IrClassSymbol) {
|
||||||
fragments,
|
classModelMap[current]?.let {
|
||||||
false,
|
preDeclarationBlock.statements += it.preDeclarationBlock.statements
|
||||||
true
|
postDeclarationBlock.statements += it.postDeclarationBlock.statements
|
||||||
).merge()
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
program.resolveTemporaryNames()
|
DFS.dfs(
|
||||||
|
classModelMap.keys,
|
||||||
val jsCode = TextOutputImpl()
|
{ klass -> classModelMap[klass]?.superClasses ?: emptyList() },
|
||||||
|
declarationHandler
|
||||||
program.accept(JsToStringGenerationVisitor(jsCode, NoOpSourceLocationConsumer))
|
|
||||||
|
|
||||||
return CompilationOutputs(
|
|
||||||
jsCode.toString(),
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+253
@@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* 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.config.CommonConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.export.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstSerializer
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
|
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
|
||||||
|
import org.jetbrains.kotlin.js.backend.NoOpSourceLocationConsumer
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.js.util.TextOutputImpl
|
||||||
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
|
class IrModuleToJsTransformerTmp(
|
||||||
|
private val backendContext: JsIrBackendContext,
|
||||||
|
private val mainArguments: List<String>?,
|
||||||
|
private val generateScriptModule: Boolean = false,
|
||||||
|
var namer: NameTables = NameTables(emptyList(), context = backendContext),
|
||||||
|
private val fullJs: Boolean = true,
|
||||||
|
private val dceJs: Boolean = false,
|
||||||
|
private val multiModule: Boolean = false,
|
||||||
|
private val relativeRequirePath: Boolean = false,
|
||||||
|
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||||
|
private val removeUnusedAssociatedObjects: Boolean = true,
|
||||||
|
) {
|
||||||
|
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
||||||
|
|
||||||
|
private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||||
|
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||||
|
|
||||||
|
fun generateModule(modules: Iterable<IrModuleFragment>): CompilerResult {
|
||||||
|
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||||
|
|
||||||
|
val exportData = modules.associate { module ->
|
||||||
|
module to module.files.associate { file ->
|
||||||
|
file to exportModelGenerator.generateExport(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dts = wrapTypeScript(mainModuleName, moduleKind, exportData.values.flatMap { it.values.flatMap { it } }.toTypeScript(moduleKind))
|
||||||
|
|
||||||
|
modules.forEach { module ->
|
||||||
|
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsCode = if (fullJs) generateWrappedModuleBody(mainModuleName, moduleKind, generateProgramFragments(modules, exportData)) else null
|
||||||
|
|
||||||
|
val dceJsCode = if (dceJs) {
|
||||||
|
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
|
||||||
|
|
||||||
|
generateWrappedModuleBody(mainModuleName, moduleKind, generateProgramFragments(modules, exportData))
|
||||||
|
} else null
|
||||||
|
|
||||||
|
return CompilerResult(jsCode, dceJsCode, dts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateBinaryAst(files: Iterable<IrFile>): Map<String, ByteArray> {
|
||||||
|
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||||
|
|
||||||
|
val exportData = files.associate { file ->
|
||||||
|
file to exportModelGenerator.generateExport(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||||
|
|
||||||
|
val serializer = JsIrAstSerializer() { "TODO" }
|
||||||
|
|
||||||
|
val result = mutableMapOf<String, ByteArray>()
|
||||||
|
files.forEach { f ->
|
||||||
|
val exports = exportData[f]!! // TODO
|
||||||
|
val fragment = generateProgramFragment(f, exports)
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
serializer.serialize(fragment, output)
|
||||||
|
val binaryAst = output.toByteArray()
|
||||||
|
result[f.fileEntry.name] = binaryAst
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateProgramFragments(
|
||||||
|
modules: Iterable<IrModuleFragment>,
|
||||||
|
exportData: Map<IrModuleFragment, Map<IrFile, List<ExportedDeclaration>>>,
|
||||||
|
): List<List<JsIrProgramFragment>> {
|
||||||
|
|
||||||
|
val fragments = mutableMapOf<IrFile, JsIrProgramFragment>()
|
||||||
|
modules.forEach { m ->
|
||||||
|
m.files.forEach { f ->
|
||||||
|
val exports = exportData[m]!![f]!! // TODO
|
||||||
|
fragments[f] = generateProgramFragment(f, exports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modules.map { it.files.map { fragments[it]!! } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||||
|
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||||
|
|
||||||
|
private fun generateProgramFragment(file: IrFile, exports: List<ExportedDeclaration>): JsIrProgramFragment {
|
||||||
|
val nameGenerator = JsNameLinkingNamer(backendContext)
|
||||||
|
|
||||||
|
val staticContext = JsStaticContext(
|
||||||
|
backendContext = backendContext,
|
||||||
|
irNamer = nameGenerator,
|
||||||
|
globalNameScope = namer.globalNames
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = JsIrProgramFragment(file.fqName.asString())
|
||||||
|
|
||||||
|
val internalModuleName = JsName("_", false)
|
||||||
|
val globalNames = NameTable<String>(namer.globalNames)
|
||||||
|
val exportStatements =
|
||||||
|
ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport(
|
||||||
|
ExportedModule(mainModuleName, moduleKind, exports),
|
||||||
|
internalModuleName,
|
||||||
|
)
|
||||||
|
|
||||||
|
result.exports.statements += exportStatements
|
||||||
|
|
||||||
|
if (exports.isNotEmpty()) {
|
||||||
|
result.dts = exports.toTypeScript(moduleKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
val statements = result.declarations.statements
|
||||||
|
|
||||||
|
val fileStatements = file.accept(IrFileToJsTransformer(), staticContext).statements
|
||||||
|
if (fileStatements.isNotEmpty()) {
|
||||||
|
var startComment = ""
|
||||||
|
|
||||||
|
if (generateRegionComments) {
|
||||||
|
startComment = "region "
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateRegionComments || generateFilePaths) {
|
||||||
|
val originalPath = file.path
|
||||||
|
val path = pathPrefixMap.entries
|
||||||
|
.find { (k, _) -> originalPath.startsWith(k) }
|
||||||
|
?.let { (k, v) -> v + originalPath.substring(k.length) }
|
||||||
|
?: originalPath
|
||||||
|
|
||||||
|
startComment += "file: $path"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startComment.isNotEmpty()) {
|
||||||
|
statements.add(JsSingleLineComment(startComment))
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.addAll(fileStatements)
|
||||||
|
if (generateRegionComments) {
|
||||||
|
statements += JsSingleLineComment("endregion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
staticContext.classModels.entries.forEach { (symbol, model) ->
|
||||||
|
result.classes[nameGenerator.getNameForClass(symbol.owner)] =
|
||||||
|
JsIrIcClassModel(model.klass.superTypes.map { staticContext.getNameForClass((it.classifierOrFail as IrClassSymbol).owner) })
|
||||||
|
}
|
||||||
|
|
||||||
|
result.initializers.statements += staticContext.initializerBlock.statements
|
||||||
|
|
||||||
|
if (mainArguments != null) {
|
||||||
|
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(file)?.let {
|
||||||
|
val jsName = staticContext.getNameForStaticFunction(it)
|
||||||
|
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
||||||
|
val generateContinuation = it.isLoweredSuspendFunction(backendContext)
|
||||||
|
result.mainFunction = JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, staticContext)).makeStmt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backendContext.testFunsPerFile[file]?.let {
|
||||||
|
result.testFunInvocation = JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt()
|
||||||
|
result.suiteFn = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.importedModules += nameGenerator.importedModules
|
||||||
|
|
||||||
|
fun computeTag(declaration: IrDeclaration): String {
|
||||||
|
// TODO proper tags
|
||||||
|
return System.identityHashCode(declaration).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
nameGenerator.nameMap.entries.forEach { (declaration, name) ->
|
||||||
|
val tag = computeTag(declaration)
|
||||||
|
result.nameBindings[tag] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
nameGenerator.imports.entries.forEach { (declaration, importExpression) ->
|
||||||
|
val tag = computeTag(declaration)
|
||||||
|
result.imports[tag] = importExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMainArguments(
|
||||||
|
generateArgv: Boolean,
|
||||||
|
generateContinuation: Boolean,
|
||||||
|
staticContext: JsStaticContext,
|
||||||
|
): List<JsExpression> {
|
||||||
|
val mainArguments = this.mainArguments!!
|
||||||
|
val mainArgumentsArray =
|
||||||
|
if (generateArgv) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
|
||||||
|
|
||||||
|
val continuation = if (generateContinuation) {
|
||||||
|
backendContext.coroutineEmptyContinuation.owner
|
||||||
|
.let { it.getter!! }
|
||||||
|
.let { staticContext.getNameForStaticFunction(it) }
|
||||||
|
.let { JsInvocation(it.makeRef()) }
|
||||||
|
} else null
|
||||||
|
|
||||||
|
return listOfNotNull(mainArgumentsArray, continuation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateWrappedModuleBody(
|
||||||
|
moduleName: String,
|
||||||
|
moduleKind: ModuleKind,
|
||||||
|
fragments: List<List<JsIrProgramFragment>>
|
||||||
|
): CompilationOutputs {
|
||||||
|
val program = Merger(
|
||||||
|
moduleName,
|
||||||
|
moduleKind,
|
||||||
|
fragments,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
).merge()
|
||||||
|
|
||||||
|
program.resolveTemporaryNames()
|
||||||
|
|
||||||
|
val jsCode = TextOutputImpl()
|
||||||
|
|
||||||
|
program.accept(JsToStringGenerationVisitor(jsCode, NoOpSourceLocationConsumer))
|
||||||
|
|
||||||
|
return CompilationOutputs(
|
||||||
|
jsCode.toString(),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
+9
-2
@@ -32,7 +32,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
}
|
}
|
||||||
private val classPrototypeRef = prototypeOf(classNameRef)
|
private val classPrototypeRef = prototypeOf(classNameRef)
|
||||||
private val classBlock = JsGlobalBlock()
|
private val classBlock = JsGlobalBlock()
|
||||||
private val classModel = JsIrClassModel(irClass.superTypes.map { context.getNameForClass((it.classifierOrFail as IrClassSymbol).owner) })
|
private val classModel = JsIrClassModel(irClass)
|
||||||
|
|
||||||
private val es6mode = context.staticContext.backendContext.es6mode
|
private val es6mode = context.staticContext.backendContext.es6mode
|
||||||
|
|
||||||
@@ -364,7 +364,14 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true
|
private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true
|
||||||
private val IrClassifierSymbol.isEffectivelyExternal get() = (owner as? IrDeclaration)?.isEffectivelyExternal() == true
|
private val IrClassifierSymbol.isEffectivelyExternal get() = (owner as? IrDeclaration)?.isEffectivelyExternal() == true
|
||||||
|
|
||||||
class JsIrClassModel(val superClasses: List<JsName>) {
|
class JsIrClassModel(val klass: IrClass) {
|
||||||
|
val superClasses = klass.superTypes.map { it.classifierOrFail as IrClassSymbol }
|
||||||
|
|
||||||
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsIrIcClassModel(val superClasses: List<JsName>) {
|
||||||
val preDeclarationBlock = JsGlobalBlock()
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
val postDeclarationBlock = JsGlobalBlock()
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
}
|
}
|
||||||
+5
-5
@@ -19,7 +19,7 @@ class JsIrProgramFragment(val packageFqn: String) {
|
|||||||
val importedModules = mutableListOf<JsImportedModule>()
|
val importedModules = mutableListOf<JsImportedModule>()
|
||||||
val imports = mutableMapOf<String, JsExpression>()
|
val imports = mutableMapOf<String, JsExpression>()
|
||||||
var dts: String? = null
|
var dts: String? = null
|
||||||
val classes = mutableMapOf<JsName, JsIrClassModel>()
|
val classes = mutableMapOf<JsName, JsIrIcClassModel>()
|
||||||
val initializers = JsGlobalBlock()
|
val initializers = JsGlobalBlock()
|
||||||
var mainFunction: JsStatement? = null
|
var mainFunction: JsStatement? = null
|
||||||
var testFunInvocation: JsStatement? = null
|
var testFunInvocation: JsStatement? = null
|
||||||
@@ -50,10 +50,10 @@ class Merger(
|
|||||||
importStatements.putIfAbsent(declaration, JsVars(JsVars.JsVar(importName, rename(importExpression))))
|
importStatements.putIfAbsent(declaration, JsVars(JsVars.JsVar(importName, rename(importExpression))))
|
||||||
}
|
}
|
||||||
|
|
||||||
val classModels = mutableMapOf<JsName, JsIrClassModel>() + f.classes
|
val classModels = mutableMapOf<JsName, JsIrIcClassModel>() + f.classes
|
||||||
f.classes.clear()
|
f.classes.clear()
|
||||||
classModels.entries.forEach { (name, model) ->
|
classModels.entries.forEach { (name, model) ->
|
||||||
f.classes[rename(name)] = JsIrClassModel(model.superClasses.map { rename(it) }).also {
|
f.classes[rename(name)] = JsIrIcClassModel(model.superClasses.map { rename(it) }).also {
|
||||||
it.preDeclarationBlock.statements += model.preDeclarationBlock.statements
|
it.preDeclarationBlock.statements += model.preDeclarationBlock.statements
|
||||||
it.postDeclarationBlock.statements += model.postDeclarationBlock.statements
|
it.postDeclarationBlock.statements += model.postDeclarationBlock.statements
|
||||||
rename(it.preDeclarationBlock)
|
rename(it.preDeclarationBlock)
|
||||||
@@ -115,7 +115,7 @@ class Merger(
|
|||||||
|
|
||||||
moduleBody.addWithComment("block: pre-declaration", preDeclarationBlock)
|
moduleBody.addWithComment("block: pre-declaration", preDeclarationBlock)
|
||||||
|
|
||||||
val classModels = mutableMapOf<JsName, JsIrClassModel>()
|
val classModels = mutableMapOf<JsName, JsIrIcClassModel>()
|
||||||
val initializerBlock = JsGlobalBlock()
|
val initializerBlock = JsGlobalBlock()
|
||||||
fragments.forEach {
|
fragments.forEach {
|
||||||
it.forEach {
|
it.forEach {
|
||||||
@@ -197,7 +197,7 @@ class Merger(
|
|||||||
|
|
||||||
|
|
||||||
private fun processClassModels(
|
private fun processClassModels(
|
||||||
classModelMap: Map<JsName, JsIrClassModel>,
|
classModelMap: Map<JsName, JsIrIcClassModel>,
|
||||||
preDeclarationBlock: JsBlock,
|
preDeclarationBlock: JsBlock,
|
||||||
postDeclarationBlock: JsBlock
|
postDeclarationBlock: JsBlock
|
||||||
) {
|
) {
|
||||||
|
|||||||
+3
-2
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils.serialization
|
package org.jetbrains.kotlin.ir.backend.js.utils.serialization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
@@ -83,8 +84,8 @@ class JsIrAstDeserializer : JsAstDeserializerBase() {
|
|||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserialize(proto: IrClassModel): Pair<JsName, JsIrClassModel> {
|
private fun deserialize(proto: IrClassModel): Pair<JsName, JsIrIcClassModel> {
|
||||||
return deserializeName(proto.nameId) to JsIrClassModel(proto.superClassesList.map { deserializeName(it) }).apply {
|
return deserializeName(proto.nameId) to JsIrIcClassModel(proto.superClassesList.map { deserializeName(it) }).apply {
|
||||||
if (proto.hasPreDeclarationBlock()) {
|
if (proto.hasPreDeclarationBlock()) {
|
||||||
preDeclarationBlock.statements += deserializeGlobalBlock(proto.preDeclarationBlock).statements
|
preDeclarationBlock.statements += deserializeGlobalBlock(proto.preDeclarationBlock).statements
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils.serialization
|
package org.jetbrains.kotlin.ir.backend.js.utils.serialization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
|
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
|
||||||
@@ -88,7 +89,7 @@ class JsIrAstSerializer: JsAstSerializerBase() {
|
|||||||
return fragmentBuilder.build()
|
return fragmentBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serialize(name: JsName, classModel: JsIrClassModel): IrClassModel {
|
private fun serialize(name: JsName, classModel: JsIrIcClassModel): IrClassModel {
|
||||||
val builder = IrClassModel.newBuilder()
|
val builder = IrClassModel.newBuilder()
|
||||||
builder.nameId = serialize(name)
|
builder.nameId = serialize(name)
|
||||||
classModel.superClasses.forEach { builder.addSuperClasses(serialize(it)) }
|
classModel.superClasses.forEach { builder.addSuperClasses(serialize(it)) }
|
||||||
|
|||||||
Reference in New Issue
Block a user