[JS IR] handle main function per-file

This commit is contained in:
Anton Bannykh
2021-09-15 17:32:54 +03:00
committed by teamcityserver
parent 1af1d13cf3
commit 9f2762cfa6
3 changed files with 30 additions and 23 deletions
@@ -150,7 +150,7 @@ class IrModuleToJsTransformer(
declareFreshGlobal = { JsName(sanitizeName(it)) } // TODO: Declare fresh name
)
val moduleBody = generateModuleBody(modules, staticContext)
val (moduleBody, callToMain) = generateModuleBody(modules, staticContext)
val internalModuleName = JsName("_")
val globalNames = NameTable<String>(namer.globalNames)
@@ -175,8 +175,8 @@ class IrModuleToJsTransformer(
statements.addWithComment("block: imports", importStatements + crossModuleImports)
statements += moduleBody
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
if (generateMainCall) {
statements += generateCallToMain(modules, staticContext)
if (generateMainCall && callToMain != null) {
statements += callToMain
}
statements += JsReturn(internalModuleName.makeRef())
}
@@ -282,10 +282,12 @@ class IrModuleToJsTransformer(
}
}
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): List<JsStatement> {
val fragments = modules.flatMap { it.files.map { generateProgramFragment(it, staticContext) } }
private data class ModuleBody(val statements: List<JsStatement>, val mainFunction: JsStatement?)
val statements = merge(fragments, staticContext).toMutableList()
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, staticContext: JsStaticContext): ModuleBody {
val fragments = modules.map { it.files.map { generateProgramFragment(it, staticContext) } }
val statements = merge(fragments.flatMap { it }, staticContext).toMutableList()
// TODO: handle tests incrementally
modules.forEach {
@@ -296,7 +298,9 @@ class IrModuleToJsTransformer(
}
}
return statements
val callToMain = fragments.last().sortedBy { it.packageFqn }.firstNotNullOfOrNull { it.mainFunction }
return ModuleBody(statements, callToMain)
}
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
@@ -306,7 +310,7 @@ class IrModuleToJsTransformer(
require(staticContext.classModels.isEmpty())
require(staticContext.initializerBlock.statements.isEmpty())
val result = JsIrProgramFragment()
val result = JsIrProgramFragment(file.fqName.asString())
val statements = result.declarations.statements
val fileStatements = file.accept(IrFileToJsTransformer(), staticContext).statements
@@ -338,6 +342,15 @@ class IrModuleToJsTransformer(
result.classes += staticContext.classModels
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()
}
}
staticContext.classModels.clear()
staticContext.initializerBlock.statements.clear()
@@ -390,18 +403,6 @@ class IrModuleToJsTransformer(
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(
getNameForExternalDeclaration: (IrDeclarationWithName) -> JsName,
declareFreshGlobal: (String) -> JsName
@@ -7,9 +7,11 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
import org.jetbrains.kotlin.js.backend.ast.JsStatement
class JsIrProgramFragment {
class JsIrProgramFragment(val packageFqn: String) {
val declarations = JsGlobalBlock()
val classes = mutableMapOf<IrClassSymbol, JsIrClassModel>()
val initializers = JsGlobalBlock()
var mainFunction: JsStatement? = null
}
@@ -46,14 +46,18 @@ class JsMainFunctionDetector(val context: JsCommonBackendContext) {
}
}
fun getMainFunctionOrNull(file: IrFile): IrSimpleFunction? {
// TODO: singleOrNull looks suspicious
return file.declarations.filterIsInstance<IrSimpleFunction>().singleOrNull { it.isMain(allowEmptyParameters = true) }
}
fun getMainFunctionOrNull(module: IrModuleFragment): IrSimpleFunction? {
var resultPair: Pair<String, IrSimpleFunction>? = null
module.files.forEach { f ->
val fqn = f.fqName.asString()
f.declarations.filterIsInstance<IrSimpleFunction>().singleOrNull { it.isMain(allowEmptyParameters = true) }?.let {
getMainFunctionOrNull(f)?.let {
val result = resultPair
if (result == null) {
resultPair = Pair(fqn, it)