[klib] Deduplicate KLIB serialization logic in K/JS and K/N

Introduce the new `serializeModuleIntoKlib` function that all KLIB-based
backends can use to serialize a module to a KLIB.

^KT-64392 Fixed
This commit is contained in:
Sergej Jaskiewicz
2024-01-19 19:19:08 +01:00
committed by Space Team
parent c335cd0b1e
commit 93a1ceae75
8 changed files with 348 additions and 310 deletions
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibCheckers
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.*
@@ -57,6 +56,7 @@ import org.jetbrains.kotlin.library.impl.buildKotlinLibrary
import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
import org.jetbrains.kotlin.library.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.IncrementalNextRoundException
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
@@ -100,8 +100,6 @@ private val CompilerConfiguration.metadataVersion
internal val SerializedIrFile.fileMetadata: ByteArray
get() = backendSpecificMetadata ?: error("Expect file caches to have backendSpecificMetadata, but '$path' doesn't")
class KotlinFileSerializedData(val metadata: ByteArray, val irData: SerializedIrFile)
fun generateKLib(
depsDescriptors: ModulesStructure,
outputKlibPath: String,
@@ -624,100 +622,72 @@ fun serializeModuleIntoKlib(
builtInsPlatform: BuiltInsPlatform = BuiltInsPlatform.JS,
serializeSingleFile: (KtSourceFile) -> ProtoBuf.PackageFragment
) {
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] ?: true
val moduleExportedNames = moduleFragment.collectExportedNames()
val irDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings)
if (builtInsPlatform == BuiltInsPlatform.JS) {
val cleanFilesIrData = cleanFiles.map { it.irData }
JsKlibCheckers.check(cleanFilesIrData, moduleFragment, moduleExportedNames, irDiagnosticReporter, configuration)
}
val serializedIr =
JsIrModuleSerializer(
irDiagnosticReporter,
moduleFragment.irBuiltins,
compatibilityMode,
normalizeAbsolutePaths = absolutePathNormalization,
sourceBaseDirs = sourceBaseDirs,
configuration.languageVersionSettings,
signatureClashChecks,
) { JsIrFileMetadata(moduleExportedNames[it]?.values?.toSmartList() ?: emptyList()) }.serializedIrModule(moduleFragment)
val moduleDescriptor = moduleFragment.descriptor
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(),
fileMetadata,
debugInfo,
)
val serializerOutput = serializeModuleIntoKlib(
moduleName = moduleFragment.name.asString(),
irModuleFragment = moduleFragment,
configuration = configuration,
diagnosticReporter = diagnosticReporter,
sourceFiles = files,
compatibilityMode = CompatibilityMode(abiVersion),
cleanFiles = cleanFiles,
dependencies = dependencies,
createModuleSerializer = {
irDiagnosticReporter,
irBuiltins,
compatibilityMode,
normalizeAbsolutePaths,
sourceBaseDirs,
languageVersionSettings,
shouldCheckSignaturesOnUniqueness,
->
JsIrModuleSerializer(
irDiagnosticReporter,
irBuiltins,
compatibilityMode,
normalizeAbsolutePaths,
sourceBaseDirs,
languageVersionSettings,
shouldCheckSignaturesOnUniqueness,
) { JsIrFileMetadata(moduleExportedNames[it]?.values?.toSmartList() ?: emptyList()) }
},
serializeFileMetadata = { serializeSingleFile(it) to FqName.ROOT },
runKlibCheckers = { irModuleFragment, irDiagnosticReporter, compilerConfiguration ->
if (builtInsPlatform == BuiltInsPlatform.JS) {
val cleanFilesIrData = cleanFiles.map { it.irData ?: error("Metadata-only KLIBs are not supported in Kotlin/JS") }
JsKlibCheckers.check(cleanFilesIrData, irModuleFragment, moduleExportedNames, irDiagnosticReporter, compilerConfiguration)
}
}
}
},
processCompiledFileData = { ioFile, compiledFile ->
incrementalResultsConsumer?.run {
processPackagePart(ioFile, compiledFile.metadata, empty, empty)
with(compiledFile.irData!!) {
processIrFile(
ioFile,
fileData,
types,
signatures,
strings,
declarations,
bodies,
fqName.toByteArray(),
fileMetadata,
debugInfo,
)
}
}
},
processKlibHeader = {
incrementalResultsConsumer?.processHeader(it)
},
)
val additionalFiles = mutableListOf<KotlinFileSerializedData>()
for ((ktSourceFile, binaryFile) in files.zip(serializedIr.files)) {
assert(ktSourceFile.path == binaryFile.path) {
"""The Kt and Ir files are put in different order
Kt: ${ktSourceFile.path}
Ir: ${binaryFile.path}
""".trimMargin()
}
val packageFragment = serializeSingleFile(ktSourceFile)
val compiledKotlinFile = KotlinFileSerializedData(packageFragment.toByteArray(), binaryFile)
additionalFiles += compiledKotlinFile
val ioFile = ktSourceFile.toIoFileOrNull()
assert(ioFile != null) {
"No file found for source ${ktSourceFile.path}"
}
processCompiledFileData(ioFile!!, compiledKotlinFile)
}
val compiledKotlinFiles = cleanFiles + additionalFiles
val header = serializeKlibHeader(
configuration.languageVersionSettings, moduleDescriptor,
compiledKotlinFiles.map { it.irData.fqName }.distinct().sorted(),
emptyList()
).toByteArray()
incrementalResultsConsumer?.run {
processHeader(header)
}
val serializedMetadata =
makeSerializedKlibMetadata(
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 fullSerializedIr = serializerOutput.serializedIr ?: error("Metadata-only KLIBs are not supported in Kotlin/JS")
val versions = KotlinLibraryVersioning(
abiVersion = compatibilityMode.abiVersion,
abiVersion = abiVersion,
libraryVersion = null,
compilerVersion = KotlinCompilerVersion.VERSION,
metadataVersion = KlibMetadataVersion.INSTANCE.toString(),
@@ -737,9 +707,9 @@ fun serializeModuleIntoKlib(
}
buildKotlinLibrary(
linkDependencies = dependencies,
linkDependencies = serializerOutput.neededLibraries,
ir = fullSerializedIr,
metadata = serializedMetadata,
metadata = serializerOutput.serializedMetadata ?: error("expected serialized metadata"),
dataFlowGraph = null,
manifestProperties = properties,
moduleName = moduleName,
@@ -809,13 +779,6 @@ private fun Map<IrModuleFragment, KotlinLibrary>.getUniqueNameForEachFragment():
}.toMap()
}
fun KtSourceFile.toIoFileOrNull(): File? = when (this) {
is KtIoFileSourceFile -> file
is KtVirtualFileSourceFile -> VfsUtilCore.virtualToIoFile(virtualFile)
is KtPsiSourceFile -> VfsUtilCore.virtualToIoFile(psiFile.virtualFile)
else -> path?.let(::File)
}
fun IncrementalDataProvider.getSerializedData(newSources: List<KtSourceFile>): List<KotlinFileSerializedData> {
val nonCompiledSources = newSources.associateBy { it.toIoFileOrNull()!! }
val compiledIrFiles = serializedIrFiles