blob: 395ff75ca7eed5b81a815d520fb1436931be6362 [file] [log] [blame]
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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 com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.CompilationErrorException
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.lower.ExpectDeclarationRemover
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.common.serialization.metadata.DynamicTypeDeserializer
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataIncrementalSerializer
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.*
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.IrSymbol
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.ErrorTolerancePolicy
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.konan.properties.Properties
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
import org.jetbrains.kotlin.library.impl.buildKotlinLibrary
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.progress.IncrementalNextRoundException
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorForNotFoundClasses
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.util.DummyLogger
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.File
val KotlinLibrary.moduleName: String
get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)
// Considering library built-ins if it has no dependencies.
// All non-built-ins libraries must have built-ins as a dependency.
val KotlinLibrary.isBuiltIns: Boolean
get() = manifestProperties
.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true)
.isEmpty()
private val CompilerConfiguration.metadataVersion
get() = get(CommonConfigurationKeys.METADATA_VERSION) as? KlibMetadataVersion ?: KlibMetadataVersion.INSTANCE
private val CompilerConfiguration.expectActualLinker: Boolean
get() = get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false
class KotlinFileSerializedData(val metadata: ByteArray, val irData: SerializedIrFile)
fun IrMessageLogger?.toResolverLogger(): Logger {
if (this == null) return DummyLogger
return object : Logger {
override fun log(message: String) {
report(IrMessageLogger.Severity.INFO, message, null)
}
override fun error(message: String) {
report(IrMessageLogger.Severity.ERROR, message, null)
}
override fun warning(message: String) {
report(IrMessageLogger.Severity.WARNING, message, null)
}
override fun fatal(message: String): Nothing {
report(IrMessageLogger.Severity.ERROR, message, null)
kotlin.error("FATAL ERROR: $message")
}
}
}
fun generateIrForKlibSerialization(
project: Project,
files: List<KtFile>,
configuration: CompilerConfiguration,
analysisResult: AnalysisResult,
sortedDependencies: Collection<KotlinLibrary>,
icData: MutableList<KotlinFileSerializedData>,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
irFactory: IrFactory,
verifySignatures: Boolean = true,
getDescriptorByLibrary: (KotlinLibrary) -> ModuleDescriptor,
): IrModuleFragment {
val incrementalDataProvider = configuration.get(JSConfigurationKeys.INCREMENTAL_DATA_PROVIDER)
val errorPolicy = configuration.get(JSConfigurationKeys.ERROR_TOLERANCE_POLICY) ?: ErrorTolerancePolicy.DEFAULT
val messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
val allowUnboundSymbols = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val serializedIrFiles = mutableListOf<SerializedIrFile>()
if (incrementalDataProvider != null) {
val nonCompiledSources = files.map { VfsUtilCore.virtualToIoFile(it.virtualFile) to it }.toMap()
val compiledIrFiles = incrementalDataProvider.serializedIrFiles
val compiledMetaFiles = incrementalDataProvider.compiledPackageParts
assert(compiledIrFiles.size == compiledMetaFiles.size)
val storage = mutableListOf<KotlinFileSerializedData>()
for (f in compiledIrFiles.keys) {
if (f in nonCompiledSources) continue
val irData = compiledIrFiles[f] ?: error("No Ir Data found for file $f")
val metaFile = compiledMetaFiles[f] ?: error("No Meta Data found for file $f")
val irFile = with(irData) {
SerializedIrFile(fileData, String(fqn), f.path.replace('\\', '/'), types, signatures, strings, bodies, declarations, debugInfo)
}
storage.add(KotlinFileSerializedData(metaFile.metadata, irFile))
}
icData.addAll(storage)
serializedIrFiles.addAll(storage.map { it.irData })
}
val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), irFactory)
val psi2Ir = Psi2IrTranslator(configuration.languageVersionSettings, Psi2IrConfiguration(errorPolicy.allowErrors, allowUnboundSymbols))
val psi2IrContext = psi2Ir.createGeneratorContext(analysisResult.moduleDescriptor, analysisResult.bindingContext, symbolTable)
val irBuiltIns = psi2IrContext.irBuiltIns
val feContext = psi2IrContext.run {
JsIrLinker.JsFePluginContext(moduleDescriptor, symbolTable, typeTranslator, irBuiltIns)
}
val stubGenerator = DeclarationStubGeneratorImpl(
psi2IrContext.moduleDescriptor,
symbolTable,
irBuiltIns,
DescriptorByIdSignatureFinderImpl(psi2IrContext.moduleDescriptor, JsManglerDesc),
)
val irLinker = JsIrLinker(
psi2IrContext.moduleDescriptor,
messageLogger,
psi2IrContext.irBuiltIns,
psi2IrContext.symbolTable,
feContext,
ICData(serializedIrFiles, errorPolicy.allowErrors),
stubGenerator = stubGenerator
)
sortedDependencies.map { irLinker.deserializeOnlyHeaderModule(getDescriptorByLibrary(it), it) }
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(
project,
files,
irLinker,
messageLogger,
expectDescriptorToSymbol,
stubGenerator
)
if (verifySignatures) {
moduleFragment.acceptVoid(ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc)))
}
if (configuration.getBoolean(JSConfigurationKeys.FAKE_OVERRIDE_VALIDATOR)) {
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
irLinker.modules.forEach { fakeOverrideChecker.check(it) }
}
if (!configuration.expectActualLinker) {
moduleFragment.transform(ExpectDeclarationRemover(psi2IrContext.symbolTable, false), null)
}
return moduleFragment
}
fun generateKLib(
depsDescriptors: ModulesStructure,
irFactory: IrFactory,
outputKlibPath: String,
nopack: Boolean,
verifySignatures: Boolean = true,
abiVersion: KotlinAbiVersion = KotlinAbiVersion.CURRENT,
jsOutputName: String?
) {
val project = depsDescriptors.project
val files = (depsDescriptors.mainModule as MainModule.SourceFiles).files
val configuration = depsDescriptors.compilerConfiguration
val allDependencies = depsDescriptors.allDependencies.map { it.library }
val messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
val icData = mutableListOf<KotlinFileSerializedData>()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val moduleFragment = generateIrForKlibSerialization(
project,
files,
configuration,
depsDescriptors.jsFrontEndResult.jsAnalysisResult,
sortDependencies(depsDescriptors.descriptors),
icData,
expectDescriptorToSymbol,
irFactory,
verifySignatures
) {
depsDescriptors.getModuleDescriptor(it)
}
serializeModuleIntoKlib(
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
project,
configuration,
messageLogger,
depsDescriptors.jsFrontEndResult.bindingContext,
files,
outputKlibPath,
allDependencies,
moduleFragment,
expectDescriptorToSymbol,
icData,
nopack,
perFile = false,
depsDescriptors.jsFrontEndResult.hasErrors,
abiVersion,
jsOutputName
)
}
data class IrModuleInfo(
val module: IrModuleFragment,
val allDependencies: List<IrModuleFragment>,
val bultins: IrBuiltIns,
val symbolTable: SymbolTable,
val deserializer: JsIrLinker,
val moduleFragmentToUniqueName: Map<IrModuleFragment, String>,
)
fun sortDependencies(mapping: Map<KotlinLibrary, ModuleDescriptor>): Collection<KotlinLibrary> {
val m2l = mapping.map { it.value to it.key }.toMap()
return DFS.topologicalOrder(mapping.keys) { m ->
val descriptor = mapping[m] ?: error("No descriptor found for library ${m.libraryName}")
descriptor.allDependencyModules.filter { it != descriptor }.map { m2l[it] }
}.reversed()
}
fun deserializeDependencies(
sortedDependencies: Collection<KotlinLibrary>,
irLinker: JsIrLinker,
mainModuleLib: KotlinLibrary?,
filesToLoad: Set<String>?,
mapping: (KotlinLibrary) -> ModuleDescriptor
): Map<IrModuleFragment, KotlinLibrary> {
return sortedDependencies.associateBy { klib ->
val descriptor = mapping(klib)
when {
mainModuleLib == null -> irLinker.deserializeIrModuleHeader(descriptor, klib, { DeserializationStrategy.EXPLICITLY_EXPORTED })
filesToLoad != null && klib == mainModuleLib -> irLinker.deserializeDirtyFiles(descriptor, klib, filesToLoad)
filesToLoad != null && klib != mainModuleLib -> irLinker.deserializeHeadersWithInlineBodies(descriptor, klib)
klib == mainModuleLib -> irLinker.deserializeIrModuleHeader(descriptor, klib, { DeserializationStrategy.ALL })
else -> irLinker.deserializeIrModuleHeader(descriptor, klib, { DeserializationStrategy.EXPLICITLY_EXPORTED })
}
}
}
fun getFunctionFactoryCallback(stdlibModule: IrModuleFragment) = { packageFragmentDescriptor: PackageFragmentDescriptor ->
IrFileImpl(
NaiveSourceBasedFileEntryImpl("${packageFragmentDescriptor.fqName}-[K][Suspend]Functions"),
packageFragmentDescriptor,
stdlibModule
).also { stdlibModule.files += it }
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun loadIr(
depsDescriptors: ModulesStructure,
irFactory: IrFactory,
verifySignatures: Boolean,
filesToLoad: Set<String>? = null,
loadFunctionInterfacesIntoStdlib: Boolean = false,
): IrModuleInfo {
val project = depsDescriptors.project
val mainModule = depsDescriptors.mainModule
val configuration = depsDescriptors.compilerConfiguration
val allDependencies = depsDescriptors.allDependencies.map { it.library }
val errorPolicy = configuration.get(JSConfigurationKeys.ERROR_TOLERANCE_POLICY) ?: ErrorTolerancePolicy.DEFAULT
val messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
val allowUnboundSymbol = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val signaturer = IdSignatureDescriptor(JsManglerDesc)
val symbolTable = SymbolTable(signaturer, irFactory)
when (mainModule) {
is MainModule.SourceFiles -> {
assert(filesToLoad == null)
val psi2IrContext = preparePsi2Ir(depsDescriptors, errorPolicy, symbolTable, allowUnboundSymbol)
val friendModules =
mapOf(psi2IrContext.moduleDescriptor.name.asString() to depsDescriptors.friendDependencies.map { it.library.uniqueName })
return getIrModuleInfoForSourceFiles(
psi2IrContext,
project,
configuration,
mainModule.files,
sortDependencies(depsDescriptors.descriptors),
friendModules,
symbolTable,
messageLogger,
loadFunctionInterfacesIntoStdlib,
verifySignatures,
) { depsDescriptors.getModuleDescriptor(it) }
}
is MainModule.Klib -> {
val mainPath = File(mainModule.libPath).canonicalPath
val mainModuleLib = allDependencies.find { it.libraryFile.canonicalPath == mainPath }
?: error("No module with ${mainModule.libPath} found")
val moduleDescriptor = depsDescriptors.getModuleDescriptor(mainModuleLib)
val sortedDependencies = sortDependencies(depsDescriptors.descriptors)
val friendModules = mapOf(mainModuleLib.uniqueName to depsDescriptors.friendDependencies.map { it.library.uniqueName })
return getIrModuleInfoForKlib(
moduleDescriptor,
sortedDependencies,
friendModules,
filesToLoad,
configuration,
symbolTable,
messageLogger,
loadFunctionInterfacesIntoStdlib,
) { depsDescriptors.getModuleDescriptor(it) }
}
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun getIrModuleInfoForKlib(
moduleDescriptor: ModuleDescriptor,
sortedDependencies: Collection<KotlinLibrary>,
friendModules: Map<String, List<String>>,
filesToLoad: Set<String>?,
configuration: CompilerConfiguration,
symbolTable: SymbolTable,
messageLogger: IrMessageLogger,
loadFunctionInterfacesIntoStdlib: Boolean,
mapping: (KotlinLibrary) -> ModuleDescriptor,
): IrModuleInfo {
val mainModuleLib = sortedDependencies.last()
val typeTranslator = TypeTranslatorImpl(symbolTable, configuration.languageVersionSettings, moduleDescriptor)
val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
val allowUnboundSymbols = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(allowUnboundSymbols)
val irLinker = JsIrLinker(
currentModule = null,
messageLogger = messageLogger,
builtIns = irBuiltIns,
symbolTable = symbolTable,
translationPluginContext = null,
icData = null,
friendModules = friendModules,
unlinkedDeclarationsSupport = unlinkedDeclarationsSupport
)
val deserializedModuleFragmentsToLib = deserializeDependencies(sortedDependencies, irLinker, mainModuleLib, filesToLoad, mapping)
val deserializedModuleFragments = deserializedModuleFragmentsToLib.keys.toList()
irBuiltIns.functionFactory = IrDescriptorBasedFunctionFactory(
irBuiltIns,
symbolTable,
typeTranslator,
if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null,
true
)
val moduleFragment = deserializedModuleFragments.last()
irLinker.init(null, emptyList())
ExternalDependenciesGenerator(symbolTable, listOf(irLinker)).generateUnboundSymbolsAsDependencies()
irLinker.postProcess()
return IrModuleInfo(
moduleFragment,
deserializedModuleFragments,
irBuiltIns,
symbolTable,
irLinker,
deserializedModuleFragmentsToLib.getUniqueNameForEachFragment()
)
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun getIrModuleInfoForSourceFiles(
psi2IrContext: GeneratorContext,
project: Project,
configuration: CompilerConfiguration,
files: List<KtFile>,
allSortedDependencies: Collection<KotlinLibrary>,
friendModules: Map<String, List<String>>,
symbolTable: SymbolTable,
messageLogger: IrMessageLogger,
loadFunctionInterfacesIntoStdlib: Boolean,
verifySignatures: Boolean,
mapping: (KotlinLibrary) -> ModuleDescriptor
): IrModuleInfo {
val irBuiltIns = psi2IrContext.irBuiltIns
val feContext = psi2IrContext.run {
JsIrLinker.JsFePluginContext(moduleDescriptor, symbolTable, typeTranslator, irBuiltIns)
}
val allowUnboundSymbols = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false
val unlinkedDeclarationsSupport = JsUnlinkedDeclarationsSupport(allowUnboundSymbols)
val irLinker = JsIrLinker(
currentModule = psi2IrContext.moduleDescriptor,
messageLogger = messageLogger,
builtIns = irBuiltIns,
symbolTable = symbolTable,
translationPluginContext = feContext,
icData = null,
friendModules = friendModules,
unlinkedDeclarationsSupport = unlinkedDeclarationsSupport
)
val deserializedModuleFragmentsToLib = deserializeDependencies(allSortedDependencies, irLinker, null,null, mapping)
val deserializedModuleFragments = deserializedModuleFragmentsToLib.keys.toList()
(irBuiltIns as IrBuiltInsOverDescriptors).functionFactory =
IrDescriptorBasedFunctionFactory(
irBuiltIns,
symbolTable,
psi2IrContext.typeTranslator,
if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null,
true
)
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, files, irLinker, messageLogger)
if (!allowUnboundSymbols) {
symbolTable.noUnboundLeft("Unbound symbols left after linker")
}
// TODO: not sure whether this check should be enabled by default. Add configuration key for it.
val mangleChecker = ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc))
if (verifySignatures) {
moduleFragment.acceptVoid(mangleChecker)
}
if (configuration.getBoolean(JSConfigurationKeys.FAKE_OVERRIDE_VALIDATOR)) {
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
irLinker.modules.forEach { fakeOverrideChecker.check(it) }
}
if (verifySignatures) {
irBuiltIns.knownBuiltins.forEach { it.acceptVoid(mangleChecker) }
}
return IrModuleInfo(
moduleFragment,
deserializedModuleFragments,
irBuiltIns,
symbolTable,
irLinker,
deserializedModuleFragmentsToLib.getUniqueNameForEachFragment()
)
}
fun prepareAnalyzedSourceModule(
project: Project,
files: List<KtFile>,
configuration: CompilerConfiguration,
dependencies: List<String>,
friendDependencies: List<String>,
analyzer: AbstractAnalyzerWithCompilerReport,
errorPolicy: ErrorTolerancePolicy = configuration.get(JSConfigurationKeys.ERROR_TOLERANCE_POLICY) ?: ErrorTolerancePolicy.DEFAULT,
): ModulesStructure {
val mainModule = MainModule.SourceFiles(files)
val sourceModule = ModulesStructure(project, mainModule, configuration, dependencies, friendDependencies)
return sourceModule.apply {
runAnalysis(errorPolicy, analyzer)
}
}
private fun preparePsi2Ir(
depsDescriptors: ModulesStructure,
errorIgnorancePolicy: ErrorTolerancePolicy,
symbolTable: SymbolTable,
allowUnboundSymbols: Boolean
): GeneratorContext {
val analysisResult = depsDescriptors.jsFrontEndResult
val psi2Ir = Psi2IrTranslator(
depsDescriptors.compilerConfiguration.languageVersionSettings,
Psi2IrConfiguration(errorIgnorancePolicy.allowErrors, allowUnboundSymbols)
)
return psi2Ir.createGeneratorContext(
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
symbolTable
)
}
fun GeneratorContext.generateModuleFragmentWithPlugins(
project: Project,
files: List<KtFile>,
irLinker: IrDeserializer,
messageLogger: IrMessageLogger,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null,
stubGenerator: DeclarationStubGenerator? = null
): IrModuleFragment {
val psi2Ir = Psi2IrTranslator(languageVersionSettings, configuration)
val extensions = IrGenerationExtension.getInstances(project)
if (extensions.isNotEmpty()) {
// plugin context should be instantiated before postprocessing steps
val pluginContext = IrPluginContextImpl(
moduleDescriptor,
bindingContext,
languageVersionSettings,
symbolTable,
typeTranslator,
irBuiltIns,
linker = irLinker,
messageLogger
)
for (extension in extensions) {
psi2Ir.addPostprocessingStep { module ->
val old = stubGenerator?.unboundSymbolGeneration
try {
stubGenerator?.unboundSymbolGeneration = true
extension.generate(module, pluginContext)
} finally {
stubGenerator?.unboundSymbolGeneration = old!!
}
}
}
}
return psi2Ir.generateModuleFragment(
this,
files,
listOf(stubGenerator ?: irLinker),
extensions,
expectDescriptorToSymbol
)
}
private fun createBuiltIns(storageManager: StorageManager) = object : KotlinBuiltIns(storageManager) {}
public val JsFactories = KlibMetadataFactories(::createBuiltIns, DynamicTypeDeserializer)
fun getModuleDescriptorByLibrary(current: KotlinLibrary, mapping: Map<String, ModuleDescriptorImpl>): ModuleDescriptorImpl {
val md = JsFactories.DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns(
current,
LanguageVersionSettingsImpl.DEFAULT,
LockBasedStorageManager.NO_LOCKS,
null,
packageAccessHandler = null, // TODO: This is a speed optimization used by Native. Don't bother for now.
lookupTracker = LookupTracker.DO_NOTHING
)
// if (isBuiltIns) runtimeModule = md
val dependencies = current.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { mapping.getValue(it) }
md.setDependencies(listOf(md) + dependencies)
return md
}
sealed class MainModule {
class SourceFiles(val files: List<KtFile>) : MainModule()
class Klib(val libPath: String) : MainModule()
}
class ModulesStructure(
val project: Project,
val mainModule: MainModule,
val compilerConfiguration: CompilerConfiguration,
val dependencies: Collection<String>,
friendDependenciesPaths: Collection<String>,
) {
val allResolvedDependencies = jsResolveLibraries(
dependencies,
compilerConfiguration[JSConfigurationKeys.REPOSITORIES] ?: emptyList(),
compilerConfiguration[IrMessageLogger.IR_MESSAGE_LOGGER].toResolverLogger()
)
val allDependencies = allResolvedDependencies.getFullResolvedList()
val friendDependencies = allDependencies.run {
val friendAbsolutePaths = friendDependenciesPaths.map { File(it).canonicalPath }
filter {
it.library.libraryFile.absolutePath in friendAbsolutePaths
}
}
val moduleDependencies: Map<KotlinLibrary, List<KotlinLibrary>> = run {
val transitives = allDependencies
transitives.associate { klib ->
klib.library to klib.resolvedDependencies.map { d -> d.library }
}.toMap()
}
private val builtInsDep = allDependencies.find { it.library.isBuiltIns }
class JsFrontEndResult(val jsAnalysisResult: AnalysisResult, val hasErrors: Boolean) {
val moduleDescriptor: ModuleDescriptor
get() = jsAnalysisResult.moduleDescriptor
val bindingContext: BindingContext
get() = jsAnalysisResult.bindingContext
}
lateinit var jsFrontEndResult: JsFrontEndResult
fun runAnalysis(errorPolicy: ErrorTolerancePolicy, analyzer: AbstractAnalyzerWithCompilerReport) {
require(mainModule is MainModule.SourceFiles)
val files = mainModule.files
analyzer.analyzeAndReport(files) {
TopDownAnalyzerFacadeForJSIR.analyzeFiles(
files,
project,
compilerConfiguration,
allDependencies.map { getModuleDescriptor(it.library) },
friendDependencies.map { getModuleDescriptor(it.library) },
analyzer.targetEnvironment,
thisIsBuiltInsModule = builtInModuleDescriptor == null,
customBuiltInsModule = builtInModuleDescriptor
)
}
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val analysisResult = analyzer.analysisResult
if (compilerConfiguration.getBoolean(CommonConfigurationKeys.INCREMENTAL_COMPILATION)) {
/** can throw [IncrementalNextRoundException] */
compareMetadataAndGoToNextICRoundIfNeeded(analysisResult, compilerConfiguration, project, files, errorPolicy.allowErrors)
}
var hasErrors = false
if (analyzer.hasErrors() || analysisResult !is JsAnalysisResult) {
if (!errorPolicy.allowErrors)
throw CompilationErrorException()
else hasErrors = true
}
hasErrors = TopDownAnalyzerFacadeForJSIR.checkForErrors(files, analysisResult.bindingContext, errorPolicy) || hasErrors
jsFrontEndResult = JsFrontEndResult(analysisResult, hasErrors)
}
private val languageVersionSettings: LanguageVersionSettings = compilerConfiguration.languageVersionSettings
private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure")
private var runtimeModule: ModuleDescriptorImpl? = null
// TODO: these are roughly equivalent to KlibResolvedModuleDescriptorsFactoryImpl. Refactor me.
val descriptors = mutableMapOf<KotlinLibrary, ModuleDescriptorImpl>()
fun getModuleDescriptor(current: KotlinLibrary): ModuleDescriptorImpl = descriptors.getOrPut(current) {
val isBuiltIns = current.unresolvedDependencies.isEmpty()
val lookupTracker = compilerConfiguration[CommonConfigurationKeys.LOOKUP_TRACKER] ?: LookupTracker.DO_NOTHING
val md = JsFactories.DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns(
current,
languageVersionSettings,
storageManager,
runtimeModule?.builtIns,
packageAccessHandler = null, // TODO: This is a speed optimization used by Native. Don't bother for now.
lookupTracker = lookupTracker
)
if (isBuiltIns) runtimeModule = md
val dependencies = moduleDependencies.getValue(current).map { getModuleDescriptor(it) }
md.setDependencies(listOf(md) + dependencies)
md
}
val builtInModuleDescriptor =
if (builtInsDep != null)
getModuleDescriptor(builtInsDep.library)
else
null // null in case compiling builtInModule itself
}
private fun getDescriptorForElement(
context: BindingContext,
element: PsiElement
): DeclarationDescriptor = BindingContextUtils.getNotNull(context, BindingContext.DECLARATION_TO_DESCRIPTOR, element)
fun serializeModuleIntoKlib(
moduleName: String,
project: Project,
configuration: CompilerConfiguration,
messageLogger: IrMessageLogger,
bindingContext: BindingContext,
files: List<KtFile>,
klibPath: String,
dependencies: List<KotlinLibrary>,
moduleFragment: IrModuleFragment,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
cleanFiles: List<KotlinFileSerializedData>,
nopack: Boolean,
perFile: Boolean,
containsErrorCode: Boolean = false,
abiVersion: KotlinAbiVersion,
jsOutputName: String?,
) {
assert(files.size == moduleFragment.files.size)
val compatibilityMode = CompatibilityMode(abiVersion)
val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList()
val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false
val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: false
val serializedIr =
JsIrModuleSerializer(
messageLogger,
moduleFragment.irBuiltins,
expectDescriptorToSymbol,
compatibilityMode,
skipExpects = !configuration.expectActualLinker,
normalizeAbsolutePaths = absolutePathNormalization,
sourceBaseDirs = sourceBaseDirs,
signatureClashChecks
).serializedIrModule(moduleFragment)
val moduleDescriptor = moduleFragment.descriptor
val metadataSerializer = KlibMetadataIncrementalSerializer(configuration, project, containsErrorCode)
val incrementalResultsConsumer = configuration.get(JSConfigurationKeys.INCREMENTAL_RESULTS_CONSUMER)
val empty = ByteArray(0)
fun processCompiledFileData(ioFile: File, compiledFile: KotlinFileSerializedData) {
incrementalResultsConsumer?.run {
processPackagePart(ioFile, compiledFile.metadata, empty, empty)
with(compiledFile.irData) {
processIrFile(ioFile, fileData, types, signatures, strings, declarations, bodies, fqName.toByteArray(), debugInfo)
}
}
}
val additionalFiles = mutableListOf<KotlinFileSerializedData>()
for ((ktFile, binaryFile) in files.zip(serializedIr.files)) {
assert(ktFile.virtualFilePath == binaryFile.path) {
"""The Kt and Ir files are put in different order
Kt: ${ktFile.virtualFilePath}
Ir: ${binaryFile.path}
""".trimMargin()
}
val packageFragment = metadataSerializer.serializeScope(ktFile, bindingContext, moduleDescriptor)
val compiledKotlinFile = KotlinFileSerializedData(packageFragment.toByteArray(), binaryFile)
additionalFiles += compiledKotlinFile
processCompiledFileData(VfsUtilCore.virtualToIoFile(ktFile.virtualFile), compiledKotlinFile)
}
val compiledKotlinFiles = (cleanFiles + additionalFiles)
val header = metadataSerializer.serializeHeader(
moduleDescriptor,
compiledKotlinFiles.map { it.irData.fqName }.distinct().sorted(),
emptyList()
).toByteArray()
incrementalResultsConsumer?.run {
processHeader(header)
}
val serializedMetadata =
metadataSerializer.serializedMetadata(
compiledKotlinFiles.groupBy { it.irData.fqName }
.map { (fqn, data) -> fqn to data.sortedBy { it.irData.path }.map { it.metadata } }.toMap(),
header
)
val fullSerializedIr = SerializedIrModule(compiledKotlinFiles.map { it.irData })
val versions = KotlinLibraryVersioning(
abiVersion = compatibilityMode.abiVersion,
libraryVersion = null,
compilerVersion = KotlinCompilerVersion.VERSION,
metadataVersion = KlibMetadataVersion.INSTANCE.toString(),
irVersion = KlibIrVersion.INSTANCE.toString()
)
val properties = Properties().also { p ->
if (jsOutputName != null) {
p.setProperty(KLIB_PROPERTY_JS_OUTPUT_NAME, jsOutputName)
}
if (containsErrorCode) {
p.setProperty(KLIB_PROPERTY_CONTAINS_ERROR_CODE, "true")
}
}
buildKotlinLibrary(
linkDependencies = dependencies,
ir = fullSerializedIr,
metadata = serializedMetadata,
dataFlowGraph = null,
manifestProperties = properties,
moduleName = moduleName,
nopack = nopack,
perFile = perFile,
output = klibPath,
versions = versions,
builtInsPlatform = BuiltInsPlatform.JS
)
}
const val KLIB_PROPERTY_JS_OUTPUT_NAME = "jsOutputName"
private fun KlibMetadataIncrementalSerializer.serializeScope(
ktFile: KtFile,
bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor
): ProtoBuf.PackageFragment {
val memberScope = ktFile.declarations.map { getDescriptorForElement(bindingContext, it) }
return serializePackageFragment(moduleDescriptor, memberScope, ktFile.packageFqName)
}
private fun compareMetadataAndGoToNextICRoundIfNeeded(
analysisResult: AnalysisResult,
config: CompilerConfiguration,
project: Project,
files: List<KtFile>,
allowErrors: Boolean
) {
val nextRoundChecker = config.get(JSConfigurationKeys.INCREMENTAL_NEXT_ROUND_CHECKER) ?: return
val bindingContext = analysisResult.bindingContext
val serializer = KlibMetadataIncrementalSerializer(config, project, allowErrors)
for (ktFile in files) {
val packageFragment = serializer.serializeScope(ktFile, bindingContext, analysisResult.moduleDescriptor)
// to minimize a number of IC rounds, we should inspect all proto for changes first,
// then go to a next round if needed, with all new dirty files
nextRoundChecker.checkProtoChanges(VfsUtilCore.virtualToIoFile(ktFile.virtualFile), packageFragment.toByteArray())
}
if (nextRoundChecker.shouldGoToNextRound()) throw IncrementalNextRoundException()
}
private fun KlibMetadataIncrementalSerializer(configuration: CompilerConfiguration, project: Project, allowErrors: Boolean) =
KlibMetadataIncrementalSerializer(
languageVersionSettings = configuration.languageVersionSettings,
metadataVersion = configuration.metadataVersion,
project = project,
exportKDoc = false,
skipExpects = !configuration.expectActualLinker,
allowErrorTypes = allowErrors
)
private fun Map<IrModuleFragment, KotlinLibrary>.getUniqueNameForEachFragment(): Map<IrModuleFragment, String> {
return this.entries.mapNotNull { (moduleFragment, klib) ->
klib.manifestProperties.getProperty(KLIB_PROPERTY_JS_OUTPUT_NAME)?.let {
moduleFragment to it
}
}.toMap()
}