From 0f183ed6622432ee6ecd9f5b372f3291b214ec6b Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Thu, 21 Mar 2019 15:59:10 +0300 Subject: [PATCH] JS IR: split compilation and deserialization --- .../ir/backend/js/JsIrBackendContext.kt | 15 +- .../kotlin/ir/backend/js/compiler.kt | 289 +---------------- .../jetbrains/kotlin/ir/backend/js/klib.kt | 298 ++++++++++++++++++ .../js/lower/serialization/ir/JsIrLinker.kt | 1 - .../generators/tests/GenerateIrRuntime.kt | 13 +- .../kotlin/js/test/BasicIrBoxTest.kt | 39 ++- 6 files changed, 343 insertions(+), 312 deletions(-) create mode 100644 compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 6353912a563..a71a13c469c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -9,15 +9,14 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.Symbols -import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.js.JsDeclarationFactory import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor -import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.SourceManager @@ -43,12 +42,11 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.Variance class JsIrBackendContext( - val module: ModuleDescriptorImpl, + val module: ModuleDescriptor, override val irBuiltIns: IrBuiltIns, val symbolTable: SymbolTable, irModuleFragment: IrModuleFragment, - override val configuration: CompilerConfiguration, - val phaseConfig: PhaseConfig + override val configuration: CompilerConfiguration ) : CommonBackendContext { override val builtIns = module.builtIns @@ -83,8 +81,7 @@ class JsIrBackendContext( } } - override val sharedVariablesManager = - JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile) + override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile) override val declarationFactory = JsDeclarationFactory() companion object { @@ -226,10 +223,6 @@ class JsIrBackendContext( val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable")) val throwISEymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single()) - val throwNPESymbol = ir.symbols.ThrowNullPointerException - val throwCCESymbol = ir.symbols.ThrowTypeCastException - val throwNWBESymbol = ir.symbols.ThrowNoWhenBranchMatchedException - val throwUPAESymbol = ir.symbols.ThrowUninitializedPropertyAccessException val coroutineImplLabelPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner } val coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner } 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 5143b76a7de..b89bbef80f8 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 @@ -6,173 +6,31 @@ package org.jetbrains.kotlin.ir.backend.js import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider -import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.config.languageVersionSettings -import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider -import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -import org.jetbrains.kotlin.incremental.components.LookupTracker -import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsDeclarationTable -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.newJsDescriptorUniqId -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataModuleDescriptor -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataSerializationUtil -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataVersion -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.createJsKlibMetadataPackageFragmentProvider -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.* import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator -import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.patchDeclarationParents -import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration -import org.jetbrains.kotlin.storage.LockBasedStorageManager -import org.jetbrains.kotlin.utils.DFS -import java.io.File -import java.nio.file.Files.move -import java.nio.file.Paths -import java.nio.file.StandardCopyOption - -sealed class TranslationResult { - class CompiledJsCode(val jsCode: String) : TranslationResult() - - object CompiledKlib : TranslationResult() -} - -data class KlibModuleRef( - val moduleName: String, - val klibPath: String -) - -enum class CompilationMode { - KLIB, - JS -} - -internal val JS_KLIBRARY_CAPABILITY = ModuleDescriptor.Capability("JS KLIBRARY") -internal val moduleHeaderFileName = "module.kji" -internal val declarationsDirName = "ir/" -private val logggg = object : LoggingContext { - override var inVerbosePhase: Boolean - get() = TODO("not implemented") - set(_) {} - - override fun log(message: () -> String) {} -} - -private fun metadataFileName(moduleName: String) = "$moduleName.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}" - -private val CompilerConfiguration.metadataVersion - get() = get(CommonConfigurationKeys.METADATA_VERSION) as? JsKlibMetadataVersion ?: JsKlibMetadataVersion.INSTANCE fun compile( project: Project, files: List, configuration: CompilerConfiguration, phaseConfig: PhaseConfig, - compileMode: CompilationMode, immediateDependencies: List, - allDependencies: List, - outputKlibPath: String -): TranslationResult { + allDependencies: List +): String { + val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = + loadIr(project, files, configuration, immediateDependencies, allDependencies) - val deserializedModuleParts: Map = - allDependencies.associateWith { loadKlibMetadataParts(it) } + val moduleDescriptor = moduleFragment.descriptor - fun findModuleByName(name: String): KlibModuleRef = - allDependencies.find { it.moduleName == name } ?: error("Module is not found: $name") + val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration) - val moduleDependencies: Map> = - deserializedModuleParts.mapValues { (_, parts) -> - parts.importedModules.map(::findModuleByName) - } - - val sortedImmediateDependencies: List = - DFS.topologicalOrder(immediateDependencies) { moduleDependencies.getValue(it) } - .reversed() - - val depsDescriptors = ModulesStructure( - LookupTracker.DO_NOTHING, - configuration.metadataVersion, - configuration.languageVersionSettings, - moduleDependencies - ) - - val builtInsDep = sortedImmediateDependencies.firstOrNull() - val builtInModuleDescriptor = - if (builtInsDep != null) - depsDescriptors.getModuleDescriptor(builtInsDep) - else - null // null in case compiling builtInModule itself - - val analysisResult = - TopDownAnalyzerFacadeForJS.analyzeFiles( - files, - project, - configuration, - sortedImmediateDependencies.map { depsDescriptors.getModuleDescriptor(it) }, - friendModuleDescriptors = emptyList(), - thisIsBuiltInsModule = builtInModuleDescriptor == null, - customBuiltInsModule = builtInModuleDescriptor - ) - - ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() - TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext) - - val moduleDescriptor = analysisResult.moduleDescriptor as ModuleDescriptorImpl - val symbolTable = SymbolTable() - - val psi2IrTranslator = Psi2IrTranslator(configuration.languageVersionSettings) - val psi2IrContext = psi2IrTranslator.createGeneratorContext(moduleDescriptor, analysisResult.bindingContext, symbolTable) - val irBuiltIns = psi2IrContext.irBuiltIns - - val deserializer = JsIrLinker(moduleDescriptor, logggg, irBuiltIns, symbolTable) - - val deserializedModuleFragments = sortedImmediateDependencies.map { - val moduleFile = File(it.klibPath, moduleHeaderFileName) - deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it))!! - } - - val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files, deserializer) - - if (compileMode == CompilationMode.KLIB) { - deserializedModuleFragments.forEach { - ExternalDependenciesGenerator(it.descriptor, symbolTable, irBuiltIns).generateUnboundSymbolsAsDependencies() - } - deserializedModuleFragments.forEach { it.patchDeclarationParents() } - val moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME) as String - serializeModuleIntoKlib( - moduleName, - configuration.metadataVersion, - configuration.languageVersionSettings, - psi2IrContext.symbolTable, - psi2IrContext.bindingContext, - outputKlibPath, - immediateDependencies, - moduleFragment - ) - - return TranslationResult.CompiledKlib - } - - val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration, phaseConfig) - - deserializedModuleFragments.forEach { + // Load declarations referenced during `context` initialization + dependencyModules.forEach { ExternalDependenciesGenerator( it.descriptor, symbolTable, @@ -182,136 +40,21 @@ fun compile( } // TODO: check the order - val irFiles = deserializedModuleFragments.flatMap { it.files } + moduleFragment.files + val irFiles = dependencyModules.flatMap { it.files } + moduleFragment.files moduleFragment.files.clear() moduleFragment.files += irFiles + // TODO Why is this needed? ExternalDependenciesGenerator( - moduleDescriptor = context.module, - symbolTable = context.symbolTable, - irBuiltIns = context.irBuiltIns + moduleDescriptor = moduleDescriptor, + symbolTable = symbolTable, + irBuiltIns = irBuiltIns ).generateUnboundSymbolsAsDependencies() moduleFragment.patchDeclarationParents() - jsPhases.invokeToplevel(context.phaseConfig, context, moduleFragment) + jsPhases.invokeToplevel(phaseConfig, context, moduleFragment) val jsProgram = moduleFragment.accept(IrModuleToJsTransformer(context), null) - return TranslationResult.CompiledJsCode(jsProgram.toString()) -} - -private fun loadKlibMetadataParts( - moduleId: KlibModuleRef -): JsKlibMetadataParts { - val metadataFile = File(moduleId.klibPath, metadataFileName(moduleId.moduleName)) - val serializer = JsKlibMetadataSerializationUtil - return serializer.readModuleAsProto(metadataFile.readBytes()) -} - -private fun loadKlibMetadata( - parts: JsKlibMetadataParts, - moduleId: KlibModuleRef, - isBuiltIn: Boolean, - lookupTracker: LookupTracker, - storageManager: LockBasedStorageManager, - metadataVersion: JsKlibMetadataVersion, - languageVersionSettings: LanguageVersionSettings, - builtinsModule: ModuleDescriptorImpl?, - dependencies: List -): ModuleDescriptorImpl { - assert(isBuiltIn == (builtinsModule === null)) - val builtIns = builtinsModule?.builtIns ?: object : KotlinBuiltIns(storageManager) {} - val md = ModuleDescriptorImpl(Name.special("<${moduleId.moduleName}>"), storageManager, builtIns, - capabilities = mapOf(JS_KLIBRARY_CAPABILITY to File(moduleId.klibPath))) - if (isBuiltIn) builtIns.builtInsModule = md - val currentModuleFragmentProvider = createJsKlibMetadataPackageFragmentProvider( - storageManager, md, parts.header, parts.body, metadataVersion, - CompilerDeserializationConfiguration(languageVersionSettings), - lookupTracker - ) - - val packageFragmentProvider = if (isBuiltIn) { - val functionFragmentProvider = functionInterfacePackageFragmentProvider(storageManager, md) - CompositePackageFragmentProvider(listOf(functionFragmentProvider, currentModuleFragmentProvider)) - } else currentModuleFragmentProvider - - md.initialize(packageFragmentProvider) - md.setDependencies(listOf(md) + dependencies) - - return md -} - - -class ModulesStructure( - private val lookupTracker: LookupTracker, - private val metadataVersion: JsKlibMetadataVersion, - private val languageVersionSettings: LanguageVersionSettings, - private val moduleDependencies: Map> -) { - private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure") - private var runtimeModule: ModuleDescriptorImpl? = null - - private val descriptors = mutableMapOf() - - fun getModuleDescriptor(current: KlibModuleRef): ModuleDescriptorImpl = descriptors.getOrPut(current) { - val parts = loadKlibMetadataParts(current) - val isBuiltIns = parts.importedModules.isEmpty() - loadKlibMetadata( - parts, - current, - isBuiltIns, - lookupTracker, - storageManager, - metadataVersion, - languageVersionSettings, - runtimeModule, - moduleDependencies.getValue(current).map { getModuleDescriptor(it) } - ).also { - if (isBuiltIns) runtimeModule = it - } - } -} - -fun serializeModuleIntoKlib( - moduleName: String, - metadataVersion: JsKlibMetadataVersion, - languageVersionSettings: LanguageVersionSettings, - symbolTable: SymbolTable, - bindingContext: BindingContext, - klibPath: String, - dependencies: List, - moduleFragment: IrModuleFragment -) { - val declarationTable = JsDeclarationTable(moduleFragment.irBuiltins, DescriptorTable()) - - val serializedIr = JsIrModuleSerializer(logggg, declarationTable).serializedIrModule(moduleFragment) - val serializer = JsKlibMetadataSerializationUtil - - val moduleDescription = - JsKlibMetadataModuleDescriptor(moduleName, dependencies.map { it.moduleName }, moduleFragment.descriptor) - val serializedData = serializer.serializeMetadata( - bindingContext, - moduleDescription, - languageVersionSettings, - metadataVersion - ) { declarationDescriptor -> - val index = declarationTable.descriptorTable.get(declarationDescriptor) - index?.let { newJsDescriptorUniqId(it) } - } - - val klibDir = File(klibPath).also { - it.deleteRecursively() - it.mkdirs() - } - - val moduleFile = File(klibDir, moduleHeaderFileName) - moduleFile.writeBytes(serializedIr.module) - - val irDeclarationDir = File(klibDir, declarationsDirName).also { it.mkdir() } - val irCombinedFile = File(irDeclarationDir, "irCombined.knd") - move(Paths.get(serializedIr.combinedDeclarationFilePath), Paths.get(irCombinedFile.path), StandardCopyOption.REPLACE_EXISTING) - - File(klibDir, "${moduleDescription.name}.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}").also { - it.writeBytes(serializedData.asByteArray()) - } -} + return jsProgram.toString() +} \ No newline at end of file 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 new file mode 100644 index 00000000000..c596089dee6 --- /dev/null +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -0,0 +1,298 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.backend.common.LoggingContext +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsDeclarationTable +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.newJsDescriptorUniqId +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataModuleDescriptor +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataSerializationUtil +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataVersion +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.createJsKlibMetadataPackageFragmentProvider +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.* +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS +import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.utils.DFS +import java.io.File +import java.nio.file.Files.move +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +data class KlibModuleRef( + val moduleName: String, + val klibPath: String +) + +internal val JS_KLIBRARY_CAPABILITY = ModuleDescriptor.Capability("JS KLIBRARY") +internal val moduleHeaderFileName = "module.kji" +internal val declarationsDirName = "ir/" +private val logggg = object : LoggingContext { + override var inVerbosePhase: Boolean + get() = TODO("not implemented") + set(_) {} + + override fun log(message: () -> String) {} +} + +private fun metadataFileName(moduleName: String) = "$moduleName.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}" + +private val CompilerConfiguration.metadataVersion + get() = get(CommonConfigurationKeys.METADATA_VERSION) as? JsKlibMetadataVersion ?: JsKlibMetadataVersion.INSTANCE + +fun generateKLib( + project: Project, + files: List, + configuration: CompilerConfiguration, + immediateDependencies: List, + allDependencies: List, + outputKlibPath: String +): KlibModuleRef { + val depsDescriptors = ModulesStructure(project, files, configuration, immediateDependencies, allDependencies) + + val analysisResult = depsDescriptors.runAnalysis() + + val psi2IrTranslator = Psi2IrTranslator(configuration.languageVersionSettings) + val psi2IrContext = psi2IrTranslator.createGeneratorContext(analysisResult.moduleDescriptor, analysisResult.bindingContext) + + val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files) + + val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!! + serializeModuleIntoKlib( + moduleName, + configuration.metadataVersion, + configuration.languageVersionSettings, + psi2IrContext.bindingContext, + outputKlibPath, + immediateDependencies, + moduleFragment + ) + + return KlibModuleRef(moduleName, outputKlibPath) +} + +data class IrModuleInfo( + val module: IrModuleFragment, + val allDependencies: List, + val bultins: IrBuiltIns, + val symbolTable: SymbolTable, + val deserializer: JsIrLinker +) + +fun loadIr( + project: Project, + files: List, + configuration: CompilerConfiguration, + immediateDependencies: List, + allDependencies: List +): IrModuleInfo { + val depsDescriptors = ModulesStructure(project, files, configuration, immediateDependencies, allDependencies) + + val analysisResult = depsDescriptors.runAnalysis() + + val moduleDescriptor = analysisResult.moduleDescriptor + + val symbolTable = SymbolTable() + + val psi2IrTranslator = Psi2IrTranslator(configuration.languageVersionSettings) + val psi2IrContext = psi2IrTranslator.createGeneratorContext(moduleDescriptor, analysisResult.bindingContext, symbolTable) + val irBuiltIns = psi2IrContext.irBuiltIns + + val deserializer = JsIrLinker(moduleDescriptor, logggg, irBuiltIns, symbolTable) + + val deserializedModuleFragments = depsDescriptors.sortedImmediateDependencies.map { + val moduleFile = File(it.klibPath, moduleHeaderFileName) + deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it))!! + } + + val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files, deserializer) + + return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, deserializer) +} + +private fun loadKlibMetadataParts( + moduleId: KlibModuleRef +): JsKlibMetadataParts { + val metadataFile = File(moduleId.klibPath, metadataFileName(moduleId.moduleName)) + val serializer = JsKlibMetadataSerializationUtil + return serializer.readModuleAsProto(metadataFile.readBytes()) +} + +private fun loadKlibMetadata( + parts: JsKlibMetadataParts, + moduleId: KlibModuleRef, + isBuiltIn: Boolean, + lookupTracker: LookupTracker, + storageManager: LockBasedStorageManager, + metadataVersion: JsKlibMetadataVersion, + languageVersionSettings: LanguageVersionSettings, + builtinsModule: ModuleDescriptorImpl?, + dependencies: List +): ModuleDescriptorImpl { + assert(isBuiltIn == (builtinsModule === null)) + val builtIns = builtinsModule?.builtIns ?: object : KotlinBuiltIns(storageManager) {} + val md = ModuleDescriptorImpl(Name.special("<${moduleId.moduleName}>"), storageManager, builtIns, + capabilities = mapOf(JS_KLIBRARY_CAPABILITY to File(moduleId.klibPath))) + if (isBuiltIn) builtIns.builtInsModule = md + val currentModuleFragmentProvider = createJsKlibMetadataPackageFragmentProvider( + storageManager, md, parts.header, parts.body, metadataVersion, + CompilerDeserializationConfiguration(languageVersionSettings), + lookupTracker + ) + + val packageFragmentProvider = if (isBuiltIn) { + val functionFragmentProvider = functionInterfacePackageFragmentProvider(storageManager, md) + CompositePackageFragmentProvider(listOf(functionFragmentProvider, currentModuleFragmentProvider)) + } else currentModuleFragmentProvider + + md.initialize(packageFragmentProvider) + md.setDependencies(listOf(md) + dependencies) + + return md +} + + +class ModulesStructure( + private val project: Project, + private val files: List, + private val compilerConfiguration: CompilerConfiguration, + immediateDependencies: List, + private val allDependencies: List +) { + private val deserializedModuleParts: Map = + allDependencies.associateWith { loadKlibMetadataParts(it) } + + fun findModuleByName(name: String): KlibModuleRef = + allDependencies.find { it.moduleName == name } ?: error("Module is not found: $name") + + val moduleDependencies: Map> = + deserializedModuleParts.mapValues { (_, parts) -> + parts.importedModules.map(::findModuleByName) + } + + val sortedImmediateDependencies: List = + DFS.topologicalOrder(immediateDependencies) { moduleDependencies.getValue(it) } + .reversed() + + val builtInsDep = sortedImmediateDependencies.firstOrNull() + + fun runAnalysis(): JsAnalysisResult { + val analysisResult = + TopDownAnalyzerFacadeForJS.analyzeFiles( + files, + project, + compilerConfiguration, + sortedImmediateDependencies.map { getModuleDescriptor(it) }, + friendModuleDescriptors = emptyList(), + thisIsBuiltInsModule = builtInModuleDescriptor == null, + customBuiltInsModule = builtInModuleDescriptor + ) + + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext) + + return analysisResult + } + + private val lookupTracker: LookupTracker = LookupTracker.DO_NOTHING + private val metadataVersion: JsKlibMetadataVersion = compilerConfiguration.metadataVersion + private val languageVersionSettings: LanguageVersionSettings = compilerConfiguration.languageVersionSettings + + private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure") + private var runtimeModule: ModuleDescriptorImpl? = null + + private val descriptors = mutableMapOf() + + fun getModuleDescriptor(current: KlibModuleRef): ModuleDescriptorImpl = descriptors.getOrPut(current) { + val parts = loadKlibMetadataParts(current) + val isBuiltIns = parts.importedModules.isEmpty() + loadKlibMetadata( + parts, + current, + isBuiltIns, + lookupTracker, + storageManager, + metadataVersion, + languageVersionSettings, + runtimeModule, + moduleDependencies.getValue(current).map { getModuleDescriptor(it) } + ).also { + if (isBuiltIns) runtimeModule = it + } + } + + val builtInModuleDescriptor = + if (builtInsDep != null) + getModuleDescriptor(builtInsDep) + else + null // null in case compiling builtInModule itself +} + +fun serializeModuleIntoKlib( + moduleName: String, + metadataVersion: JsKlibMetadataVersion, + languageVersionSettings: LanguageVersionSettings, + bindingContext: BindingContext, + klibPath: String, + dependencies: List, + moduleFragment: IrModuleFragment +) { + val declarationTable = JsDeclarationTable(moduleFragment.irBuiltins, DescriptorTable()) + + val serializedIr = JsIrModuleSerializer(logggg, declarationTable).serializedIrModule(moduleFragment) + val serializer = JsKlibMetadataSerializationUtil + + val moduleDescription = + JsKlibMetadataModuleDescriptor(moduleName, dependencies.map { it.moduleName }, moduleFragment.descriptor) + val serializedData = serializer.serializeMetadata( + bindingContext, + moduleDescription, + languageVersionSettings, + metadataVersion + ) { declarationDescriptor -> + val index = declarationTable.descriptorTable.get(declarationDescriptor) + index?.let { newJsDescriptorUniqId(it) } + } + + val klibDir = File(klibPath).also { + it.deleteRecursively() + it.mkdirs() + } + + val moduleFile = File(klibDir, moduleHeaderFileName) + moduleFile.writeBytes(serializedIr.module) + + val irDeclarationDir = File(klibDir, declarationsDirName).also { it.mkdir() } + val irCombinedFile = File(irDeclarationDir, "irCombined.knd") + move(Paths.get(serializedIr.combinedDeclarationFilePath), Paths.get(irCombinedFile.path), StandardCopyOption.REPLACE_EXISTING) + + File(klibDir, "${moduleDescription.name}.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}").also { + it.writeBytes(serializedData.asByteArray()) + } +} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt index d3f01503681..6fcd2e600ea 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.common.library.CombinedIrFileReader import org.jetbrains.kotlin.backend.common.library.DeclarationId import org.jetbrains.kotlin.backend.common.serialization.* import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.backend.js.JS_KLIBRARY_CAPABILITY import org.jetbrains.kotlin.ir.backend.js.moduleHeaderFileName import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt index f54348c5943..d206112efc2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt @@ -9,14 +9,10 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiManager -import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.* -import org.jetbrains.kotlin.ir.backend.js.CompilationMode -import org.jetbrains.kotlin.ir.backend.js.compile -import org.jetbrains.kotlin.ir.backend.js.jsPhases +import org.jetbrains.kotlin.ir.backend.js.generateKLib import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.test.JsIrTestRuntime import org.jetbrains.kotlin.psi.KtFile @@ -60,13 +56,10 @@ fun main() { fun buildKlib(sources: List, outputPath: String) { - val configuration = buildConfiguration(environment) - compile( + generateKLib( project = environment.project, files = sources.map(::createPsiFile), - configuration = configuration, - phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jsPhases), - compileMode = CompilationMode.KLIB, + configuration = buildConfiguration(environment), immediateDependencies = emptyList(), allDependencies = emptyList(), outputKlibPath = outputPath 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 fa24a8b2538..2a4a10a370d 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 @@ -109,25 +109,30 @@ abstract class BasicIrBoxTest( if (!isMainModule) it.replace("_v5.js", "/") else it } - val result: TranslationResult = compile( - project = config.project, - files = filesToCompile, - configuration = config.configuration, - phaseConfig = config.configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jsPhases), - compileMode = if (isMainModule) CompilationMode.JS else CompilationMode.KLIB, - immediateDependencies = dependencies, - allDependencies = allDependencies, - outputKlibPath = actualOutputFile - ) + if (isMainModule) { + val jsCode = compile( + project = config.project, + files = filesToCompile, + configuration = config.configuration, + phaseConfig = config.configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jsPhases), + immediateDependencies = dependencies, + allDependencies = allDependencies + ) - val moduleName = config.configuration.get(CommonConfigurationKeys.MODULE_NAME) as String - val module = KlibModuleRef(moduleName, actualOutputFile) - - compilationCache[outputFile.name.replace(".js", ".meta.js")] = module - - if (result is TranslationResult.CompiledJsCode) { - val wrappedCode = wrapWithModuleEmulationMarkers(result.jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) + val wrappedCode = wrapWithModuleEmulationMarkers(jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) outputFile.write(wrappedCode) + + } else { + val module = generateKLib( + project = config.project, + files = filesToCompile, + configuration = config.configuration, + immediateDependencies = dependencies, + allDependencies = allDependencies, + outputKlibPath = actualOutputFile + ) + + compilationCache[outputFile.name.replace(".js", ".meta.js")] = module } }