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