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.*
|
import java.util.*
|
||||||
|
|
||||||
fun eliminateDeadDeclarations(
|
fun eliminateDeadDeclarations(
|
||||||
module: IrModuleFragment,
|
modules: Iterable<IrModuleFragment>,
|
||||||
context: JsIrBackendContext,
|
context: JsIrBackendContext,
|
||||||
mainFunction: IrSimpleFunction?
|
mainFunction: IrSimpleFunction?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val allRoots = stageController.withInitialIr { buildRoots(module, context, mainFunction) }
|
val allRoots = stageController.withInitialIr { buildRoots(modules, context, mainFunction) }
|
||||||
|
|
||||||
val usefulDeclarations = usefulDeclarations(allRoots, context)
|
val usefulDeclarations = usefulDeclarations(allRoots, context)
|
||||||
|
|
||||||
stageController.unrestrictDeclarationListsAccess {
|
stageController.unrestrictDeclarationListsAccess {
|
||||||
removeUselessDeclarations(module, usefulDeclarations)
|
removeUselessDeclarations(modules, usefulDeclarations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ private fun IrField.isConstant(): Boolean {
|
|||||||
return correspondingPropertySymbol?.owner?.isConst ?: false
|
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 =
|
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) }
|
file.declarations.flatMap { if (it is IrProperty) listOfNotNull(it.backingField, it.getter, it.setter) else listOf(it) }
|
||||||
.filter {
|
.filter {
|
||||||
it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true
|
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
|
return rootDeclarations
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeUselessDeclarations(module: IrModuleFragment, usefulDeclarations: Set<IrDeclaration>) {
|
private fun removeUselessDeclarations(modules: Iterable<IrModuleFragment>, usefulDeclarations: Set<IrDeclaration>) {
|
||||||
module.files.forEach {
|
modules.forEach { module ->
|
||||||
it.acceptVoid(object : IrElementVisitorVoid {
|
module.files.forEach {
|
||||||
override fun visitElement(element: IrElement) {
|
it.acceptVoid(object : IrElementVisitorVoid {
|
||||||
element.acceptChildrenVoid(this)
|
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
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitClass(declaration: IrClass) {
|
override fun visitFile(declaration: IrFile) {
|
||||||
process(declaration)
|
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 IrConstructorCall.shouldKeepAnnotation(): Boolean {
|
||||||
|
associatedObject()?.let { obj ->
|
||||||
|
if (obj !in usefulDeclarations) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun process(container: IrDeclarationContainer) {
|
override fun visitClass(declaration: IrClass) {
|
||||||
container.declarations.transformFlat { member ->
|
process(declaration)
|
||||||
if (member !in usefulDeclarations) {
|
// Remove annotations for `findAssociatedObject` feature, which reference objects eliminated by the DCE.
|
||||||
emptyList()
|
// Otherwise `JsClassGenerator.generateAssociatedKeyProperties` will try to reference the object factory (which is removed).
|
||||||
} else {
|
// That will result in an error from the Namer. It cannot generate a name for an absent declaration.
|
||||||
member.acceptVoid(this)
|
declaration.annotations = declaration.annotations.filter { it.shouldKeepAnnotation() }
|
||||||
null
|
}
|
||||||
|
|
||||||
|
// 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?
|
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
|
||||||
val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase
|
val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase
|
||||||
|
|
||||||
// TODO `fold` -> `reduce`
|
|
||||||
val jsPhases = SameTypeNamedPhaseWrapper(
|
val jsPhases = SameTypeNamedPhaseWrapper(
|
||||||
name = "IrModuleLowering",
|
name = "IrModuleLowering",
|
||||||
description = "IR module lowering",
|
description = "IR module lowering",
|
||||||
|
|||||||
@@ -67,28 +67,27 @@ fun compile(
|
|||||||
is MainModule.Klib -> dependencyModules
|
is MainModule.Klib -> dependencyModules
|
||||||
}
|
}
|
||||||
|
|
||||||
val irFiles = allModules.flatMap { it.files }
|
|
||||||
|
|
||||||
deserializer.postProcess()
|
deserializer.postProcess()
|
||||||
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
|
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
|
||||||
|
|
||||||
moduleFragment.files.clear()
|
allModules.forEach { module ->
|
||||||
moduleFragment.files += irFiles
|
moveBodilessDeclarationsToSeparatePlace(context, module)
|
||||||
|
}
|
||||||
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
|
|
||||||
|
|
||||||
if (dceDriven) {
|
if (dceDriven) {
|
||||||
|
|
||||||
// TODO we should only generate tests for the current module
|
// TODO we should only generate tests for the current module
|
||||||
// TODO should be done incrementally
|
// TODO should be done incrementally
|
||||||
generateTests(context, moduleFragment)
|
allModules.forEach { module ->
|
||||||
|
generateTests(context, module)
|
||||||
|
}
|
||||||
|
|
||||||
val controller = MutableController(context, pirLowerings)
|
val controller = MutableController(context, pirLowerings)
|
||||||
stageController = controller
|
stageController = controller
|
||||||
|
|
||||||
controller.currentStage = controller.lowerings.size + 1
|
controller.currentStage = controller.lowerings.size + 1
|
||||||
|
|
||||||
eliminateDeadDeclarations(moduleFragment, context, mainFunction)
|
eliminateDeadDeclarations(allModules, context, mainFunction)
|
||||||
|
|
||||||
// TODO investigate whether this is needed anymore
|
// TODO investigate whether this is needed anymore
|
||||||
stageController = object : StageController {
|
stageController = object : StageController {
|
||||||
@@ -96,11 +95,11 @@ fun compile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
||||||
return transformer.generateModule(moduleFragment, fullJs = true, dceJs = false)
|
return transformer.generateModule(allModules, fullJs = true, dceJs = false)
|
||||||
} else {
|
} else {
|
||||||
jsPhases.invokeToplevel(phaseConfig, context, listOf(moduleFragment))
|
jsPhases.invokeToplevel(phaseConfig, context, allModules)
|
||||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
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))
|
jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, listOf(moduleFragment))
|
||||||
|
|
||||||
val transformer = IrModuleToJsTransformer(context, null, null, true, nameTables)
|
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(
|
ExportedModule(
|
||||||
sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!),
|
sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!),
|
||||||
(context.externalPackageFragment.values + module.files).flatMap {
|
(context.externalPackageFragment.values + modules.flatMap { it.files }).flatMap {
|
||||||
generateExport(it)
|
generateExport(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
+40
-34
@@ -35,7 +35,7 @@ class IrModuleToJsTransformer(
|
|||||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||||
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
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) {
|
val additionalPackages = with(backendContext) {
|
||||||
externalPackageFragment.values + listOf(
|
externalPackageFragment.values + listOf(
|
||||||
bodilessBuiltInsPackageFragment,
|
bodilessBuiltInsPackageFragment,
|
||||||
@@ -43,28 +43,32 @@ class IrModuleToJsTransformer(
|
|||||||
) + packageLevelJsModules
|
) + packageLevelJsModules
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportedModule = ExportModelGenerator(backendContext).generateExport(module)
|
val exportedModule = ExportModelGenerator(backendContext).generateExport(modules)
|
||||||
val dts = exportedModule.toTypeScript()
|
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) {
|
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
|
// 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.
|
// 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())
|
val namer = NameTables(emptyList())
|
||||||
namer.merge(module.files, additionalPackages)
|
namer.merge(modules.flatMap { it.files }, additionalPackages)
|
||||||
generateWrappedModuleBody(module, exportedModule, namer)
|
generateWrappedModuleBody(modules, exportedModule, namer)
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
return CompilerResult(jsCode, dceJsCode, dts)
|
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 program = JsProgram()
|
||||||
|
|
||||||
val nameGenerator = IrNamerImpl(
|
val nameGenerator = IrNamerImpl(
|
||||||
@@ -88,7 +92,7 @@ class IrModuleToJsTransformer(
|
|||||||
declareFreshGlobal = { JsName(sanitizeName(it)) } // TODO: Declare fresh name
|
declareFreshGlobal = { JsName(sanitizeName(it)) } // TODO: Declare fresh name
|
||||||
)
|
)
|
||||||
|
|
||||||
val moduleBody = generateModuleBody(module, rootContext)
|
val moduleBody = generateModuleBody(modules, rootContext)
|
||||||
val exportStatements = ExportModelToJsStatements(internalModuleName, namer)
|
val exportStatements = ExportModelToJsStatements(internalModuleName, namer)
|
||||||
.generateModuleExport(exportedModule)
|
.generateModuleExport(exportedModule)
|
||||||
|
|
||||||
@@ -123,7 +127,7 @@ class IrModuleToJsTransformer(
|
|||||||
return program.toString()
|
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 {
|
val statements = mutableListOf<JsStatement>().also {
|
||||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
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 generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||||
val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||||
|
|
||||||
module.files.forEach {
|
modules.forEach { module ->
|
||||||
val fileStatements = it.accept(IrFileToJsTransformer(), context).statements
|
module.files.forEach {
|
||||||
if (fileStatements.isNotEmpty()) {
|
val fileStatements = it.accept(IrFileToJsTransformer(), context).statements
|
||||||
var startComment = ""
|
if (fileStatements.isNotEmpty()) {
|
||||||
|
var startComment = ""
|
||||||
|
|
||||||
if (generateRegionComments) {
|
if (generateRegionComments) {
|
||||||
startComment = "region "
|
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))
|
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 {
|
fun compile(files: List<KtFile>): String {
|
||||||
|
|||||||
Reference in New Issue
Block a user