JS IR: split compilation and deserialization
This commit is contained in:
+4
-11
@@ -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 }
|
||||
|
||||
@@ -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<File>("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<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
phaseConfig: PhaseConfig,
|
||||
compileMode: CompilationMode,
|
||||
immediateDependencies: List<KlibModuleRef>,
|
||||
allDependencies: List<KlibModuleRef>,
|
||||
outputKlibPath: String
|
||||
): TranslationResult {
|
||||
allDependencies: List<KlibModuleRef>
|
||||
): String {
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||
loadIr(project, files, configuration, immediateDependencies, allDependencies)
|
||||
|
||||
val deserializedModuleParts: Map<KlibModuleRef, JsKlibMetadataParts> =
|
||||
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<KlibModuleRef, List<KlibModuleRef>> =
|
||||
deserializedModuleParts.mapValues { (_, parts) ->
|
||||
parts.importedModules.map(::findModuleByName)
|
||||
}
|
||||
|
||||
val sortedImmediateDependencies: List<KlibModuleRef> =
|
||||
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>
|
||||
): 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<KlibModuleRef, List<KlibModuleRef>>
|
||||
) {
|
||||
private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure")
|
||||
private var runtimeModule: ModuleDescriptorImpl? = null
|
||||
|
||||
private val descriptors = mutableMapOf<KlibModuleRef, ModuleDescriptorImpl>()
|
||||
|
||||
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<KlibModuleRef>,
|
||||
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()
|
||||
}
|
||||
@@ -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<File>("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<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
immediateDependencies: List<KlibModuleRef>,
|
||||
allDependencies: List<KlibModuleRef>,
|
||||
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<IrModuleFragment>,
|
||||
val bultins: IrBuiltIns,
|
||||
val symbolTable: SymbolTable,
|
||||
val deserializer: JsIrLinker
|
||||
)
|
||||
|
||||
fun loadIr(
|
||||
project: Project,
|
||||
files: List<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
immediateDependencies: List<KlibModuleRef>,
|
||||
allDependencies: List<KlibModuleRef>
|
||||
): 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>
|
||||
): 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<KtFile>,
|
||||
private val compilerConfiguration: CompilerConfiguration,
|
||||
immediateDependencies: List<KlibModuleRef>,
|
||||
private val allDependencies: List<KlibModuleRef>
|
||||
) {
|
||||
private val deserializedModuleParts: Map<KlibModuleRef, JsKlibMetadataParts> =
|
||||
allDependencies.associateWith { loadKlibMetadataParts(it) }
|
||||
|
||||
fun findModuleByName(name: String): KlibModuleRef =
|
||||
allDependencies.find { it.moduleName == name } ?: error("Module is not found: $name")
|
||||
|
||||
val moduleDependencies: Map<KlibModuleRef, List<KlibModuleRef>> =
|
||||
deserializedModuleParts.mapValues { (_, parts) ->
|
||||
parts.importedModules.map(::findModuleByName)
|
||||
}
|
||||
|
||||
val sortedImmediateDependencies: List<KlibModuleRef> =
|
||||
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<KlibModuleRef, ModuleDescriptorImpl>()
|
||||
|
||||
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<KlibModuleRef>,
|
||||
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())
|
||||
}
|
||||
}
|
||||
-1
@@ -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
|
||||
|
||||
@@ -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<String>, 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user