diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt index 33ddef7d381..3e8f29180e8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt @@ -25,17 +25,17 @@ import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* fun eliminateDeadDeclarations( - module: IrModuleFragment, + modules: Iterable, context: JsIrBackendContext, mainFunction: IrSimpleFunction? ) { - val allRoots = stageController.withInitialIr { buildRoots(module, context, mainFunction) } + val allRoots = stageController.withInitialIr { buildRoots(modules, context, mainFunction) } val usefulDeclarations = usefulDeclarations(allRoots, context) stageController.unrestrictDeclarationListsAccess { - removeUselessDeclarations(module, usefulDeclarations) + removeUselessDeclarations(modules, usefulDeclarations) } } @@ -43,9 +43,9 @@ private fun IrField.isConstant(): Boolean { return correspondingPropertySymbol?.owner?.isConst ?: false } -private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable { +private fun buildRoots(modules: Iterable, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable { val rootDeclarations = - (module.files + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file -> + (modules.flatMap { it.files } + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file -> file.declarations.flatMap { if (it is IrProperty) listOfNotNull(it.backingField, it.getter, it.setter) else listOf(it) } .filter { it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true @@ -68,45 +68,47 @@ private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, ma return rootDeclarations } -private fun removeUselessDeclarations(module: IrModuleFragment, usefulDeclarations: Set) { - module.files.forEach { - it.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFile(declaration: IrFile) { - process(declaration) - } - - private fun IrConstructorCall.shouldKeepAnnotation(): Boolean { - associatedObject()?.let { obj -> - if (obj !in usefulDeclarations) return false +private fun removeUselessDeclarations(modules: Iterable, usefulDeclarations: Set) { + modules.forEach { module -> + module.files.forEach { + it.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) } - return true - } - override fun visitClass(declaration: IrClass) { - process(declaration) - // Remove annotations for `findAssociatedObject` feature, which reference objects eliminated by the DCE. - // Otherwise `JsClassGenerator.generateAssociatedKeyProperties` will try to reference the object factory (which is removed). - // That will result in an error from the Namer. It cannot generate a name for an absent declaration. - declaration.annotations = declaration.annotations.filter { it.shouldKeepAnnotation() } - } + override fun visitFile(declaration: IrFile) { + process(declaration) + } - // TODO bring back the primary constructor fix + private fun IrConstructorCall.shouldKeepAnnotation(): Boolean { + associatedObject()?.let { obj -> + if (obj !in usefulDeclarations) return false + } + return true + } - private fun process(container: IrDeclarationContainer) { - container.declarations.transformFlat { member -> - if (member !in usefulDeclarations) { - emptyList() - } else { - member.acceptVoid(this) - null + override fun visitClass(declaration: IrClass) { + process(declaration) + // Remove annotations for `findAssociatedObject` feature, which reference objects eliminated by the DCE. + // Otherwise `JsClassGenerator.generateAssociatedKeyProperties` will try to reference the object factory (which is removed). + // That will result in an error from the Namer. It cannot generate a name for an absent declaration. + declaration.annotations = declaration.annotations.filter { it.shouldKeepAnnotation() } + } + + // TODO bring back the primary constructor fix + + private fun process(container: IrDeclarationContainer) { + container.declarations.transformFlat { member -> + if (member !in usefulDeclarations) { + emptyList() + } else { + member.acceptVoid(this) + null + } } } - } - }) + }) + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index f8b56aef7fc..0b463dd44f0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -739,7 +739,6 @@ val loweringList = listOf( // TODO comment? Eliminate ModuleLowering's? Don't filter them here? val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase -// TODO `fold` -> `reduce` val jsPhases = SameTypeNamedPhaseWrapper( name = "IrModuleLowering", description = "IR module lowering", diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 77fcd765f48..6d24d4642cb 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -67,28 +67,27 @@ fun compile( is MainModule.Klib -> dependencyModules } - val irFiles = allModules.flatMap { it.files } - deserializer.postProcess() symbolTable.noUnboundLeft("Unbound symbols at the end of linker") - moduleFragment.files.clear() - moduleFragment.files += irFiles - - moveBodilessDeclarationsToSeparatePlace(context, moduleFragment) + allModules.forEach { module -> + moveBodilessDeclarationsToSeparatePlace(context, module) + } if (dceDriven) { // TODO we should only generate tests for the current module // TODO should be done incrementally - generateTests(context, moduleFragment) + allModules.forEach { module -> + generateTests(context, module) + } val controller = MutableController(context, pirLowerings) stageController = controller controller.currentStage = controller.lowerings.size + 1 - eliminateDeadDeclarations(moduleFragment, context, mainFunction) + eliminateDeadDeclarations(allModules, context, mainFunction) // TODO investigate whether this is needed anymore stageController = object : StageController { @@ -96,11 +95,11 @@ fun compile( } val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) - return transformer.generateModule(moduleFragment, fullJs = true, dceJs = false) + return transformer.generateModule(allModules, fullJs = true, dceJs = false) } else { - jsPhases.invokeToplevel(phaseConfig, context, listOf(moduleFragment)) + jsPhases.invokeToplevel(phaseConfig, context, allModules) val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) - return transformer.generateModule(moduleFragment, generateFullJs, generateDceJs) + return transformer.generateModule(allModules, generateFullJs, generateDceJs) } } @@ -113,5 +112,5 @@ fun generateJsCode( jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, listOf(moduleFragment)) val transformer = IrModuleToJsTransformer(context, null, null, true, nameTables) - return transformer.generateModule(moduleFragment).jsCode!! + return transformer.generateModule(listOf(moduleFragment)).jsCode!! } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt index 4167469b139..4d54c83bb1b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt @@ -36,10 +36,10 @@ class ExportModelGenerator(val context: JsIrBackendContext) { } } - fun generateExport(module: IrModuleFragment): ExportedModule = + fun generateExport(modules: Iterable): ExportedModule = ExportedModule( sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!), - (context.externalPackageFragment.values + module.files).flatMap { + (context.externalPackageFragment.values + modules.flatMap { it.files }).flatMap { generateExport(it) } ) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index b3f647b6439..7b530daeac0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -35,7 +35,7 @@ class IrModuleToJsTransformer( private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!! private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS) - fun generateModule(module: IrModuleFragment, fullJs: Boolean = true, dceJs: Boolean = false): CompilerResult { + fun generateModule(modules: Iterable, fullJs: Boolean = true, dceJs: Boolean = false): CompilerResult { val additionalPackages = with(backendContext) { externalPackageFragment.values + listOf( bodilessBuiltInsPackageFragment, @@ -43,28 +43,32 @@ class IrModuleToJsTransformer( ) + packageLevelJsModules } - val exportedModule = ExportModelGenerator(backendContext).generateExport(module) + val exportedModule = ExportModelGenerator(backendContext).generateExport(modules) val dts = exportedModule.toTypeScript() - module.files.forEach { StaticMembersLowering(backendContext).lower(it) } + modules.forEach { module -> + module.files.forEach { StaticMembersLowering(backendContext).lower(it) } + } - namer.merge(module.files, additionalPackages) + modules.forEach { module -> + namer.merge(module.files, additionalPackages) + } - val jsCode = if (fullJs) generateWrappedModuleBody(module, exportedModule, namer) else null + val jsCode = if (fullJs) generateWrappedModuleBody(modules, exportedModule, namer) else null val dceJsCode = if (dceJs) { - eliminateDeadDeclarations(module, backendContext, mainFunction) + eliminateDeadDeclarations(modules, backendContext, mainFunction) // Use a fresh namer for DCE so that we could compare the result with DCE-driven // 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()) - namer.merge(module.files, additionalPackages) - generateWrappedModuleBody(module, exportedModule, namer) + namer.merge(modules.flatMap { it.files }, additionalPackages) + generateWrappedModuleBody(modules, exportedModule, namer) } else null return CompilerResult(jsCode, dceJsCode, dts) } - private fun generateWrappedModuleBody(module: IrModuleFragment, exportedModule: ExportedModule, namer: NameTables): String { + private fun generateWrappedModuleBody(modules: Iterable, exportedModule: ExportedModule, namer: NameTables): String { val program = JsProgram() val nameGenerator = IrNamerImpl( @@ -88,7 +92,7 @@ class IrModuleToJsTransformer( declareFreshGlobal = { JsName(sanitizeName(it)) } // TODO: Declare fresh name ) - val moduleBody = generateModuleBody(module, rootContext) + val moduleBody = generateModuleBody(modules, rootContext) val exportStatements = ExportModelToJsStatements(internalModuleName, namer) .generateModuleExport(exportedModule) @@ -123,7 +127,7 @@ class IrModuleToJsTransformer( return program.toString() } - private fun generateModuleBody(module: IrModuleFragment, context: JsGenerationContext): List { + private fun generateModuleBody(modules: Iterable, context: JsGenerationContext): List { val statements = mutableListOf().also { if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt() } @@ -136,31 +140,33 @@ class IrModuleToJsTransformer( val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH) val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP) - module.files.forEach { - val fileStatements = it.accept(IrFileToJsTransformer(), context).statements - if (fileStatements.isNotEmpty()) { - var startComment = "" + modules.forEach { module -> + module.files.forEach { + val fileStatements = it.accept(IrFileToJsTransformer(), context).statements + if (fileStatements.isNotEmpty()) { + var startComment = "" - if (generateRegionComments) { - startComment = "region " + 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() } - - 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() } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt index a942eb6d42c..73c5e6f8fff 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt @@ -582,9 +582,9 @@ class GenerateIrRuntime { jsPhases.invokeToplevel(phaseConfig, context, listOf(module)) - val transformer = IrModuleToJsTransformer(context, null, null) + val transformer = IrModuleToJsTransformer(context, null) - return transformer.generateModule(module) + return transformer.generateModule(listOf(module)) } fun compile(files: List): String {