[JS IR] Memory consumption optimization
- Remove IR after JS AST generation - Remove JS AST after/during JS code generation
This commit is contained in:
committed by
Space
parent
14b7db0187
commit
453faeaa45
@@ -43,10 +43,7 @@ import org.jetbrains.kotlin.backend.wasm.dce.eliminateDeadDeclarations
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.SourceMapsInfo
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
||||
@@ -55,7 +52,6 @@ import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
@@ -79,6 +75,78 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
return K2JSCompilerArguments()
|
||||
}
|
||||
|
||||
private data class TransformResult(val out: CompilationOutputs, val dts: String)
|
||||
|
||||
private class Ir2JsTransformer(
|
||||
val arguments: K2JSCompilerArguments,
|
||||
val module: ModulesStructure,
|
||||
val phaseConfig: PhaseConfig,
|
||||
val messageCollector: MessageCollector,
|
||||
val mainCallArguments: List<String>?
|
||||
) {
|
||||
private fun lowerIr(): LoweredIr {
|
||||
val granularity = when {
|
||||
arguments.irPerModule -> JsGenerationGranularity.PER_MODULE
|
||||
arguments.irPerFile -> JsGenerationGranularity.PER_FILE
|
||||
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||
}
|
||||
|
||||
val irFactory = when {
|
||||
arguments.irNewIr2Js -> IrFactoryImplForJsIC(WholeWorldStageController())
|
||||
else -> IrFactoryImpl
|
||||
}
|
||||
|
||||
return compile(
|
||||
module,
|
||||
phaseConfig,
|
||||
irFactory,
|
||||
dceRuntimeDiagnostic = RuntimeDiagnostic.resolve(
|
||||
arguments.irDceRuntimeDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
baseClassIntoMetadata = arguments.irBaseClassInMetadata,
|
||||
safeExternalBoolean = arguments.irSafeExternalBoolean,
|
||||
safeExternalBooleanDiagnostic = RuntimeDiagnostic.resolve(
|
||||
arguments.irSafeExternalBooleanDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
granularity = granularity,
|
||||
icCompatibleIr2Js = arguments.irNewIr2Js,
|
||||
)
|
||||
}
|
||||
|
||||
private fun makeJsCodeGeneratorAndDts(): Pair<JsCodeGenerator, String> {
|
||||
val ir = lowerIr()
|
||||
val transformer = IrModuleToJsTransformerTmp(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName)
|
||||
|
||||
val mode = TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule, arguments.irMinimizedMemberNames)
|
||||
return transformer.makeJsCodeGeneratorAndDts(ir.allModules, mode)
|
||||
}
|
||||
|
||||
fun compileAndTransformIrNew(): TransformResult {
|
||||
val (generator, dts) = makeJsCodeGeneratorAndDts()
|
||||
val out = generator.generateJsCode(relativeRequirePath = true, outJsProgram = false)
|
||||
return TransformResult(out, dts)
|
||||
}
|
||||
|
||||
fun compileAndTransformIrOld(): TransformResult {
|
||||
val ir = lowerIr()
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
ir.context,
|
||||
mainCallArguments,
|
||||
fullJs = !arguments.irDce,
|
||||
dceJs = arguments.irDce,
|
||||
multiModule = arguments.irPerModule,
|
||||
relativeRequirePath = true,
|
||||
moduleToName = ir.moduleFragmentToUniqueName
|
||||
)
|
||||
|
||||
val result = transformer.generateModule(ir.allModules)
|
||||
return TransformResult(result.outputs.values.single(), result.tsDefinitions ?: error("No ts definitions"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun doExecute(
|
||||
arguments: K2JSCompilerArguments,
|
||||
configuration: CompilerConfiguration,
|
||||
@@ -301,6 +369,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
|
||||
val outputs = jsExecutableProducer.buildExecutable(
|
||||
multiModule = arguments.irPerModule,
|
||||
outJsProgram = false,
|
||||
rebuildCallback = { rebuiltModule ->
|
||||
messageCollector.report(INFO, "IC module builder rebuilt module [${File(rebuiltModule).name}]")
|
||||
}
|
||||
@@ -368,72 +437,23 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
|
||||
val granularity = when {
|
||||
arguments.irPerModule -> JsGenerationGranularity.PER_MODULE
|
||||
arguments.irPerFile -> JsGenerationGranularity.PER_FILE
|
||||
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||
}
|
||||
try {
|
||||
val irFactory = when {
|
||||
arguments.irNewIr2Js -> IrFactoryImplForJsIC(WholeWorldStageController())
|
||||
else -> IrFactoryImpl
|
||||
}
|
||||
|
||||
val ir = compile(
|
||||
module,
|
||||
phaseConfig,
|
||||
irFactory,
|
||||
dceRuntimeDiagnostic = RuntimeDiagnostic.resolve(
|
||||
arguments.irDceRuntimeDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
baseClassIntoMetadata = arguments.irBaseClassInMetadata,
|
||||
safeExternalBoolean = arguments.irSafeExternalBoolean,
|
||||
safeExternalBooleanDiagnostic = RuntimeDiagnostic.resolve(
|
||||
arguments.irSafeExternalBooleanDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
granularity = granularity,
|
||||
icCompatibleIr2Js = arguments.irNewIr2Js,
|
||||
)
|
||||
|
||||
val compiledModule: CompilerResult = if (arguments.irNewIr2Js) {
|
||||
val transformer = IrModuleToJsTransformerTmp(
|
||||
ir.context,
|
||||
mainCallArguments,
|
||||
relativeRequirePath = true,
|
||||
moduleToName = ir.moduleFragmentToUniqueName
|
||||
)
|
||||
|
||||
transformer.generateModule(
|
||||
ir.allModules,
|
||||
setOf(TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule, arguments.irMinimizedMemberNames))
|
||||
)
|
||||
val (outputs, tsDefinitions) = if (arguments.irNewIr2Js) {
|
||||
Ir2JsTransformer(arguments, module, phaseConfig, messageCollector, mainCallArguments).compileAndTransformIrNew()
|
||||
} else {
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
ir.context,
|
||||
mainCallArguments,
|
||||
fullJs = !arguments.irDce,
|
||||
dceJs = arguments.irDce,
|
||||
multiModule = arguments.irPerModule,
|
||||
relativeRequirePath = true,
|
||||
moduleToName = ir.moduleFragmentToUniqueName
|
||||
)
|
||||
|
||||
transformer.generateModule(ir.allModules)
|
||||
Ir2JsTransformer(arguments, module, phaseConfig, messageCollector, mainCallArguments).compileAndTransformIrOld()
|
||||
}
|
||||
|
||||
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
|
||||
|
||||
val outputs = compiledModule.outputs.values.single()
|
||||
|
||||
outputFile.write(outputs)
|
||||
outputs.dependencies.forEach { (name, content) ->
|
||||
outputFile.resolveSibling("$name.js").write(content)
|
||||
}
|
||||
if (arguments.generateDts) {
|
||||
val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!!
|
||||
dtsFile.writeText(compiledModule.tsDefinitions ?: error("No ts definitions"))
|
||||
dtsFile.writeText(tsDefinitions)
|
||||
}
|
||||
} catch (e: CompilationException) {
|
||||
messageCollector.report(
|
||||
|
||||
@@ -38,7 +38,6 @@ fun compileWithIC(
|
||||
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
|
||||
es6mode: Boolean = false,
|
||||
multiModule: Boolean = false,
|
||||
relativeRequirePath: Boolean = false,
|
||||
verifySignatures: Boolean = true,
|
||||
baseClassIntoMetadata: Boolean = false,
|
||||
lowerPerModule: Boolean = false,
|
||||
@@ -79,12 +78,7 @@ fun compileWithIC(
|
||||
|
||||
lowerPreservingTags(allModules, context, PhaseConfig(jsPhases), symbolTable.irFactory.stageController as WholeWorldStageController)
|
||||
|
||||
val transformer = IrModuleToJsTransformerTmp(
|
||||
context,
|
||||
mainArguments,
|
||||
relativeRequirePath = relativeRequirePath,
|
||||
)
|
||||
|
||||
val transformer = IrModuleToJsTransformerTmp(context, mainArguments)
|
||||
return transformer.generateBinaryAst(filesToLower, allModules)
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -16,27 +16,28 @@ class JsExecutableProducer(
|
||||
private val caches: List<ModuleArtifact>,
|
||||
private val relativeRequirePath: Boolean
|
||||
) {
|
||||
fun buildExecutable(multiModule: Boolean, rebuildCallback: (String) -> Unit = {}) = if (multiModule) {
|
||||
buildMultiModuleExecutable(rebuildCallback)
|
||||
fun buildExecutable(multiModule: Boolean, outJsProgram: Boolean, rebuildCallback: (String) -> Unit = {}) = if (multiModule) {
|
||||
buildMultiModuleExecutable(outJsProgram, rebuildCallback)
|
||||
} else {
|
||||
buildSingleModuleExecutable(rebuildCallback)
|
||||
buildSingleModuleExecutable(outJsProgram, rebuildCallback)
|
||||
}
|
||||
|
||||
private fun buildSingleModuleExecutable(rebuildCallback: (String) -> Unit): CompilationOutputs {
|
||||
val program = JsIrProgram(caches.map { cacheArtifact -> cacheArtifact.loadJsIrModule() })
|
||||
private fun buildSingleModuleExecutable(outJsProgram: Boolean, rebuildCallback: (String) -> Unit): CompilationOutputs {
|
||||
val modules = caches.map { cacheArtifact -> cacheArtifact.loadJsIrModule() }
|
||||
val out = generateSingleWrappedModuleBody(
|
||||
moduleName = mainModuleName,
|
||||
moduleKind = moduleKind,
|
||||
fragments = program.modules.flatMap { it.fragments },
|
||||
fragments = modules.flatMap { it.fragments },
|
||||
sourceMapsInfo = sourceMapsInfo,
|
||||
generateScriptModule = false,
|
||||
generateCallToMain = true
|
||||
generateCallToMain = true,
|
||||
outJsProgram = outJsProgram
|
||||
)
|
||||
rebuildCallback(mainModuleName)
|
||||
return out
|
||||
}
|
||||
|
||||
private fun buildMultiModuleExecutable(rebuildCallback: (String) -> Unit): CompilationOutputs {
|
||||
private fun buildMultiModuleExecutable(outJsProgram: Boolean, rebuildCallback: (String) -> Unit): CompilationOutputs {
|
||||
val jsMultiModuleCache = JsMultiModuleCache(caches)
|
||||
val cachedProgram = jsMultiModuleCache.loadProgramHeadersFromCache()
|
||||
|
||||
@@ -64,7 +65,8 @@ class JsExecutableProducer(
|
||||
sourceMapsInfo = sourceMapsInfo,
|
||||
generateScriptModule = false,
|
||||
generateCallToMain = generateCallToMain,
|
||||
crossModuleReferences = crossRef
|
||||
crossModuleReferences = crossRef,
|
||||
outJsProgram = outJsProgram
|
||||
)
|
||||
jsMultiModuleCache.commitCompiledJsCode(artifact, compiledModule)
|
||||
rebuildCallback(moduleName)
|
||||
|
||||
+110
-72
@@ -64,11 +64,30 @@ enum class TranslationMode(
|
||||
|
||||
class JsIrFragmentAndBinaryAst(val irFile: IrFile, val fragment: JsIrProgramFragment, val binaryAst: ByteArray)
|
||||
|
||||
class JsCodeGenerator(
|
||||
private val program: JsIrProgram,
|
||||
private val multiModule: Boolean,
|
||||
private val mainModuleName: String,
|
||||
private val moduleKind: ModuleKind,
|
||||
private val sourceMapsInfo: SourceMapsInfo?
|
||||
) {
|
||||
fun generateJsCode(relativeRequirePath: Boolean, outJsProgram: Boolean): CompilationOutputs {
|
||||
return generateWrappedModuleBody(
|
||||
multiModule,
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
program,
|
||||
sourceMapsInfo,
|
||||
relativeRequirePath,
|
||||
false,
|
||||
outJsProgram
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class IrModuleToJsTransformerTmp(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val mainArguments: List<String>?,
|
||||
private val generateScriptModule: Boolean = false,
|
||||
private val relativeRequirePath: Boolean = false,
|
||||
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||
private val removeUnusedAssociatedObjects: Boolean = true,
|
||||
) {
|
||||
@@ -76,40 +95,40 @@ class IrModuleToJsTransformerTmp(
|
||||
|
||||
private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
|
||||
|
||||
fun generateModule(modules: Iterable<IrModuleFragment>, modes: Set<TranslationMode>): CompilerResult {
|
||||
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<Pair<IrFile, List<ExportedDeclaration>>>)
|
||||
|
||||
private fun List<IrAndExportedDeclarations>.flatExportedDeclarations(): List<ExportedDeclaration> {
|
||||
return this.flatMap { data -> data.files.flatMap { it.second } }
|
||||
}
|
||||
|
||||
private fun associateIrAndExport(modules: Iterable<IrModuleFragment>): List<IrAndExportedDeclarations> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
|
||||
val exportData = modules.associate { module ->
|
||||
module to module.files.associate { file ->
|
||||
return modules.map { module ->
|
||||
val files = module.files.map { file ->
|
||||
file to exportModelGenerator.generateExportWithExternals(file)
|
||||
}
|
||||
IrAndExportedDeclarations(module, files)
|
||||
}
|
||||
}
|
||||
|
||||
val dts = ExportedModule(mainModuleName, moduleKind, exportData.values.flatMap { it.values.flatten() }).toTypeScript()
|
||||
|
||||
private fun doStaticMembersLowering(modules: Iterable<IrModuleFragment>) {
|
||||
modules.forEach { module ->
|
||||
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun compilationOutput(multiModule: Boolean, minimizedMemberNames: Boolean) = generateWrappedModuleBody(
|
||||
multiModule,
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
generateProgramFragments(modules, exportData, minimizedMemberNames),
|
||||
SourceMapsInfo.from(backendContext.configuration),
|
||||
relativeRequirePath,
|
||||
generateScriptModule,
|
||||
)
|
||||
fun generateModule(modules: Iterable<IrModuleFragment>, modes: Set<TranslationMode>, relativeRequirePath: Boolean): CompilerResult {
|
||||
val exportData = associateIrAndExport(modules)
|
||||
val dts = ExportedModule(mainModuleName, moduleKind, exportData.flatExportedDeclarations()).toTypeScript()
|
||||
doStaticMembersLowering(modules)
|
||||
|
||||
val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java)
|
||||
|
||||
modes.filter { !it.dce }.forEach {
|
||||
if (it.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
result[it] = compilationOutput(it.perModule, it.minimizedMemberNames)
|
||||
result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true)
|
||||
}
|
||||
|
||||
if (modes.any { it.dce }) {
|
||||
@@ -117,26 +136,30 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
modes.filter { it.dce }.forEach {
|
||||
if (it.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
result[it] = compilationOutput(it.perModule, it.minimizedMemberNames)
|
||||
result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true)
|
||||
}
|
||||
|
||||
return CompilerResult(result, dts)
|
||||
}
|
||||
|
||||
fun makeJsCodeGeneratorAndDts(modules: Iterable<IrModuleFragment>, mode: TranslationMode): Pair<JsCodeGenerator, String> {
|
||||
val exportData = associateIrAndExport(modules)
|
||||
val dts = ExportedModule(mainModuleName, moduleKind, exportData.flatExportedDeclarations()).toTypeScript()
|
||||
doStaticMembersLowering(modules)
|
||||
|
||||
if (mode.dce) {
|
||||
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
|
||||
}
|
||||
|
||||
return makeJsCodeGeneratorFromIr(exportData, mode) to dts
|
||||
}
|
||||
|
||||
fun generateBinaryAst(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<JsIrFragmentAndBinaryAst> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
|
||||
val exportData = files.map { it to exportModelGenerator.generateExportWithExternals(it) }
|
||||
|
||||
allModules.forEach {
|
||||
it.files.forEach {
|
||||
StaticMembersLowering(backendContext).lower(it)
|
||||
}
|
||||
}
|
||||
doStaticMembersLowering(allModules)
|
||||
|
||||
val serializer = JsIrAstSerializer()
|
||||
return exportData.map { (file, exports) ->
|
||||
@@ -158,29 +181,35 @@ class IrModuleToJsTransformerTmp(
|
||||
return moduleToName[this] ?: sanitizeName(safeName)
|
||||
}
|
||||
|
||||
private fun generateProgramFragments(
|
||||
modules: Iterable<IrModuleFragment>,
|
||||
exportData: Map<IrModuleFragment, Map<IrFile, List<ExportedDeclaration>>>,
|
||||
minimizedMemberNames: Boolean
|
||||
): JsIrProgram {
|
||||
return JsIrProgram(
|
||||
modules.map { m ->
|
||||
private fun makeJsCodeGeneratorFromIr(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsCodeGenerator {
|
||||
if (mode.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
|
||||
val program = JsIrProgram(
|
||||
exportData.map { data ->
|
||||
JsIrModule(
|
||||
m.safeName,
|
||||
m.externalModuleName(),
|
||||
m.files.map {
|
||||
val exports = exportData[m]!![it]!!
|
||||
generateProgramFragment(it, exports, minimizedMemberNames)
|
||||
},
|
||||
data.fragment.safeName,
|
||||
data.fragment.externalModuleName(),
|
||||
data.files.map { (file, exports) ->
|
||||
generateProgramFragment(file, exports, mode.minimizedMemberNames)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
return JsCodeGenerator(program, mode.perModule, mainModuleName, moduleKind, sourceMapInfo)
|
||||
}
|
||||
|
||||
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||
|
||||
private fun generateProgramFragment(file: IrFile, exports: List<ExportedDeclaration>, minimizedMemberNames: Boolean): JsIrProgramFragment {
|
||||
private fun generateProgramFragment(
|
||||
file: IrFile,
|
||||
exports: List<ExportedDeclaration>,
|
||||
minimizedMemberNames: Boolean
|
||||
): JsIrProgramFragment {
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames)
|
||||
|
||||
val globalNameScope = NameTable<IrDeclaration>()
|
||||
@@ -332,48 +361,56 @@ private fun generateWrappedModuleBody(
|
||||
program: JsIrProgram,
|
||||
sourceMapsInfo: SourceMapsInfo?,
|
||||
relativeRequirePath: Boolean,
|
||||
generateScriptModule: Boolean
|
||||
generateScriptModule: Boolean,
|
||||
outJsProgram: Boolean
|
||||
): CompilationOutputs {
|
||||
if (multiModule) {
|
||||
|
||||
val moduleToRef = program.crossModuleDependencies(relativeRequirePath)
|
||||
|
||||
val main = program.mainModule
|
||||
val others = program.otherModules
|
||||
|
||||
val mainModule = generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
main.fragments,
|
||||
sourceMapsInfo,
|
||||
generateScriptModule,
|
||||
generateCallToMain = true,
|
||||
moduleToRef[main]!!,
|
||||
)
|
||||
|
||||
val dependencies = others.map { module ->
|
||||
val moduleName = module.externalModuleName
|
||||
|
||||
moduleName to generateSingleWrappedModuleBody(
|
||||
moduleName,
|
||||
// mutable container allows explicitly remove elements from itself,
|
||||
// so we are able to help GC to free heavy JsIrModule objects
|
||||
// TODO: It makes sense to invent something better, because this logic can be easily broken
|
||||
val moduleToRef = program.asCrossModuleDependencies(relativeRequirePath).toMutableList()
|
||||
val mainModule = moduleToRef.removeLast().let { (main, mainRef) ->
|
||||
generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
module.fragments,
|
||||
main.fragments,
|
||||
sourceMapsInfo,
|
||||
generateScriptModule,
|
||||
generateCallToMain = false,
|
||||
moduleToRef[module]!!,
|
||||
generateCallToMain = true,
|
||||
mainRef,
|
||||
outJsProgram
|
||||
)
|
||||
}
|
||||
|
||||
val dependencies = buildList(moduleToRef.size) {
|
||||
while (moduleToRef.isNotEmpty()) {
|
||||
moduleToRef.removeFirst().let { (module, moduleRef) ->
|
||||
val moduleName = module.externalModuleName
|
||||
val moduleCompilationOutput = generateSingleWrappedModuleBody(
|
||||
moduleName,
|
||||
moduleKind,
|
||||
module.fragments,
|
||||
sourceMapsInfo,
|
||||
generateScriptModule,
|
||||
generateCallToMain = false,
|
||||
moduleRef,
|
||||
outJsProgram
|
||||
)
|
||||
add(moduleName to moduleCompilationOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CompilationOutputs(mainModule.jsCode, mainModule.jsProgram, mainModule.sourceMap, dependencies)
|
||||
} else {
|
||||
return generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
program.modules.flatMap { it.fragments },
|
||||
program.asFragments(),
|
||||
sourceMapsInfo,
|
||||
generateScriptModule,
|
||||
generateCallToMain = true,
|
||||
outJsProgram = outJsProgram
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -386,6 +423,7 @@ fun generateSingleWrappedModuleBody(
|
||||
generateScriptModule: Boolean,
|
||||
generateCallToMain: Boolean,
|
||||
crossModuleReferences: CrossModuleReferences = CrossModuleReferences.Empty,
|
||||
outJsProgram: Boolean = true
|
||||
): CompilationOutputs {
|
||||
val program = Merger(
|
||||
moduleName,
|
||||
@@ -428,7 +466,7 @@ fun generateSingleWrappedModuleBody(
|
||||
|
||||
return CompilationOutputs(
|
||||
jsCode.toString(),
|
||||
program,
|
||||
program.takeIf { outJsProgram },
|
||||
sourceMapBuilder?.build()
|
||||
)
|
||||
}
|
||||
|
||||
+10
-6
@@ -55,19 +55,23 @@ class JsIrModuleHeader(
|
||||
val externalNames: Set<String> by lazy { nameBindings.keys - definitions }
|
||||
}
|
||||
|
||||
class JsIrProgram(val modules: List<JsIrModule>) {
|
||||
val mainModule = modules.last()
|
||||
val otherModules = modules.dropLast(1)
|
||||
|
||||
fun crossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModule, CrossModuleReferences> {
|
||||
class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
fun asCrossModuleDependencies(relativeRequirePath: Boolean): List<Pair<JsIrModule, CrossModuleReferences>> {
|
||||
val resolver = CrossModuleDependenciesResolver(modules.map { it.makeModuleHeader() })
|
||||
modules = emptyList()
|
||||
val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
|
||||
return crossModuleReferences.entries.associate {
|
||||
return crossModuleReferences.entries.map {
|
||||
val module = it.key.associatedModule ?: error("Internal error: module ${it.key.moduleName} is not loaded")
|
||||
it.value.initJsImportsForModule(module)
|
||||
module to it.value
|
||||
}
|
||||
}
|
||||
|
||||
fun asFragments(): List<JsIrProgramFragment> {
|
||||
val fragments = modules.flatMap { it.fragments }
|
||||
modules = emptyList()
|
||||
return fragments
|
||||
}
|
||||
}
|
||||
|
||||
class CrossModuleDependenciesResolver(private val headers: List<JsIrModuleHeader>) {
|
||||
|
||||
@@ -258,7 +258,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
)
|
||||
|
||||
val rebuiltModules = mutableSetOf<String>()
|
||||
val jsOutput = jsExecutableProducer.buildExecutable(true) { rebuiltModules += it }
|
||||
val jsOutput = jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true) { rebuiltModules += it }
|
||||
verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS)
|
||||
verifyJsCode(projStep.id, testInfo.last().moduleName, jsOutput)
|
||||
}
|
||||
|
||||
@@ -70,13 +70,9 @@ abstract class AbstractJsKLibABITestCase : AbstractKlibABITestCase() {
|
||||
icCompatibleIr2Js = true
|
||||
)
|
||||
|
||||
val transformer = IrModuleToJsTransformerTmp(
|
||||
ir.context,
|
||||
emptyList(),
|
||||
relativeRequirePath = false
|
||||
)
|
||||
val transformer = IrModuleToJsTransformerTmp(ir.context, emptyList())
|
||||
|
||||
val compiledResult = transformer.generateModule(ir.allModules, setOf(TranslationMode.FULL_DCE_MINIMIZED_NAMES))
|
||||
val compiledResult = transformer.generateModule(ir.allModules, setOf(TranslationMode.FULL_DCE_MINIMIZED_NAMES), false)
|
||||
|
||||
val dceOutput = compiledResult.outputs[TranslationMode.FULL_DCE_MINIMIZED_NAMES] ?: error("No DCE output")
|
||||
|
||||
@@ -122,4 +118,4 @@ abstract class AbstractJsKLibABITestCase : AbstractKlibABITestCase() {
|
||||
companion object {
|
||||
private const val BIN_DIR_NAME = "_bins_js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ class JsIrBackendFacade(
|
||||
caches = testServices.jsIrIncrementalDataProvider.getCaches(),
|
||||
relativeRequirePath = false
|
||||
)
|
||||
jsExecutableProducer.buildExecutable(it.perModule)
|
||||
jsExecutableProducer.buildExecutable(it.perModule, true)
|
||||
},
|
||||
tsDefinitions = null
|
||||
)
|
||||
@@ -173,11 +173,7 @@ class JsIrBackendFacade(
|
||||
val dceOutputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL_DCE_MINIMIZED_NAMES) + ".js")
|
||||
if (!esModules) {
|
||||
if (runNewIr2Js) {
|
||||
val transformer = IrModuleToJsTransformerTmp(
|
||||
loweredIr.context,
|
||||
mainArguments,
|
||||
relativeRequirePath = false
|
||||
)
|
||||
val transformer = IrModuleToJsTransformerTmp(loweredIr.context, mainArguments)
|
||||
|
||||
// If runIrDce then include DCE results
|
||||
// If perModuleOnly then skip whole program
|
||||
@@ -186,7 +182,8 @@ class JsIrBackendFacade(
|
||||
.filter { (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) }
|
||||
.filter { it.dce == it.minimizedMemberNames }
|
||||
.toSet()
|
||||
return BinaryArtifacts.Js.JsIrArtifact(outputFile, transformer.generateModule(loweredIr.allModules, translationModes)).dump(module)
|
||||
val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, false)
|
||||
return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module)
|
||||
} else {
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
loweredIr.context,
|
||||
|
||||
Reference in New Issue
Block a user