[klib] Don't pass around metadata serialization closure

Pass the metadata serializer instance instead. This allows to further
reduce code duplication by introducing the common interface
`KlibSingleFileMetadataSerializer` for abstracting away K1 and K2
representation of a source file, as well as reusing
`Fir2KlibMetadataSerializer` across different backends.

KT-64392
This commit is contained in:
Sergej Jaskiewicz
2024-01-22 14:21:20 +01:00
committed by Space Team
parent 5a6f51e474
commit f39335b3f7
23 changed files with 388 additions and 406 deletions
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.pipeline.Fir2KlibMetadataSerializer
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
@@ -470,13 +471,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
sourceModule.getModuleDescriptor(it)
}
val metadataSerializer =
KlibMetadataIncrementalSerializer(
environmentForJS.configuration,
sourceModule.project,
sourceModule.jsFrontEndResult.hasErrors
)
val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter()
generateKLib(
sourceModule,
@@ -486,10 +480,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
icData = icData,
moduleFragment = moduleFragment,
diagnosticReporter = diagnosticsReporter,
builtInsPlatform = if (arguments.wasm) BuiltInsPlatform.WASM else BuiltInsPlatform.JS
) { file ->
metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
}
builtInsPlatform = if (arguments.wasm) BuiltInsPlatform.WASM else BuiltInsPlatform.JS,
)
val messageCollector = environmentForJS.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
reportCollectedDiagnostics(environmentForJS.configuration, diagnosticsReporter, messageCollector)
@@ -562,7 +554,16 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
// This happens because we check the next round before compilation errors.
// Test reproducer: testFileWithConstantRemoved
// Issue: https://youtrack.jetbrains.com/issue/KT-58824/
if (shouldGoToNextIcRound(moduleStructure, analyzedOutput.output, fir2IrActualizedResult)) {
val shouldGoToNextIcRound = shouldGoToNextIcRound(moduleStructure.compilerConfiguration) {
Fir2KlibMetadataSerializer(
moduleStructure.compilerConfiguration,
analyzedOutput.output,
fir2IrActualizedResult,
exportKDoc = false,
produceHeaderKlib = false,
)
}
if (shouldGoToNextIcRound) {
throw IncrementalNextRoundException()
}
}
@@ -22,13 +22,13 @@ import org.jetbrains.kotlin.diagnostics.impl.PendingDiagnosticsCollectorWithSupp
import org.jetbrains.kotlin.fir.BinaryModuleData
import org.jetbrains.kotlin.fir.DependencyListForCliModule
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.backend.Fir2IrConfiguration
import org.jetbrains.kotlin.fir.backend.Fir2IrExtensions
import org.jetbrains.kotlin.fir.backend.Fir2IrVisibilityConverter
import org.jetbrains.kotlin.fir.backend.js.FirJsKotlinMangler
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.pipeline.*
import org.jetbrains.kotlin.fir.serialization.FirKLibSerializerExtension
import org.jetbrains.kotlin.fir.serialization.serializeSingleFirFile
import org.jetbrains.kotlin.fir.session.KlibIcData
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
@@ -41,16 +41,13 @@ import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
import org.jetbrains.kotlin.library.unresolvedDependencies
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.platform.wasm.WasmPlatforms
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.metadataVersion
import org.jetbrains.kotlin.wasm.resolve.WasmJsPlatformAnalyzerServices
import org.jetbrains.kotlin.wasm.resolve.WasmWasiPlatformAnalyzerServices
import java.io.File
import java.nio.file.Paths
inline fun <F> compileModuleToAnalyzedFir(
@@ -253,50 +250,6 @@ fun transformFirToIr(
}
}
private class Fir2KlibSerializer(
moduleStructure: ModulesStructure,
private val firOutputs: List<ModuleCompilerAnalyzedOutput>,
private val fir2IrActualizedResult: Fir2IrActualizedResult
) {
private val firFilesAndSessionsBySourceFile = buildMap {
for (output in firOutputs) {
output.fir.forEach {
put(it.sourceFile!!, it)
}
}
}
private val actualizedExpectDeclarations by lazy {
fir2IrActualizedResult.irActualizedResult?.actualizedExpectDeclarations?.extractFirDeclarations()
}
private val metadataVersion = moduleStructure.compilerConfiguration.metadataVersion()
private val languageVersionSettings = moduleStructure.compilerConfiguration.languageVersionSettings
val sourceFiles: List<KtSourceFile> = firFilesAndSessionsBySourceFile.keys.toList()
fun serializeSingleFirFile(file: KtSourceFile): ProtoBuf.PackageFragment {
val firFile = firFilesAndSessionsBySourceFile[file]
?: error("cannot find FIR file by source file ${file.name} (${file.path})")
val components = fir2IrActualizedResult.components
return serializeSingleFirFile(
firFile,
components.session,
components.scopeSession,
actualizedExpectDeclarations,
FirKLibSerializerExtension(
components.session, components.firProvider, metadataVersion,
ConstValueProviderImpl(components),
allowErrorTypes = false, exportKDoc = false,
components.annotationsFromPluginRegistrar.createAdditionalMetadataProvider()
),
languageVersionSettings,
)
}
}
fun serializeFirKlib(
moduleStructure: ModulesStructure,
firOutputs: List<ModuleCompilerAnalyzedOutput>,
@@ -308,14 +261,20 @@ fun serializeFirKlib(
jsOutputName: String?,
useWasmPlatform: Boolean,
) {
val fir2KlibSerializer = Fir2KlibSerializer(moduleStructure, firOutputs, fir2IrActualizedResult)
val icData = moduleStructure.compilerConfiguration.incrementalDataProvider?.getSerializedData(fir2KlibSerializer.sourceFiles)
val fir2KlibMetadataSerializer = Fir2KlibMetadataSerializer(
moduleStructure.compilerConfiguration,
firOutputs,
fir2IrActualizedResult,
exportKDoc = false,
produceHeaderKlib = false,
)
val icData = moduleStructure.compilerConfiguration.incrementalDataProvider?.getSerializedData(fir2KlibMetadataSerializer.sourceFiles)
serializeModuleIntoKlib(
moduleStructure.compilerConfiguration[CommonConfigurationKeys.MODULE_NAME]!!,
moduleStructure.compilerConfiguration,
diagnosticsReporter,
fir2KlibSerializer.sourceFiles,
fir2KlibMetadataSerializer,
klibPath = outputKlibPath,
moduleStructure.allDependencies,
fir2IrActualizedResult.irModuleFragment,
@@ -325,27 +284,6 @@ fun serializeFirKlib(
containsErrorCode = messageCollector.hasErrors() || diagnosticsReporter.hasErrors,
abiVersion = KotlinAbiVersion.CURRENT, // TODO get from test file data
jsOutputName = jsOutputName,
serializeSingleFile = fir2KlibSerializer::serializeSingleFirFile,
builtInsPlatform = if (useWasmPlatform) BuiltInsPlatform.WASM else BuiltInsPlatform.JS,
)
}
fun shouldGoToNextIcRound(
moduleStructure: ModulesStructure,
firOutputs: List<ModuleCompilerAnalyzedOutput>,
fir2IrActualizedResult: Fir2IrActualizedResult
): Boolean {
val nextRoundChecker = moduleStructure.compilerConfiguration.get(JSConfigurationKeys.INCREMENTAL_NEXT_ROUND_CHECKER) ?: return false
val fir2KlibSerializer = Fir2KlibSerializer(moduleStructure, firOutputs, fir2IrActualizedResult)
for (ktFile in fir2KlibSerializer.sourceFiles) {
val packageFragment = fir2KlibSerializer.serializeSingleFirFile(ktFile)
// 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(File(ktFile.path!!), packageFragment.toByteArray())
}
return nextRoundChecker.shouldGoToNextRound()
}