Don't merge files into a single module

This commit is contained in:
Anton Bannykh
2020-06-03 16:15:15 +03:00
parent 5239ab477c
commit deb5dc1057
6 changed files with 95 additions and 89 deletions
@@ -25,17 +25,17 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
fun eliminateDeadDeclarations(
module: IrModuleFragment,
modules: Iterable<IrModuleFragment>,
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<IrDeclaration> {
private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable<IrDeclaration> {
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<IrDeclaration>) {
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<IrModuleFragment>, usefulDeclarations: Set<IrDeclaration>) {
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
}
}
}
}
})
})
}
}
}
@@ -739,7 +739,6 @@ val loweringList = listOf<Lowering>(
// 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",
@@ -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!!
}
@@ -36,10 +36,10 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
}
}
fun generateExport(module: IrModuleFragment): ExportedModule =
fun generateExport(modules: Iterable<IrModuleFragment>): ExportedModule =
ExportedModule(
sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!),
(context.externalPackageFragment.values + module.files).flatMap {
(context.externalPackageFragment.values + modules.flatMap { it.files }).flatMap {
generateExport(it)
}
)
@@ -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<IrModuleFragment>, 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<IrModuleFragment>, 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<JsStatement> {
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, context: JsGenerationContext): List<JsStatement> {
val statements = mutableListOf<JsStatement>().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()
}
}
@@ -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<KtFile>): String {