diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt index e0c464b55f3..a2096268080 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt @@ -128,6 +128,13 @@ class K2JSCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xir-only", description = "Disables pre-IR backend") var irOnly: Boolean by FreezableVar(false) + @Argument( + value = "-Xinclude", + valueDescription = "", + description = "A path to an intermediate library that should be processed in the same manner as source files." + ) + var includes: String? by NullableStringFreezableVar(null) + @Argument( value = "-Xgenerate-dts", description = "Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only." 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 ebba3bfd4b3..1cedaeedb53 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 @@ -36,11 +36,11 @@ import org.jetbrains.kotlin.js.config.EcmaVersion import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding +import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.util.Logger -import org.jetbrains.kotlin.utils.JsMetadataVersion import org.jetbrains.kotlin.utils.KotlinPaths import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull @@ -102,11 +102,13 @@ class K2JsIrCompiler : CLICompiler() { if (arguments.version) { return OK } - messageCollector.report(ERROR, "Specify at least one source file or directory", null) - return COMPILATION_ERROR + if (arguments.includes.isNullOrEmpty()) { + messageCollector.report(ERROR, "Specify at least one source file or directory", null) + return COMPILATION_ERROR + } } - val libraries: List = configureLibraries(arguments.libraries) + val libraries: List = configureLibraries(arguments.libraries) + listOfNotNull(arguments.includes) val friendLibraries: List = configureLibraries(arguments.friendModules) configuration.put(JSConfigurationKeys.LIBRARIES, libraries) @@ -138,7 +140,7 @@ class K2JsIrCompiler : CLICompiler() { return ExitCode.COMPILATION_ERROR } - if (sourcesFiles.isEmpty() && !IncrementalCompilation.isEnabledForJs()) { + if (sourcesFiles.isEmpty() && !IncrementalCompilation.isEnabledForJs() && arguments.includes.isNullOrEmpty()) { messageCollector.report(ERROR, "No source files", null) return COMPILATION_ERROR } @@ -200,10 +202,23 @@ class K2JsIrCompiler : CLICompiler() { if (arguments.irProduceJs) { val phaseConfig = createPhaseConfig(jsPhases, arguments, messageCollector) + val includes = arguments.includes + + val mainModule = if (includes != null) { + if (sourcesFiles.isNotEmpty()) { + messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present") + } + val allLibraries = resolvedLibraries.getFullList() + val mainLib = allLibraries.find { it.libraryFile.absolutePath == File(includes).absolutePath }!! + MainModule.Klib(mainLib) + } else { + MainModule.SourceFiles(sourcesFiles) + } + val compiledModule = try { compile( projectJs, - sourcesFiles, + mainModule, config.configuration, phaseConfig, allDependencies = resolvedLibraries, 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 f2fc1289c1c..4c2221aba86 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 @@ -47,7 +47,7 @@ class CompilerResult( fun compile( project: Project, - files: List, + mainModule: MainModule, configuration: CompilerConfiguration, phaseConfig: PhaseConfig, allDependencies: KotlinLibraryResolveResult, @@ -57,8 +57,8 @@ fun compile( generateFullJs: Boolean = true, generateDceJs: Boolean = false ): CompilerResult { - val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = - loadIr(project, files, configuration, allDependencies, friendDependencies) + val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = + loadIr(project, mainModule, configuration, allDependencies, friendDependencies) val moduleDescriptor = moduleFragment.descriptor @@ -72,7 +72,12 @@ fun compile( ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies() } - val irFiles = dependencyModules.flatMap { it.files } + moduleFragment.files + val allModules = when (mainModule) { + is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment) + is MainModule.Klib -> dependencyModules + } + + val irFiles = allModules.flatMap { it.files } moduleFragment.files.clear() moduleFragment.files += irFiles diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt index 9a77bffe905..73716ea67c3 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.backend.wasm.codegen.IrModuleToWasm import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.ir.backend.js.MainModule import org.jetbrains.kotlin.ir.backend.js.loadIr import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList @@ -31,7 +32,7 @@ fun compileWasm( exportedDeclarations: Set = emptySet() ): WasmCompilerResult { val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = - loadIr(project, files, configuration, allDependencies, friendDependencies) + loadIr(project, MainModule.SourceFiles(files), configuration, allDependencies, friendDependencies) val moduleDescriptor = moduleFragment.descriptor val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration) diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index 10e94bb76e1..ccad12cb796 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable +import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion import org.jetbrains.kotlin.backend.common.serialization.metadata.DynamicTypeDeserializer import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion @@ -19,8 +20,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor @@ -32,10 +31,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.KlibMetad import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.util.ExpectDeclarationRemover -import org.jetbrains.kotlin.ir.util.IrDeserializer -import org.jetbrains.kotlin.ir.util.SymbolTable -import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult import org.jetbrains.kotlin.js.config.JSConfigurationKeys @@ -126,7 +122,7 @@ fun generateKLib( icData = emptyList() } - val depsDescriptors = ModulesStructure(project, files, configuration, allDependencies, friendDependencies) + val depsDescriptors = ModulesStructure(project, MainModule.SourceFiles(files), configuration, allDependencies, friendDependencies) val psi2IrContext = runAnalysisAndPreparePsi2Ir(depsDescriptors) @@ -174,30 +170,63 @@ private fun sortDependencies(dependencies: List, mapping: Map, + mainModule: MainModule, configuration: CompilerConfiguration, allDependencies: KotlinLibraryResolveResult, friendDependencies: List ): IrModuleInfo { - val depsDescriptors = ModulesStructure(project, files, configuration, allDependencies, friendDependencies) + val depsDescriptors = ModulesStructure(project, mainModule, configuration, allDependencies, friendDependencies) - val psi2IrContext = runAnalysisAndPreparePsi2Ir(depsDescriptors) + when (mainModule) { + is MainModule.SourceFiles -> { + val psi2IrContext: GeneratorContext = runAnalysisAndPreparePsi2Ir(depsDescriptors) + val irBuiltIns = psi2IrContext.irBuiltIns + val symbolTable = psi2IrContext.symbolTable + val moduleDescriptor = psi2IrContext.moduleDescriptor - val irBuiltIns = psi2IrContext.irBuiltIns - val symbolTable = psi2IrContext.symbolTable - val moduleDescriptor = psi2IrContext.moduleDescriptor + val deserializer = JsIrLinker(moduleDescriptor, JsMangler, emptyLoggingContext, irBuiltIns, symbolTable) - val deserializer = JsIrLinker(moduleDescriptor, JsMangler, emptyLoggingContext, irBuiltIns, symbolTable) + val deserializedModuleFragments = sortDependencies(allDependencies.getFullList(), depsDescriptors.descriptors).map { + deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it))!! + } - val deserializedModuleFragments = sortDependencies(allDependencies.getFullList(), depsDescriptors.descriptors).map { - deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it))!! + deserializer.initializeExpectActualLinker() + + val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, mainModule.files, deserializer) + return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, deserializer) + } + is MainModule.Klib -> { + val moduleDescriptor = depsDescriptors.getModuleDescriptor(mainModule.lib) + val symbolTable = SymbolTable() + val constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable) + val typeTranslator = TypeTranslator( + symbolTable, + depsDescriptors.compilerConfiguration.languageVersionSettings, + builtIns = moduleDescriptor.builtIns + ) + typeTranslator.constantValueGenerator = constantValueGenerator + constantValueGenerator.typeTranslator = typeTranslator + val irBuiltIns = IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable) + val deserializer = JsIrLinker(moduleDescriptor, JsMangler, emptyLoggingContext, irBuiltIns, symbolTable) + + val deserializedModuleFragments = sortDependencies(allDependencies.getFullList(), depsDescriptors.descriptors).map { + val strategy = + if (it == mainModule.lib) + DeserializationStrategy.ALL + else + DeserializationStrategy.EXPLICITLY_EXPORTED + + deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it), strategy) + } + deserializer.initializeExpectActualLinker() + val moduleFragment = deserializedModuleFragments.last() + + val irProviders = generateTypicalIrProviderList(moduleDescriptor, irBuiltIns, symbolTable, deserializer) + ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies() + + return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, deserializer) + } } - - deserializer.initializeExpectActualLinker() - - val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, files, deserializer) - - return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, deserializer) } private fun runAnalysisAndPreparePsi2Ir(depsDescriptors: ModulesStructure): GeneratorContext { @@ -277,9 +306,14 @@ fun getModuleDescriptorByLibrary(current: KotlinLibrary, mapping: Map) : MainModule() + class Klib(val lib: KotlinLibrary) : MainModule() +} + private class ModulesStructure( private val project: Project, - private val files: List, + private val mainModule: MainModule, val compilerConfiguration: CompilerConfiguration, val allDependencies: KotlinLibraryResolveResult, private val friendDependencies: List @@ -299,6 +333,9 @@ private class ModulesStructure( val builtInsDep = allDependencies.getFullList().find { it.isBuiltIns } fun runAnalysis(): JsAnalysisResult { + require(mainModule is MainModule.SourceFiles) + val files = mainModule.files + // TODO: Should we not provide default message collector? val messageCollector: MessageCollector = compilerConfiguration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) ?: MessageCollector.NONE diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index 49dc369cc6e..d62f0ac5e2f 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -4,6 +4,7 @@ where advanced options include: -Xfriend-modules= Paths to friend modules -Xfriend-modules-disabled Disable internal declaration export -Xgenerate-dts Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only. + -Xinclude= A path to an intermediate library that should be processed in the same manner as source files. -Xir-dce Perform experimental dead code elimination -Xir-only Disables pre-IR backend -Xir-produce-js Generates JS file using IR backend. Also disables pre-IR backend diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index 99e4482bad3..ee89b6b88bc 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -9,10 +9,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.js.messageCollectorLogger -import org.jetbrains.kotlin.ir.backend.js.compile -import org.jetbrains.kotlin.ir.backend.js.generateKLib -import org.jetbrains.kotlin.ir.backend.js.jsPhases -import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries +import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.facade.MainCallParameters @@ -115,7 +112,7 @@ abstract class BasicIrBoxTest( val compiledModule = compile( project = config.project, - files = filesToCompile, + mainModule = MainModule.SourceFiles(filesToCompile), configuration = config.configuration, phaseConfig = phaseConfig, allDependencies = resolvedLibraries,