diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index 3f8dda64c61..f384ded29d2 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -44,6 +44,7 @@ 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.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory @@ -283,9 +284,11 @@ class K2JsIrCompiler : CLICompiler() { val caches = loadModuleCaches(icCaches) val moduleKind = configurationJs[JSConfigurationKeys.MODULE_KIND]!! - val compiledModule = generateJsFromAst(moduleName, moduleKind, SourceMapsInfo.from(configurationJs), caches) + val translationMode = TranslationMode.fromFlags(false, arguments.irPerModule) - val outputs = compiledModule.outputs!! + val compiledModule = generateJsFromAst(moduleName, moduleKind, SourceMapsInfo.from(configurationJs), setOf(translationMode), caches) + + val outputs = compiledModule.outputs.values.single() outputFile.write(outputs) outputs.dependencies.forEach { (name, content) -> @@ -408,18 +411,15 @@ class K2JsIrCompiler : CLICompiler() { val transformer = IrModuleToJsTransformerTmp( ir.context, mainCallArguments, - fullJs = true, - dceJs = arguments.irDce, - multiModule = arguments.irPerModule, relativeRequirePath = true, ) - transformer.generateModule(ir.allModules) + transformer.generateModule(ir.allModules, setOf(TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule))) } else { val transformer = IrModuleToJsTransformer( ir.context, mainCallArguments, - fullJs = true, + fullJs = !arguments.irDce, dceJs = arguments.irDce, multiModule = arguments.irPerModule, relativeRequirePath = true, @@ -430,11 +430,7 @@ class K2JsIrCompiler : CLICompiler() { messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms") - - val outputs = if (arguments.irDce && !arguments.irDceDriven) - compiledModule.outputsAfterDce!! - else - compiledModule.outputs!! + val outputs = compiledModule.outputs.values.single() outputFile.write(outputs) outputs.dependencies.forEach { (name, content) -> diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index e50e54ab673..b14ad5a5eb0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.ir.backend.js.utils.NameTables import org.jetbrains.kotlin.ir.declarations.IrFactory import org.jetbrains.kotlin.ir.declarations.IrModuleFragment @@ -30,8 +31,7 @@ import org.jetbrains.kotlin.js.config.RuntimeDiagnostic import org.jetbrains.kotlin.name.FqName class CompilerResult( - val outputs: CompilationOutputs?, - val outputsAfterDce: CompilationOutputs?, + val outputs: Map, val tsDefinitions: String? = null ) @@ -228,5 +228,5 @@ fun generateJsCode( jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, listOf(moduleFragment)) val transformer = IrModuleToJsTransformer(context, null, true, nameTables) - return transformer.generateModule(listOf(moduleFragment)).outputs!!.jsCode + return transformer.generateModule(listOf(moduleFragment)).outputs[TranslationMode.FULL]!!.jsCode } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt index f05bcc89d0f..63071d0c121 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt @@ -92,9 +92,6 @@ fun compileWithIC( val transformer = IrModuleToJsTransformerTmp( context, mainArguments, - fullJs = generateFullJs, - dceJs = generateDceJs, - multiModule = multiModule, relativeRequirePath = relativeRequirePath, ) @@ -129,24 +126,28 @@ fun generateJsFromAst( mainModuleName: String, moduleKind: ModuleKind, sourceMapsInfo: SourceMapsInfo?, + translationModes: Set, caches: Map, ): CompilerResult { - val deserializer = JsIrAstDeserializer() - val jsIrProgram = JsIrProgram(caches.values.map { - JsIrModule( - it.name.safeModuleName, - sanitizeName(it.name.safeModuleName), - it.asts.values.sortedBy { it.name }.mapNotNull { it.ast?.let { deserializer.deserialize(ByteArrayInputStream(it)) } }) - }) - return CompilerResult( - generateWrappedModuleBody( - multiModule = true, + fun compilationOutput(multiModule: Boolean): CompilationOutputs { + val deserializer = JsIrAstDeserializer() + val jsIrProgram = JsIrProgram(caches.values.map { + JsIrModule( + it.name.safeModuleName, + sanitizeName(it.name.safeModuleName), + it.asts.values.sortedBy { it.name }.mapNotNull { it.ast?.let { deserializer.deserialize(ByteArrayInputStream(it)) } }) + }) + + return generateWrappedModuleBody( + multiModule = multiModule, mainModuleName = mainModuleName, moduleKind = moduleKind, jsIrProgram, sourceMapsInfo = sourceMapsInfo, relativeRequirePath = false, generateScriptModule = false, - ), null - ) + ) + } + + return CompilerResult(translationModes.associate { it to compilationOutput(it.perModule) }, null) } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index f67732cc6e2..d2ac492ce2f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.utils.DFS import java.io.File +import java.util.* class IrModuleToJsTransformer( private val backendContext: JsIrBackendContext, @@ -65,18 +66,20 @@ class IrModuleToJsTransformer( namer.merge(module.files, additionalPackages) } - val jsCode = if (fullJs) generateWrappedModuleBody(modules, exportedModule, namer) else null + val code = EnumMap(TranslationMode::class.java) - val dceJsCode = if (dceJs) { + if (fullJs) code[TranslationMode.fromFlags(false, multiModule)] = generateWrappedModuleBody(modules, exportedModule, namer) + + if (dceJs) { eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects) // 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(), context = backendContext) namer.merge(modules.flatMap { it.files }, additionalPackages) - generateWrappedModuleBody(modules, exportedModule, namer) - } else null + code[TranslationMode.fromFlags(true, multiModule)] = generateWrappedModuleBody(modules, exportedModule, namer) + } - return CompilerResult(jsCode, dceJsCode, dts) + return CompilerResult(code, dts) } private fun generateWrappedModuleBody(modules: Iterable, exportedModule: ExportedModule, namer: NameTables): CompilationOutputs { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt index 7dce4f3fa6d..51968b04a87 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt @@ -30,15 +30,30 @@ import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.serialization.js.ModuleKind import java.io.ByteArrayOutputStream import java.io.File +import java.util.* + +enum class TranslationMode(val dce: Boolean, val perModule: Boolean) { + FULL(dce = false, perModule = false), + FULL_DCE(dce = true, perModule = false), + PER_MODULE(dce = false, perModule = true), + PER_MODULE_DCE(dce = true, perModule = true); + + companion object { + fun fromFlags(dce: Boolean, perModule: Boolean): TranslationMode { + return if (perModule) { + if (dce) PER_MODULE_DCE else PER_MODULE + } else { + if (dce) FULL_DCE else FULL + } + } + } +} class IrModuleToJsTransformerTmp( private val backendContext: JsIrBackendContext, private val mainArguments: List?, private val generateScriptModule: Boolean = false, var namer: NameTables = NameTables(emptyList(), context = backendContext), - private val fullJs: Boolean = true, - private val dceJs: Boolean = false, - private val multiModule: Boolean = false, private val relativeRequirePath: Boolean = false, private val moduleToName: Map = emptyMap(), private val removeUnusedAssociatedObjects: Boolean = true, @@ -48,7 +63,7 @@ class IrModuleToJsTransformerTmp( private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!! private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!! - fun generateModule(modules: Iterable): CompilerResult { + fun generateModule(modules: Iterable, modes: Set): CompilerResult { val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true) val exportData = modules.associate { module -> @@ -63,7 +78,7 @@ class IrModuleToJsTransformerTmp( module.files.forEach { StaticMembersLowering(backendContext).lower(it) } } - val jsCode = if (fullJs) generateWrappedModuleBody( + fun compilationOutput(multiModule: Boolean) = generateWrappedModuleBody( multiModule, mainModuleName, moduleKind, @@ -71,23 +86,23 @@ class IrModuleToJsTransformerTmp( SourceMapsInfo.from(backendContext.configuration), relativeRequirePath, generateScriptModule, - ) else null + ) - val dceJsCode = if (dceJs) { + val result = EnumMap(TranslationMode::class.java) + + modes.filter { !it.dce }.forEach { + result[it] = compilationOutput(it.perModule) + } + + if (modes.any { it.dce }) { eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects) + } - generateWrappedModuleBody( - multiModule, - mainModuleName, - moduleKind, - generateProgramFragments(modules, exportData), - SourceMapsInfo.from(backendContext.configuration), - relativeRequirePath, - generateScriptModule, - ) - } else null + modes.filter { it.dce }.forEach { + result[it] = compilationOutput(it.perModule) + } - return CompilerResult(jsCode, dceJsCode, dts) + return CompilerResult(result, dts) } fun generateBinaryAst(files: Iterable): Map { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt index 612e6f94365..1f5fe997548 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.js.config.* import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.library.KotlinLibrary @@ -52,10 +53,14 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu const val TEST_DATA_DIR_PATH = "js/js.translator/testData" const val OLD_MODULE_SUFFIX = "_old" - private const val OUTPUT_DIR_NAME = "outputDir" - private const val RECOMPILED_OUTPUT_DIR_NAME = "outputDir-recompiled" + private val outputDirByMode = mapOf( + TranslationMode.FULL to "outputDir", + TranslationMode.FULL_DCE to "dceOutputDir", + TranslationMode.PER_MODULE to "perModuleOutputDir", + TranslationMode.PER_MODULE_DCE to "perModuleDceOutputDir" + ) + private const val OUTPUT_KLIB_DIR_NAME = "outputKlibDir" - private const val DCE_OUTPUT_DIR_NAME = "dceOutputDir" private const val MINIFICATION_OUTPUT_DIR_NAME = "minOutputDir" object ExceptionThrowingReporter : JsConfig.Reporter() { @@ -77,38 +82,30 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu return testName + outputFileSuffix } - fun getJsModuleArtifactPath(testServices: TestServices, moduleName: String): String { - return getJsArtifactsOutputDir(testServices).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5" + fun getJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL): String { + return getJsArtifactsOutputDir(testServices, translationMode).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5" } - fun getRecompiledJsModuleArtifactPath(testServices: TestServices, moduleName: String): String { - return getJsArtifactsRecompiledOutputDir(testServices).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5" + fun getRecompiledJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL): String { + return getJsArtifactsRecompiledOutputDir(testServices, translationMode).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5" } fun getJsKlibArtifactPath(testServices: TestServices, moduleName: String): String { return getJsKlibOutputDir(testServices).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) } - fun getDceJsArtifactPath(testServices: TestServices, moduleName: String): String { - return getDceJsArtifactsOutputDir(testServices).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5" + fun getJsArtifactsOutputDir(testServices: TestServices, translationMode: TranslationMode = TranslationMode.FULL): File { + return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(outputDirByMode[translationMode]!!) } - fun getJsArtifactsOutputDir(testServices: TestServices): File { - return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(OUTPUT_DIR_NAME) - } - - fun getJsArtifactsRecompiledOutputDir(testServices: TestServices): File { - return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(RECOMPILED_OUTPUT_DIR_NAME) + fun getJsArtifactsRecompiledOutputDir(testServices: TestServices, translationMode: TranslationMode = TranslationMode.FULL): File { + return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(outputDirByMode[translationMode]!! + "-recompiled") } fun getJsKlibOutputDir(testServices: TestServices): File { return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(OUTPUT_KLIB_DIR_NAME) } - fun getDceJsArtifactsOutputDir(testServices: TestServices): File { - return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(DCE_OUTPUT_DIR_NAME) - } - fun getMinificationJsArtifactsOutputDir(testServices: TestServices): File { return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(MINIFICATION_OUTPUT_DIR_NAME) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsKLibABITestCase.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsKLibABITestCase.kt index 03d7f7ca3e2..d900b11c81e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsKLibABITestCase.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsKLibABITestCase.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp +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.IrFactoryImplForJsIC import org.jetbrains.kotlin.js.config.JSConfigurationKeys @@ -45,15 +46,12 @@ abstract class AbstractJsKLibABITestCase : AbstractKlibABITestCase() { val transformer = IrModuleToJsTransformerTmp( ir.context, emptyList(), - fullJs = true, - dceJs = true, - multiModule = true, relativeRequirePath = false ) - val compiledResult = transformer.generateModule(ir.allModules) + val compiledResult = transformer.generateModule(ir.allModules, setOf(TranslationMode.FULL_DCE)) - val dceOutput = compiledResult.outputsAfterDce ?: error("No DCE output") + val dceOutput = compiledResult.outputs[TranslationMode.FULL_DCE] ?: error("No DCE output") val binariesDir = File(buildDir, BIN_DIR_NAME).also { it.mkdirs() } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/JsIrBackendFacade.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/JsIrBackendFacade.kt index 5be901dd5aa..9cf5fe05f33 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/JsIrBackendFacade.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/JsIrBackendFacade.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc 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.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.SymbolTable @@ -98,10 +98,11 @@ class JsIrBackendFacade( val moduleName = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME) val moduleKind = configuration.get(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) val sourceMapsInfo = SourceMapsInfo.from(configuration) - val compiledModule = generateJsFromAst(moduleName, moduleKind, sourceMapsInfo, testServices.jsIrIncrementalDataProvider.getCaches()) + val translationModes = setOf(TranslationMode.FULL, TranslationMode.PER_MODULE) + val compiledModule = generateJsFromAst(moduleName, moduleKind, sourceMapsInfo, translationModes, testServices.jsIrIncrementalDataProvider.getCaches()) return BinaryArtifacts.Js.JsIrArtifact( outputFile, compiledModule, testServices.jsIrIncrementalDataProvider.getCacheForModule(module) - ).dump(module) + ).dump(module, firstTimeCompilation) } val loweredIr = compileIr( @@ -140,21 +141,23 @@ class JsIrBackendFacade( val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives val runNewIr2Js = JsEnvironmentConfigurationDirectives.RUN_NEW_IR_2_JS in module.directives + val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives - val outputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name) + ".js") - val dceOutputFile = File(JsEnvironmentConfigurator.getDceJsArtifactPath(testServices, module.name) + ".js") + val outputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL) + ".js") + val dceOutputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL_DCE) + ".js") if (!esModules) { if (runNewIr2Js) { val transformer = IrModuleToJsTransformerTmp( loweredIr.context, mainArguments, - fullJs = true, - dceJs = runIrDce, - multiModule = granularity == JsGenerationGranularity.PER_MODULE, relativeRequirePath = false ) - return BinaryArtifacts.Js.JsIrArtifact(outputFile, transformer.generateModule(loweredIr.allModules)).dump(module) + // If runIrDce then include DCE results + // If perModuleOnly then skip whole program + // (it.dce => runIrDce) && (perModuleOnly => it.perModule) + val translationModes = TranslationMode.values().filter { (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) }.toSet() + return BinaryArtifacts.Js.JsIrArtifact(outputFile, transformer.generateModule(loweredIr.allModules, translationModes)).dump(module) } else { val transformer = IrModuleToJsTransformer( loweredIr.context, @@ -257,18 +260,23 @@ class JsIrBackendFacade( } } - private fun BinaryArtifacts.Js.JsIrArtifact.dump(module: TestModule): BinaryArtifacts.Js.JsIrArtifact { + private fun BinaryArtifacts.Js.JsIrArtifact.dump(module: TestModule, firstTimeCompilation: Boolean = true): BinaryArtifacts.Js.JsIrArtifact { val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module) val moduleId = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME) val moduleKind = configuration.get(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) - val outputDceFile = File(JsEnvironmentConfigurator.getDceJsArtifactPath(testServices, module.name) + ".js") val generateDts = JsEnvironmentConfigurationDirectives.GENERATE_DTS in module.directives val dontSkipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE !in module.directives if (dontSkipRegularMode) { - compilerResult.outputs!!.writeTo(outputFile, moduleId, moduleKind) - compilerResult.outputsAfterDce?.writeTo(outputDceFile, moduleId, moduleKind) + for ((mode, output) in compilerResult.outputs.entries) { + val outputFile = if (firstTimeCompilation) { + File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + ".js") + } else { + File(JsEnvironmentConfigurator.getRecompiledJsModuleArtifactPath(testServices, module.name, mode) + ".js") + } + output.writeTo(outputFile, moduleId, moduleKind) + } } if (generateDts) { @@ -328,7 +336,9 @@ class JsIrBackendFacade( } } -fun File.augmentWithModuleName(moduleName: String): File { - check(absolutePath.endsWith("_v5.js")) - return File(absolutePath.removeSuffix("_v5.js") + "-${moduleName}_v5.js") -} \ No newline at end of file +fun String.augmentWithModuleName(moduleName: String): String { + check(endsWith("_v5.js")) + return removeSuffix("_v5.js") + "-${moduleName}_v5.js" +} + +fun File.augmentWithModuleName(moduleName: String): File = File(absolutePath.augmentWithModuleName(moduleName)) \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsArtifactsDumpHandler.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsArtifactsDumpHandler.kt index f440f030cd4..45eeebdf861 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsArtifactsDumpHandler.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsArtifactsDumpHandler.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.js.test.handlers +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.test.backend.handlers.JsBinaryArtifactHandler import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.model.BinaryArtifacts @@ -28,13 +29,19 @@ class JsArtifactsDumpHandler(testServices: TestServices) : JsBinaryArtifactHandl val testGroupOutputDirForCompilation = File(pathToRootOutputDir + "out/" + testGroupOutputDirPrefix) val testGroupOutputDirForMinification = File(pathToRootOutputDir + "out-min/" + testGroupOutputDirPrefix) + val testGroupOutputDirForPerModuleCompilation = File(pathToRootOutputDir + "out-per-module/" + testGroupOutputDirPrefix) + val testGroupOutputDirForPerModuleMinification = File(pathToRootOutputDir + "out-per-module-min/" + testGroupOutputDirPrefix) val outputDir = getOutputDir(originalFile, testGroupOutputDirForCompilation, stopFile) val dceOutputDir = getOutputDir(originalFile, testGroupOutputDirForMinification, stopFile) + val perModuleOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleCompilation, stopFile) + val preModuleDceOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleMinification, stopFile) val minOutputDir = File(dceOutputDir, originalFile.nameWithoutExtension) copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices), outputDir) - copy(JsEnvironmentConfigurator.getDceJsArtifactsOutputDir(testServices), dceOutputDir) + copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_DCE), dceOutputDir) + copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE), perModuleOutputDir) + copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE_DCE), preModuleDceOutputDir) copy(JsEnvironmentConfigurator.getMinificationJsArtifactsOutputDir(testServices), minOutputDir) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsAstHandler.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsAstHandler.kt index 642fe44c9a3..5edfbe9ed9c 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsAstHandler.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsAstHandler.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.js.test.handlers +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.js.backend.ast.JsExpressionStatement import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral import org.jetbrains.kotlin.js.backend.ast.JsProgram @@ -26,7 +27,7 @@ class JsAstHandler(testServices: TestServices) : JsBinaryArtifactHandler(testSer val ktFiles = module.files.filter { it.isKtFile }.map { it.originalContent } val jsProgram = when (val artifact = info.unwrap()) { is BinaryArtifacts.Js.OldJsArtifact -> (artifact.translationResult as TranslationResult.Success).program - is BinaryArtifacts.Js.JsIrArtifact -> artifact.compilerResult.outputs?.jsProgram ?: return + is BinaryArtifacts.Js.JsIrArtifact -> artifact.compilerResult.outputs[TranslationMode.FULL]?.jsProgram ?: return else -> return } processJsProgram(jsProgram, ktFiles, module.targetBackend!!) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsBoxRunner.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsBoxRunner.kt index eb046fbe4b8..4327eb16b58 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsBoxRunner.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsBoxRunner.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.js.test.handlers +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.js.testOld.engines.ExternalTool import org.jetbrains.kotlin.js.test.utils.* import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives @@ -34,22 +35,16 @@ class JsBoxRunner(testServices: TestServices) : AbstractJsArtifactsCollector(tes if (dontRunGeneratedCode) return - val (allJsFiles, dceAllJsFiles) = getAllFilesForRunner(testServices, modulesToArtifact) + val allJsFiles = getAllFilesForRunner(testServices, modulesToArtifact) val withModuleSystem = testWithModuleSystem(testServices) val testModuleName = getTestModuleName(testServices) val testPackage = extractTestPackage(testServices) val dontSkipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE !in globalDirectives - val dontSkipIrIc = JsEnvironmentConfigurationDirectives.SKIP_IR_INCREMENTAL_CHECKS !in globalDirectives - val recompile = testServices.moduleStructure.modules - .flatMap { it.files }.any { JsEnvironmentConfigurationDirectives.RECOMPILE in it.directives } - val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in globalDirectives if (dontSkipRegularMode) { - runGeneratedCode(allJsFiles, testModuleName, testPackage, withModuleSystem) - - if (runIrDce && !(dontSkipIrIc && recompile)) { - runGeneratedCode(dceAllJsFiles, testModuleName, testPackage, withModuleSystem) + for (jsFiles in allJsFiles.values) { + runGeneratedCode(jsFiles, testModuleName, testPackage, withModuleSystem) } } } @@ -58,7 +53,7 @@ class JsBoxRunner(testServices: TestServices) : AbstractJsArtifactsCollector(tes val globalDirectives = testServices.moduleStructure.allDirectives val esmOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices).esModulesSubDir - val esmDceOutputDir = JsEnvironmentConfigurator.getDceJsArtifactsOutputDir(testServices).esModulesSubDir + val esmDceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_DCE).esModulesSubDir val dontSkipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE !in globalDirectives val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in globalDirectives diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsIrRecompiledArtifactsIdentityHandler.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsIrRecompiledArtifactsIdentityHandler.kt index bf8283acfd4..1dc363816f7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsIrRecompiledArtifactsIdentityHandler.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsIrRecompiledArtifactsIdentityHandler.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.js.test.handlers import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.fir.backend.FirMetadataSource +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.js.test.converters.ClassicJsBackendFacade import org.jetbrains.kotlin.js.test.converters.augmentWithModuleName import org.jetbrains.kotlin.test.backend.handlers.JsBinaryArtifactHandler @@ -71,7 +72,7 @@ class JsIrRecompiledArtifactsIdentityHandler(testServices: TestServices) : JsBin } private fun Js.JsIrArtifact.allFiles(): Collection { - return listOf(outputFile) + compilerResult.outputs!!.dependencies.map { (moduleId, _) -> + return listOf(outputFile) + compilerResult.outputs[TranslationMode.FULL]!!.dependencies.map { (moduleId, _) -> outputFile.augmentWithModuleName(moduleId) }.sortedBy { it.name } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt index fe1bb3fa465..c9d96a19391 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt @@ -5,14 +5,17 @@ package org.jetbrains.kotlin.js.test.utils +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.js.JavaScript import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.test.JsAdditionalSourceProvider +import org.jetbrains.kotlin.js.test.converters.augmentWithModuleName import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION import org.jetbrains.kotlin.js.testOld.* import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.NO_JS_MODULE_SYSTEM import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.RUN_PLAIN_BOX_FUNCTION import org.jetbrains.kotlin.test.model.BinaryArtifacts @@ -96,30 +99,53 @@ fun testWithModuleSystem(testServices: TestServices): Boolean { fun getAllFilesForRunner( testServices: TestServices, modulesToArtifact: Map -): Pair, List> { +): Map> { val originalFile = testServices.moduleStructure.originalTestDataFiles.first() - val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices) - val dceOutputDir = JsEnvironmentConfigurator.getDceJsArtifactsOutputDir(testServices) val commonFiles = JsAdditionalSourceProvider.getAdditionalJsFiles(originalFile.parent).map { it.absolutePath } val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules) val additionalFiles = getAdditionalFiles(testServices) val additionalMainFiles = getAdditionalMainFiles(testServices) - val artifactsPaths = modulesToArtifact.values.map { it.outputFile.absolutePath }.filter { !File(it).isDirectory } - val klibDependencies = modulesToArtifact.values - .filterIsInstance() - .singleOrNull()?.let { artifact -> - artifact.compilerResult.outputs?.dependencies?.map { (moduleId, _) -> - artifact.outputFile.absolutePath.replace("_v5.js", "-${moduleId}_v5.js") + if (modulesToArtifact.values.any { it is BinaryArtifacts.Js.JsIrArtifact }) { + // JS IR + val (module, compilerResult) = modulesToArtifact.entries.mapNotNull { (m, c) -> (c as? BinaryArtifacts.Js.JsIrArtifact)?.let { m to c.compilerResult } }.single() + + val result = mutableMapOf>() + + compilerResult.outputs.entries.forEach { (mode, outputs) -> + val paths = mutableListOf() + + val outputFile = JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + ".js" + outputs.dependencies.forEach { (moduleId, _) -> + paths += outputFile.augmentWithModuleName(moduleId) } - } ?: emptyList() - val dceJsFiles = (klibDependencies + artifactsPaths).map { it.replace(outputDir.absolutePath, dceOutputDir.absolutePath) } + paths += outputFile - val allJsFiles = additionalFiles + inputJsFilesBefore + klibDependencies + artifactsPaths + commonFiles + additionalMainFiles + inputJsFilesAfter - val dceAllJsFiles = additionalFiles + inputJsFilesBefore + dceJsFiles + commonFiles + additionalMainFiles + inputJsFilesAfter + result[mode] = additionalFiles + inputJsFilesBefore + paths + commonFiles + additionalMainFiles + inputJsFilesAfter + } - return Pair(allJsFiles, dceAllJsFiles) + return result + } else { + // Old BE and ES modules + val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices) + val dceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_DCE) + + val artifactsPaths = modulesToArtifact.values.map { it.outputFile.absolutePath }.filter { !File(it).isDirectory } + val allJsFiles = additionalFiles + inputJsFilesBefore + artifactsPaths + commonFiles + additionalMainFiles + inputJsFilesAfter + + val result = mutableMapOf(TranslationMode.FULL to allJsFiles) + + val globalDirectives = testServices.moduleStructure.allDirectives + val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in globalDirectives + if (runIrDce) { + val dceJsFiles = artifactsPaths.map { it.replace(outputDir.absolutePath, dceOutputDir.absolutePath) } + val dceAllJsFiles = additionalFiles + inputJsFilesBefore + dceJsFiles + commonFiles + additionalMainFiles + inputJsFilesAfter + result[TranslationMode.FULL_DCE] = dceAllJsFiles + } + + return result + } } fun extractAllFilesForEsRunner(testServices: TestServices, esmOutputDir: File): Pair, List> { @@ -155,7 +181,9 @@ fun extractAllFilesForEsRunner(testServices: TestServices, esmOutputDir: File): } fun getOnlyJsFilesForRunner(testServices: TestServices, modulesToArtifact: Map): List { - return getAllFilesForRunner(testServices, modulesToArtifact).first + return getAllFilesForRunner(testServices, modulesToArtifact).let { + it[TranslationMode.FULL] ?: it[TranslationMode.PER_MODULE]!! + } } fun getTestModuleName(testServices: TestServices): String? { diff --git a/js/js.translator/testData/box/export/defaultInlineClassConstructorParam.kt b/js/js.translator/testData/box/export/defaultInlineClassConstructorParam.kt index 08c10843c73..7bab609b77c 100644 --- a/js/js.translator/testData/box/export/defaultInlineClassConstructorParam.kt +++ b/js/js.translator/testData/box/export/defaultInlineClassConstructorParam.kt @@ -1,6 +1,7 @@ // KT-49225 // RUN_PLAIN_BOX_FUNCTION // IGNORE_BACKEND: JS +// SPLIT_PER_MODULE // MODULE: lib // FILE: lib.kt diff --git a/js/js.translator/testData/box/inline/callableReference.kt b/js/js.translator/testData/box/inline/callableReference.kt index 3f5001e61d5..6497f0a9336 100644 --- a/js/js.translator/testData/box/inline/callableReference.kt +++ b/js/js.translator/testData/box/inline/callableReference.kt @@ -3,9 +3,6 @@ package foo // CHECK_CONTAINS_NO_CALLS: multiplyInline except=imul TARGET_BACKENDS=JS -// FIXME: Not inlined on IR BE -// CHECK_CONTAINS_NO_CALLS: multiplyInline except=multiply IGNORED_BACKENDS=JS - // CHECK_FUNCTION_EXISTS: runNoinline // CHECK_NOT_CALLED: runNoinline diff --git a/js/js.translator/testData/box/inlineMultiFile/trait.kt b/js/js.translator/testData/box/inlineMultiFile/trait.kt index 44f2a091cbd..fcbd3e64761 100644 --- a/js/js.translator/testData/box/inlineMultiFile/trait.kt +++ b/js/js.translator/testData/box/inlineMultiFile/trait.kt @@ -9,8 +9,6 @@ package foo import test.* -// CHECK_CONTAINS_NO_CALLS: testClassObject except=Companion_getInstance - internal fun testFinalInline(): String { return Z().finalInline({"final"}) } diff --git a/js/js.translator/testData/box/jsModule/importCountUmd.kt b/js/js.translator/testData/box/jsModule/importCountUmd.kt index ac6642ce5b3..a6719cf3d0f 100644 --- a/js/js.translator/testData/box/jsModule/importCountUmd.kt +++ b/js/js.translator/testData/box/jsModule/importCountUmd.kt @@ -1,7 +1,7 @@ // EXPECTED_REACHABLE_NODES: 1285 // MODULE_KIND: UMD // NO_JS_MODULE_SYSTEM -// FUNCTION_CALLED_TIMES: require count=3 +// FUNCTION_CALLED_TIMES: require count=3 TARGET_BACKENDS=JS @JsModule("lib") @JsNonModule