Don't merge files into a single module
This commit is contained in:
@@ -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!!
|
||||
}
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
+40
-34
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user