[JS IR] Allow IC metadata to be freed before the lowering

This commit is contained in:
Alexander Korepanov
2022-10-31 15:38:11 +01:00
committed by Space Team
parent 5b70d39bd0
commit 4aaae5347a
3 changed files with 519 additions and 436 deletions
@@ -5,15 +5,12 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS
@@ -57,7 +54,7 @@ class CacheUpdater(
private val mainArguments: List<String>?,
private val compilerInterfaceFactory: JsIrCompilerICInterfaceFactory
) {
private val stopwatch = StopwatchIC().apply { startNext("Cache initializing and klibs loading") }
private val stopwatch = StopwatchIC().apply { startNext("Loading klibs") }
private val libraries = loadLibraries(allModules)
private val dependencyGraph = buildDependenciesGraph(libraries)
@@ -68,11 +65,6 @@ class CacheUpdater(
private val mainLibraryFile = KotlinLibraryFile(File(mainModule).canonicalPath)
private val mainLibrary = libraries[mainLibraryFile] ?: notFoundIcError("main library", mainLibraryFile)
private val incrementalCaches = libraries.entries.associate { (libFile, lib) ->
val cachePath = cacheMap[lib] ?: notFoundIcError("cache path", KotlinLibraryFile(lib))
libFile to IncrementalCache(lib, cachePath)
}
private val dirtyFileStats = KotlinSourceFileMutableMap<EnumSet<DirtyFileState>>()
init {
@@ -90,9 +82,6 @@ class CacheUpdater(
}
}
private fun getLibIncrementalCache(libFile: KotlinLibraryFile) =
incrementalCaches[libFile] ?: notFoundIcError("incremental cache", libFile)
private fun loadLibraries(allModules: Collection<String>): Map<KotlinLibraryFile, KotlinLibrary> {
val allResolvedDependencies = jsResolveLibraries(
allModules,
@@ -112,6 +101,20 @@ class CacheUpdater(
}
}
private fun <T> mapLibrariesInOrder(f: (KotlinLibraryFile) -> T): List<T> {
val visited = HashSet<KotlinLibrary>(libraries.size)
val result = ArrayList<T>(libraries.size)
fun visitLibrary(lib: KotlinLibrary) {
if (visited.add(lib)) {
dependencyGraph[lib]?.forEach(::visitLibrary)
result += f(KotlinLibraryFile(lib))
}
}
visitLibrary(mainLibrary)
return result
}
private class DirtyFileExports(
override val inverseDependencies: KotlinSourceFileMutableMap<Set<IdSignature>> = KotlinSourceFileMutableMap()
) : KotlinSourceFileExports() {
@@ -155,435 +158,446 @@ class CacheUpdater(
fun isExportedSignaturesUpdated() = newExportedSignatures != oldInverseDependencies.flatSignatures()
}
private fun addFilesWithRemovedDependencies(
modifiedFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>, removedFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>
): KotlinSourceFileMap<KotlinSourceFileMetadata> {
val extraModifiedLibFiles = KotlinSourceFileMutableMap<KotlinSourceFileMetadata>()
fun addDependenciesToExtraModifiedFiles(dependencies: KotlinSourceFileMap<*>, dirtyState: DirtyFileState) {
for ((dependentLib, dependentFiles) in dependencies) {
val dependentCache = incrementalCaches[dependentLib] ?: continue
val alreadyModifiedFiles = modifiedFiles[dependentLib] ?: emptyMap()
val alreadyRemovedFiles = removedFiles[dependentLib] ?: emptyMap()
val extraModifiedFiles by lazy(LazyThreadSafetyMode.NONE) { extraModifiedLibFiles.getOrPutFiles(dependentLib) }
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(dependentLib) }
for (dependentFile in dependentFiles.keys) {
when (dependentFile) {
in alreadyModifiedFiles -> continue
in alreadyRemovedFiles -> continue
in extraModifiedFiles -> continue
else -> {
val dependentMetadata = dependentCache.fetchSourceFileFullMetadata(dependentFile)
extraModifiedFiles[dependentFile] = dependentMetadata
fileStats.addDirtFileStat(dependentFile, dirtyState)
}
}
}
}
}
removedFiles.forEachFile { _, _, removedFileMetadata ->
addDependenciesToExtraModifiedFiles(removedFileMetadata.directDependencies, DirtyFileState.REMOVED_INVERSE_DEPENDS)
addDependenciesToExtraModifiedFiles(removedFileMetadata.inverseDependencies, DirtyFileState.REMOVED_DIRECT_DEPENDS)
}
if (extraModifiedLibFiles.isNotEmpty()) {
extraModifiedLibFiles.copyFilesFrom(modifiedFiles)
return extraModifiedLibFiles
}
return modifiedFiles
}
private fun loadModifiedFiles(): KotlinSourceFileMap<KotlinSourceFileMetadata> {
val removedFilesMetadata = hashMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>()
val modifiedFiles = KotlinSourceFileMap(incrementalCaches.entries.associate { (lib, cache) ->
val (dirtyFiles, removedFiles, newFiles, modifiedConfigFiles) = cache.collectModifiedFiles(configHash)
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(lib) }
newFiles.forEach { fileStats.addDirtFileStat(it, DirtyFileState.ADDED_FILE) }
modifiedConfigFiles.forEach { fileStats.addDirtFileStat(it, DirtyFileState.MODIFIED_CONFIG) }
removedFiles.forEach { fileStats.addDirtFileStat(it.key, DirtyFileState.REMOVED_FILE) }
dirtyFiles.forEach {
if (it.key !in newFiles && it.key !in modifiedConfigFiles) {
fileStats.addDirtFileStat(it.key, DirtyFileState.MODIFIED_IR)
}
}
if (removedFiles.isNotEmpty()) {
removedFilesMetadata[lib] = removedFiles
}
lib to dirtyFiles
})
return addFilesWithRemovedDependencies(modifiedFiles, KotlinSourceFileMap(removedFilesMetadata))
}
private fun collectExportedSymbolsForDirtyFiles(
dirtyFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>
): KotlinSourceFileMutableMap<KotlinSourceFileExports> {
val exportedSymbols = KotlinSourceFileMutableMap<KotlinSourceFileExports>()
for ((libFile, srcFiles) in dirtyFiles) {
val exportedSymbolFiles = HashMap<KotlinSourceFile, KotlinSourceFileExports>(srcFiles.size)
for ((srcFile, srcFileMetadata) in srcFiles) {
val loadingFileExports = DirtyFileExports()
for ((dependentLib, dependentFiles) in srcFileMetadata.inverseDependencies) {
val dependentCache = incrementalCaches[dependentLib] ?: continue
val dirtyLibFiles = dirtyFiles[dependentLib] ?: emptyMap()
for (dependentFile in dependentFiles.keys) {
if (dependentFile !in dirtyLibFiles) {
val dependentSrcFileMetadata = dependentCache.fetchSourceFileFullMetadata(dependentFile)
dependentSrcFileMetadata.directDependencies[libFile, srcFile]?.let {
loadingFileExports.inverseDependencies[dependentLib, dependentFile] = it.keys
loadingFileExports.allExportedSignatures += it.keys
}
}
}
}
exportedSymbolFiles[srcFile] = loadingFileExports
}
if (exportedSymbolFiles.isNotEmpty()) {
exportedSymbols[libFile] = exportedSymbolFiles
}
}
return exportedSymbols
}
private fun KotlinSourceFileMutableMap<DirtyFileMetadata>.getExportedSignaturesAndAddMetadata(
jsIrLinker: JsIrLinker,
irModule: IrModuleFragment,
libFile: KotlinLibraryFile,
dirtySrcFiles: Set<KotlinSourceFile>
): Map<IdSignature, IdSignatureSource> {
val idSignatureToFile = hashMapOf<IdSignature, IdSignatureSource>()
val moduleDeserializer = jsIrLinker.moduleDeserializer(irModule.descriptor)
val incrementalCache = getLibIncrementalCache(libFile)
for (fileDeserializer in moduleDeserializer.fileDeserializers()) {
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val maybeImportedSignatures = HashSet(reachableSignatures.keys)
val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols)
for ((signature, symbol) in implementedSymbols) {
var symbolCanBeExported = maybeImportedSignatures.remove(signature)
resolveFakeOverrideFunction(symbol)?.let { resolvedSignature ->
if (resolvedSignature !in implementedSymbols) {
maybeImportedSignatures.add(resolvedSignature)
}
symbolCanBeExported = true
}
if (symbolCanBeExported) {
idSignatureToFile[signature] = IdSignatureSource(libFile, KotlinSourceFile(fileDeserializer.file), symbol)
}
}
val libSrcFile = KotlinSourceFile(fileDeserializer.file)
if (libSrcFile in dirtySrcFiles) {
val metadata = incrementalCache.fetchSourceFileFullMetadata(libSrcFile)
this[libFile, libSrcFile] = DirtyFileMetadata(maybeImportedSignatures, metadata.directDependencies)
}
}
return idSignatureToFile
}
private fun DirtyFileMetadata.setAllDependencies(
signatureHashCalculator: IdSignatureHashCalculator,
idSignatureToFile: Map<IdSignature, IdSignatureSource>,
updatedMetadata: KotlinSourceFileMap<DirtyFileMetadata>,
libFile: KotlinLibraryFile,
srcFile: KotlinSourceFile
) {
val allImportedSignatures = addParentSignatures(maybeImportedSignatures, idSignatureToFile, libFile, srcFile)
for (importedSignature in allImportedSignatures) {
val (dependencyLib, dependencyFile) = idSignatureToFile[importedSignature] ?: continue
signatureHashCalculator[importedSignature]?.also { signatureHash ->
addDirectDependency(dependencyLib, dependencyFile, importedSignature, signatureHash)
} ?: notFoundIcError("signature $importedSignature hash", dependencyLib, dependencyFile)
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(libFile, srcFile, importedSignature)
}
}
}
private fun rebuildDirtySourceMetadata(
signatureHashCalculator: IdSignatureHashCalculator,
jsIrLinker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtySrcFiles: KotlinSourceFileMap<KotlinSourceFileExports>,
): KotlinSourceFileMap<DirtyFileMetadata> {
val idSignatureToFile = hashMapOf<IdSignature, IdSignatureSource>()
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
for ((lib, irModule) in loadedFragments) {
val libDirtySrcFiles = dirtySrcFiles[lib]?.keys ?: emptySet()
idSignatureToFile += updatedMetadata.getExportedSignaturesAndAddMetadata(jsIrLinker, irModule, lib, libDirtySrcFiles)
}
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
for ((libFile, srcFiles) in updatedMetadata) {
val libDirtySrcFiles = dirtySrcFiles[libFile] ?: continue
for ((srcFile, updatedHeader) in srcFiles) {
val dirtySrcFile = libDirtySrcFiles[srcFile] ?: continue
dirtySrcFile.inverseDependencies.forEachFile { dependentLibFile, dependentSrcFile, signatures ->
signatures.forEach { signature ->
val signatureSrc = idSignatureToFile[signature]
val dependencyLib = signatureSrc?.lib ?: libFile
val dependencyFile = signatureSrc?.src ?: srcFile
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(dependentLibFile, dependentSrcFile, signature)
}
}
}
updatedHeader.setAllDependencies(signatureHashCalculator, idSignatureToFile, updatedMetadata, libFile, srcFile)
}
}
val result = KotlinSourceFileMutableMap<DirtyFileMetadata>()
for ((libFile, sourceFiles) in dirtySrcFiles) {
val incrementalCache = getLibIncrementalCache(libFile)
val srcFileUpdatedMetadata = updatedMetadata[libFile] ?: notFoundIcError("metadata", libFile)
for (srcFile in sourceFiles.keys) {
val srcMetadata = srcFileUpdatedMetadata[srcFile] ?: notFoundIcError("metadata", libFile, srcFile)
incrementalCache.updateSourceFileMetadata(srcFile, srcMetadata)
result[libFile, srcFile] = srcMetadata
}
}
return result
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addNewMetadata(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, oldMetadata: KotlinSourceFileMetadata
) = this[libFile, srcFile] ?: UpdatedDependenciesMetadata(oldMetadata).also {
this[libFile, srcFile] = it
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependenciesWithUpdatedSignatures(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, srcFileMetadata: DirtyFileMetadata
) {
// go through dependencies and collect dependencies with updated signatures
for ((dependencyLibFile, dependencySrcFiles) in srcFileMetadata.directDependencies) {
val dependencyCache = getLibIncrementalCache(dependencyLibFile)
for ((dependencySrcFile, newSignatures) in dependencySrcFiles) {
val dependencySrcMetadata = dependencyCache.fetchSourceFileFullMetadata(dependencySrcFile)
val oldSignatures = dependencySrcMetadata.inverseDependencies[libFile, srcFile] ?: emptySet()
if (oldSignatures == newSignatures) {
continue
}
val newMetadata = addNewMetadata(dependencyLibFile, dependencySrcFile, dependencySrcMetadata)
newMetadata.inverseDependencies[libFile, srcFile] = newSignatures.keys
}
}
}
private inner class CacheUpdaterInternal {
val signatureHashCalculator = IdSignatureHashCalculator()
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependenciesWithRemovedInverseDependencies(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, srcFileMetadata: DirtyFileMetadata
) {
// go through old dependencies and look for removed dependencies
for ((oldDependencyLibFile, oldDependencySrcFiles) in srcFileMetadata.oldDirectDependencies) {
val dependencyCache = incrementalCaches[oldDependencyLibFile] ?: continue
val newDirectDependencyFiles = srcFileMetadata.directDependencies[oldDependencyLibFile] ?: emptyMap()
for (oldDependencySrcFile in oldDependencySrcFiles.keys) {
if (oldDependencySrcFile in newDirectDependencyFiles) {
continue
private val incrementalCaches = libraries.entries.associate { (libFile, lib) ->
val cachePath = cacheMap[lib] ?: notFoundIcError("cache path", KotlinLibraryFile(lib))
libFile to IncrementalCache(lib, cachePath)
}
private fun getLibIncrementalCache(libFile: KotlinLibraryFile) =
incrementalCaches[libFile] ?: notFoundIcError("incremental cache", libFile)
private fun addFilesWithRemovedDependencies(
modifiedFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>, removedFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>
): KotlinSourceFileMap<KotlinSourceFileMetadata> {
val extraModifiedLibFiles = KotlinSourceFileMutableMap<KotlinSourceFileMetadata>()
fun addDependenciesToExtraModifiedFiles(dependencies: KotlinSourceFileMap<*>, dirtyState: DirtyFileState) {
for ((dependentLib, dependentFiles) in dependencies) {
val dependentCache = incrementalCaches[dependentLib] ?: continue
val alreadyModifiedFiles = modifiedFiles[dependentLib] ?: emptyMap()
val alreadyRemovedFiles = removedFiles[dependentLib] ?: emptyMap()
val extraModifiedFiles by lazy(LazyThreadSafetyMode.NONE) { extraModifiedLibFiles.getOrPutFiles(dependentLib) }
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(dependentLib) }
for (dependentFile in dependentFiles.keys) {
when (dependentFile) {
in alreadyModifiedFiles -> continue
in alreadyRemovedFiles -> continue
in extraModifiedFiles -> continue
else -> {
val dependentMetadata = dependentCache.fetchSourceFileFullMetadata(dependentFile)
extraModifiedFiles[dependentFile] = dependentMetadata
fileStats.addDirtFileStat(dependentFile, dirtyState)
}
}
}
}
val dependencySrcMetadata = dependencyCache.fetchSourceFileFullMetadata(oldDependencySrcFile)
if (dependencySrcMetadata.inverseDependencies[libFile, srcFile] != null) {
val newMetadata = addNewMetadata(oldDependencyLibFile, oldDependencySrcFile, dependencySrcMetadata)
newMetadata.inverseDependencies.removeFile(libFile, srcFile)
}
removedFiles.forEachFile { _, _, removedFileMetadata ->
addDependenciesToExtraModifiedFiles(removedFileMetadata.directDependencies, DirtyFileState.REMOVED_INVERSE_DEPENDS)
addDependenciesToExtraModifiedFiles(removedFileMetadata.inverseDependencies, DirtyFileState.REMOVED_DIRECT_DEPENDS)
}
if (extraModifiedLibFiles.isNotEmpty()) {
extraModifiedLibFiles.copyFilesFrom(modifiedFiles)
return extraModifiedLibFiles
}
return modifiedFiles
}
fun loadModifiedFiles(): KotlinSourceFileMap<KotlinSourceFileMetadata> {
val removedFilesMetadata = hashMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>()
val modifiedFiles = KotlinSourceFileMap(incrementalCaches.entries.associate { (lib, cache) ->
val (dirtyFiles, removedFiles, newFiles, modifiedConfigFiles) = cache.collectModifiedFiles(configHash)
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(lib) }
newFiles.forEach { fileStats.addDirtFileStat(it, DirtyFileState.ADDED_FILE) }
modifiedConfigFiles.forEach { fileStats.addDirtFileStat(it, DirtyFileState.MODIFIED_CONFIG) }
removedFiles.forEach { fileStats.addDirtFileStat(it.key, DirtyFileState.REMOVED_FILE) }
dirtyFiles.forEach {
if (it.key !in newFiles && it.key !in modifiedConfigFiles) {
fileStats.addDirtFileStat(it.key, DirtyFileState.MODIFIED_IR)
}
}
if (removedFiles.isNotEmpty()) {
removedFilesMetadata[lib] = removedFiles
}
lib to dirtyFiles
})
return addFilesWithRemovedDependencies(modifiedFiles, KotlinSourceFileMap(removedFilesMetadata))
}
fun collectExportedSymbolsForDirtyFiles(
dirtyFiles: KotlinSourceFileMap<KotlinSourceFileMetadata>
): KotlinSourceFileMutableMap<KotlinSourceFileExports> {
val exportedSymbols = KotlinSourceFileMutableMap<KotlinSourceFileExports>()
for ((libFile, srcFiles) in dirtyFiles) {
val exportedSymbolFiles = HashMap<KotlinSourceFile, KotlinSourceFileExports>(srcFiles.size)
for ((srcFile, srcFileMetadata) in srcFiles) {
val loadingFileExports = DirtyFileExports()
for ((dependentLib, dependentFiles) in srcFileMetadata.inverseDependencies) {
val dependentCache = incrementalCaches[dependentLib] ?: continue
val dirtyLibFiles = dirtyFiles[dependentLib] ?: emptyMap()
for (dependentFile in dependentFiles.keys) {
if (dependentFile !in dirtyLibFiles) {
val dependentSrcFileMetadata = dependentCache.fetchSourceFileFullMetadata(dependentFile)
dependentSrcFileMetadata.directDependencies[libFile, srcFile]?.let {
loadingFileExports.inverseDependencies[dependentLib, dependentFile] = it.keys
loadingFileExports.allExportedSignatures += it.keys
}
}
}
}
exportedSymbolFiles[srcFile] = loadingFileExports
}
if (exportedSymbolFiles.isNotEmpty()) {
exportedSymbols[libFile] = exportedSymbolFiles
}
}
return exportedSymbols
}
private fun KotlinSourceFileMutableMap<DirtyFileMetadata>.getExportedSignaturesAndAddMetadata(
jsIrLinker: JsIrLinker,
irModule: IrModuleFragment,
libFile: KotlinLibraryFile,
dirtySrcFiles: Set<KotlinSourceFile>
): Map<IdSignature, IdSignatureSource> {
val idSignatureToFile = hashMapOf<IdSignature, IdSignatureSource>()
val moduleDeserializer = jsIrLinker.moduleDeserializer(irModule.descriptor)
val incrementalCache = getLibIncrementalCache(libFile)
for (fileDeserializer in moduleDeserializer.fileDeserializers()) {
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val maybeImportedSignatures = HashSet(reachableSignatures.keys)
val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols)
for ((signature, symbol) in implementedSymbols) {
var symbolCanBeExported = maybeImportedSignatures.remove(signature)
resolveFakeOverrideFunction(symbol)?.let { resolvedSignature ->
if (resolvedSignature !in implementedSymbols) {
maybeImportedSignatures.add(resolvedSignature)
}
symbolCanBeExported = true
}
if (symbolCanBeExported) {
idSignatureToFile[signature] = IdSignatureSource(libFile, KotlinSourceFile(fileDeserializer.file), symbol)
}
}
val libSrcFile = KotlinSourceFile(fileDeserializer.file)
if (libSrcFile in dirtySrcFiles) {
val metadata = incrementalCache.fetchSourceFileFullMetadata(libSrcFile)
this[libFile, libSrcFile] = DirtyFileMetadata(maybeImportedSignatures, metadata.directDependencies)
}
}
return idSignatureToFile
}
private fun DirtyFileMetadata.setAllDependencies(
idSignatureToFile: Map<IdSignature, IdSignatureSource>,
updatedMetadata: KotlinSourceFileMap<DirtyFileMetadata>,
libFile: KotlinLibraryFile,
srcFile: KotlinSourceFile
) {
val allImportedSignatures = addParentSignatures(maybeImportedSignatures, idSignatureToFile, libFile, srcFile)
for (importedSignature in allImportedSignatures) {
val (dependencyLib, dependencyFile) = idSignatureToFile[importedSignature] ?: continue
signatureHashCalculator[importedSignature]?.also { signatureHash ->
addDirectDependency(dependencyLib, dependencyFile, importedSignature, signatureHash)
} ?: notFoundIcError("signature $importedSignature hash", dependencyLib, dependencyFile)
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(libFile, srcFile, importedSignature)
}
}
}
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependentsWithUpdatedImports(
signatureHashCalculator: IdSignatureHashCalculator,
libFile: KotlinLibraryFile,
srcFile: KotlinSourceFile,
srcFileMetadata: DirtyFileMetadata
) {
// go through dependent files and check if their imports were modified
for ((dependentLibFile, dependentSrcFiles) in srcFileMetadata.inverseDependencies) {
val dependentCache = incrementalCaches[dependentLibFile] ?: continue
for ((dependentSrcFile, newSignatures) in dependentSrcFiles) {
val dependentSrcMetadata = dependentCache.fetchSourceFileFullMetadata(dependentSrcFile)
val dependentSignatures = dependentSrcMetadata.directDependencies[libFile, srcFile] ?: emptyMap()
when {
dependentSrcMetadata is DirtyFileMetadata -> continue
this[dependentLibFile, dependentSrcFile]?.importedSignaturesModified == true -> continue
dependentSignatures.any { signatureHashCalculator[it.key] != it.value } -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.importedSignaturesModified = true
fun rebuildDirtySourceMetadata(
jsIrLinker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtySrcFiles: KotlinSourceFileMap<KotlinSourceFileExports>,
): KotlinSourceFileMap<DirtyFileMetadata> {
val idSignatureToFile = hashMapOf<IdSignature, IdSignatureSource>()
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
for ((lib, irModule) in loadedFragments) {
val libDirtySrcFiles = dirtySrcFiles[lib]?.keys ?: emptySet()
idSignatureToFile += updatedMetadata.getExportedSignaturesAndAddMetadata(jsIrLinker, irModule, lib, libDirtySrcFiles)
}
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
for ((libFile, srcFiles) in updatedMetadata) {
val libDirtySrcFiles = dirtySrcFiles[libFile] ?: continue
for ((srcFile, updatedHeader) in srcFiles) {
val dirtySrcFile = libDirtySrcFiles[srcFile] ?: continue
dirtySrcFile.inverseDependencies.forEachFile { dependentLibFile, dependentSrcFile, signatures ->
signatures.forEach { signature ->
val signatureSrc = idSignatureToFile[signature]
val dependencyLib = signatureSrc?.lib ?: libFile
val dependencyFile = signatureSrc?.src ?: srcFile
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(dependentLibFile, dependentSrcFile, signature)
}
}
}
dependentSignatures.keys != newSignatures -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWithTo(HashMap(newSignatures.size)) {
signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile)
updatedHeader.setAllDependencies(idSignatureToFile, updatedMetadata, libFile, srcFile)
}
}
val result = KotlinSourceFileMutableMap<DirtyFileMetadata>()
for ((libFile, sourceFiles) in dirtySrcFiles) {
val incrementalCache = getLibIncrementalCache(libFile)
val srcFileUpdatedMetadata = updatedMetadata[libFile] ?: notFoundIcError("metadata", libFile)
for (srcFile in sourceFiles.keys) {
val srcMetadata = srcFileUpdatedMetadata[srcFile] ?: notFoundIcError("metadata", libFile, srcFile)
incrementalCache.updateSourceFileMetadata(srcFile, srcMetadata)
result[libFile, srcFile] = srcMetadata
}
}
return result
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependenciesWithUpdatedSignatures(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, srcFileMetadata: DirtyFileMetadata
) {
// go through dependencies and collect dependencies with updated signatures
for ((dependencyLibFile, dependencySrcFiles) in srcFileMetadata.directDependencies) {
val dependencyCache = getLibIncrementalCache(dependencyLibFile)
for ((dependencySrcFile, newSignatures) in dependencySrcFiles) {
val dependencySrcMetadata = dependencyCache.fetchSourceFileFullMetadata(dependencySrcFile)
val oldSignatures = dependencySrcMetadata.inverseDependencies[libFile, srcFile] ?: emptySet()
if (oldSignatures == newSignatures) {
continue
}
val newMetadata = addNewMetadata(dependencyLibFile, dependencySrcFile, dependencySrcMetadata)
newMetadata.inverseDependencies[libFile, srcFile] = newSignatures.keys
}
}
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependenciesWithRemovedInverseDependencies(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, srcFileMetadata: DirtyFileMetadata
) {
// go through old dependencies and look for removed dependencies
for ((oldDependencyLibFile, oldDependencySrcFiles) in srcFileMetadata.oldDirectDependencies) {
val dependencyCache = incrementalCaches[oldDependencyLibFile] ?: continue
val newDirectDependencyFiles = srcFileMetadata.directDependencies[oldDependencyLibFile] ?: emptyMap()
for (oldDependencySrcFile in oldDependencySrcFiles.keys) {
if (oldDependencySrcFile in newDirectDependencyFiles) {
continue
}
val dependencySrcMetadata = dependencyCache.fetchSourceFileFullMetadata(oldDependencySrcFile)
if (dependencySrcMetadata.inverseDependencies[libFile, srcFile] != null) {
val newMetadata = addNewMetadata(oldDependencyLibFile, oldDependencySrcFile, dependencySrcMetadata)
newMetadata.inverseDependencies.removeFile(libFile, srcFile)
}
}
}
}
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependentsWithUpdatedImports(
libFile: KotlinLibraryFile,
srcFile: KotlinSourceFile,
srcFileMetadata: DirtyFileMetadata
) {
// go through dependent files and check if their imports were modified
for ((dependentLibFile, dependentSrcFiles) in srcFileMetadata.inverseDependencies) {
val dependentCache = incrementalCaches[dependentLibFile] ?: continue
for ((dependentSrcFile, newSignatures) in dependentSrcFiles) {
val dependentSrcMetadata = dependentCache.fetchSourceFileFullMetadata(dependentSrcFile)
val dependentSignatures = dependentSrcMetadata.directDependencies[libFile, srcFile] ?: emptyMap()
when {
dependentSrcMetadata is DirtyFileMetadata -> continue
this[dependentLibFile, dependentSrcFile]?.importedSignaturesModified == true -> continue
dependentSignatures.any { signatureHashCalculator[it.key] != it.value } -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.importedSignaturesModified = true
}
dependentSignatures.keys != newSignatures -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWithTo(HashMap(newSignatures.size)) {
signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile)
}
}
}
}
}
}
}
private fun collectFilesWithModifiedExportsAndImports(
signatureHashCalculator: IdSignatureHashCalculator,
loadedDirtyFiles: KotlinSourceFileMap<DirtyFileMetadata>
): KotlinSourceFileMap<UpdatedDependenciesMetadata> {
val filesWithModifiedExportsAndImports = KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>()
fun collectFilesWithModifiedExportsAndImports(
loadedDirtyFiles: KotlinSourceFileMap<DirtyFileMetadata>
): KotlinSourceFileMap<UpdatedDependenciesMetadata> {
val filesWithModifiedExportsAndImports = KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>()
loadedDirtyFiles.forEachFile { libFile, srcFile, srcFileMetadata ->
filesWithModifiedExportsAndImports.addDependenciesWithUpdatedSignatures(libFile, srcFile, srcFileMetadata)
filesWithModifiedExportsAndImports.addDependenciesWithRemovedInverseDependencies(libFile, srcFile, srcFileMetadata)
filesWithModifiedExportsAndImports.addDependentsWithUpdatedImports(signatureHashCalculator, libFile, srcFile, srcFileMetadata)
loadedDirtyFiles.forEachFile { libFile, srcFile, srcFileMetadata ->
filesWithModifiedExportsAndImports.addDependenciesWithUpdatedSignatures(libFile, srcFile, srcFileMetadata)
filesWithModifiedExportsAndImports.addDependenciesWithRemovedInverseDependencies(libFile, srcFile, srcFileMetadata)
filesWithModifiedExportsAndImports.addDependentsWithUpdatedImports(libFile, srcFile, srcFileMetadata)
}
return filesWithModifiedExportsAndImports
}
return filesWithModifiedExportsAndImports
}
fun collectFilesToRebuildSignatures(
filesWithModifiedExports: KotlinSourceFileMap<UpdatedDependenciesMetadata>
): KotlinSourceFileMap<KotlinSourceFileExports> {
val libFilesToRebuild = KotlinSourceFileMutableMap<KotlinSourceFileExports>()
private fun collectFilesToRebuildSignatures(
filesWithModifiedExports: KotlinSourceFileMap<UpdatedDependenciesMetadata>
): KotlinSourceFileMap<KotlinSourceFileExports> {
val libFilesToRebuild = KotlinSourceFileMutableMap<KotlinSourceFileExports>()
for ((libFile, srcFiles) in filesWithModifiedExports) {
val filesToRebuild by lazy(LazyThreadSafetyMode.NONE) { libFilesToRebuild.getOrPutFiles(libFile) }
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(libFile) }
val cache = getLibIncrementalCache(libFile)
for ((libFile, srcFiles) in filesWithModifiedExports) {
val filesToRebuild by lazy(LazyThreadSafetyMode.NONE) { libFilesToRebuild.getOrPutFiles(libFile) }
val fileStats by lazy(LazyThreadSafetyMode.NONE) { dirtyFileStats.getOrPutFiles(libFile) }
val cache = getLibIncrementalCache(libFile)
for ((srcFile, srcFileMetadata) in srcFiles) {
val isExportedSignatureUpdated = srcFileMetadata.isExportedSignaturesUpdated()
if (isExportedSignatureUpdated || srcFileMetadata.importedSignaturesModified) {
// if exported signatures or imported inline functions were modified - rebuild
filesToRebuild[srcFile] = srcFileMetadata
if (isExportedSignatureUpdated) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_EXPORTS)
for ((srcFile, srcFileMetadata) in srcFiles) {
val isExportedSignatureUpdated = srcFileMetadata.isExportedSignaturesUpdated()
if (isExportedSignatureUpdated || srcFileMetadata.importedSignaturesModified) {
// if exported signatures or imported inline functions were modified - rebuild
filesToRebuild[srcFile] = srcFileMetadata
if (isExportedSignatureUpdated) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_EXPORTS)
}
if (srcFileMetadata.importedSignaturesModified) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_IMPORTS)
}
} else {
// if signatures and inline functions are the same - just update cache metadata
cache.updateSourceFileMetadata(srcFile, srcFileMetadata)
}
if (srcFileMetadata.importedSignaturesModified) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_IMPORTS)
}
}
return libFilesToRebuild
}
fun updateStdlibIntrinsicDependencies(
linker: JsIrLinker,
mainModule: IrModuleFragment,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
) {
val stdlibDescriptor = mainModule.descriptor.builtIns.builtInsModule
val (stdlibFile, stdlibIr) = loadedFragments.entries.find {
it.value.descriptor === stdlibDescriptor
} ?: notFoundIcError("stdlib loaded fragment")
val stdlibDirtyFiles = dirtyFiles[stdlibFile] ?: return
signatureHashCalculator.updateInlineFunctionTransitiveHashes(listOf(stdlibIr))
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
val idSignatureToFile = updatedMetadata.getExportedSignaturesAndAddMetadata(linker, stdlibIr, stdlibFile, stdlibDirtyFiles)
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
updatedHeader.setAllDependencies(idSignatureToFile, updatedMetadata, libFile, srcFile)
}
val incrementalCache = getLibIncrementalCache(stdlibFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
if (libFile != stdlibFile) {
icError("unexpected lib while parsing stdlib dependencies", libFile, srcFile)
}
val cachedHeader = incrementalCache.fetchSourceFileFullMetadata(srcFile)
val needUpdate = when {
!updatedHeader.directDependencies.allFiles { lib, file, dependencies ->
cachedHeader.directDependencies[lib, file]?.keys?.containsAll(dependencies.keys) ?: dependencies.isEmpty()
} -> true
!updatedHeader.inverseDependencies.allFiles { lib, file, invDependencies ->
cachedHeader.inverseDependencies[lib, file]?.containsAll(invDependencies) ?: invDependencies.isEmpty()
} -> true
else -> false
}
if (needUpdate) {
cachedHeader.directDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.directDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.directDependencies[lib, file] = HashMap(dependencies)
}
}
} else {
// if signatures and inline functions are the same - just update cache metadata
cache.updateSourceFileMetadata(srcFile, srcFileMetadata)
cachedHeader.inverseDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.inverseDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.inverseDependencies[lib, file] = HashSet(dependencies)
}
}
incrementalCache.updateSourceFileMetadata(srcFile, updatedHeader)
}
}
}
return libFilesToRebuild
fun buildCacheArtifacts(
jsIrLinker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>
): Map<KotlinLibraryFile, IncrementalCacheArtifact> {
return mapLibrariesInOrder { libFile ->
val incrementalCache = getLibIncrementalCache(libFile)
val libFragment = loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile)
val moduleDeserializer = jsIrLinker.moduleDeserializer(libFragment.descriptor)
val signatureToIndexMapping = moduleDeserializer.fileDeserializers().associate {
KotlinSourceFile(it.file) to it.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
}
libFile to incrementalCache.buildIncrementalCacheArtifact(signatureToIndexMapping)
}.toMap()
}
}
private fun buildModuleArtifactsAndCommitCache(
jsIrLinker: JsIrLinker,
private fun commitCacheAndBuildModuleArtifacts(
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
rebuiltFileFragments: List<Pair<IrFile, JsIrProgramFragment>>
): List<ModuleArtifact> {
stopwatch.startNext("Cache committing")
stopwatch.startNext("Incremental cache - committing artifacts")
val fragmentToLibName = loadedFragments.entries.associate { it.value to it.key }
val rebuiltSrcFiles = rebuiltFileFragments.groupBy {
fragmentToLibName[it.first.module] ?: notFoundIcError("loaded fragment lib name", srcFile = KotlinSourceFile(it.first))
}
val visited = hashSetOf<KotlinLibrary>()
val artifacts = mutableListOf<ModuleArtifact>()
fun addArtifact(lib: KotlinLibrary) {
if (visited.add(lib)) {
dependencyGraph[lib]?.forEach(::addArtifact)
val artifacts = mapLibrariesInOrder { libFile ->
val libFragment = loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile)
val libRebuiltFiles = rebuiltSrcFiles[libFile]?.associate { KotlinSourceFile(it.first) to it.second } ?: emptyMap()
val lib = libraries[libFile] ?: notFoundIcError("library", libFile)
val incrementalCacheArtifact = incrementalCacheArtifacts[libFile] ?: notFoundIcError("cache artifact", libFile)
val libFile = KotlinLibraryFile(lib)
val incrementalCache = getLibIncrementalCache(libFile)
val libFragment = loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile)
val libRebuiltFiles = rebuiltSrcFiles[libFile]?.associate { KotlinSourceFile(it.first) to it.second } ?: emptyMap()
val moduleDeserializer = jsIrLinker.moduleDeserializer(libFragment.descriptor)
val signatureToIndexMapping = moduleDeserializer.fileDeserializers().associate {
KotlinSourceFile(it.file) to it.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
}
artifacts += incrementalCache.buildModuleArtifactAndCommitCache(
moduleName = libFragment.name.asString(),
externalModuleName = lib.jsOutputName,
rebuiltFileFragments = libRebuiltFiles,
signatureToIndexMapping = signatureToIndexMapping
)
}
incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
moduleName = libFragment.name.asString(),
externalModuleName = lib.jsOutputName,
rebuiltFileFragments = libRebuiltFiles,
)
}
addArtifact(mainLibrary)
stopwatch.stop()
return artifacts
}
private fun updateStdlibIntrinsicDependencies(
signatureHashCalculator: IdSignatureHashCalculator,
linker: JsIrLinker,
mainModule: IrModuleFragment,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
) {
val stdlibDescriptor = mainModule.descriptor.builtIns.builtInsModule
val (stdlibFile, stdlibIr) = loadedFragments.entries.find {
it.value.descriptor === stdlibDescriptor
} ?: notFoundIcError("stdlib loaded fragment")
val stdlibDirtyFiles = dirtyFiles[stdlibFile] ?: return
signatureHashCalculator.updateInlineFunctionTransitiveHashes(listOf(stdlibIr))
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
val idSignatureToFile = updatedMetadata.getExportedSignaturesAndAddMetadata(linker, stdlibIr, stdlibFile, stdlibDirtyFiles)
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
updatedHeader.setAllDependencies(signatureHashCalculator, idSignatureToFile, updatedMetadata, libFile, srcFile)
}
val incrementalCache = getLibIncrementalCache(stdlibFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
if (libFile != stdlibFile) {
icError("unexpected lib while parsing stdlib dependencies", libFile, srcFile)
}
val cachedHeader = incrementalCache.fetchSourceFileFullMetadata(srcFile)
val needUpdate = when {
!updatedHeader.directDependencies.allFiles { lib, file, dependencies ->
cachedHeader.directDependencies[lib, file]?.keys?.containsAll(dependencies.keys) ?: dependencies.isEmpty()
} -> true
!updatedHeader.inverseDependencies.allFiles { lib, file, invDependencies ->
cachedHeader.inverseDependencies[lib, file]?.containsAll(invDependencies) ?: invDependencies.isEmpty()
} -> true
else -> false
}
if (needUpdate) {
cachedHeader.directDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.directDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.directDependencies[lib, file] = HashMap(dependencies)
}
}
cachedHeader.inverseDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.inverseDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.inverseDependencies[lib, file] = HashSet(dependencies)
}
}
incrementalCache.updateSourceFileMetadata(srcFile, updatedHeader)
}
}
}
private fun compileDirtyFiles(
compilerForIC: JsIrCompilerICInterface,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
@@ -604,7 +618,7 @@ class CacheUpdater(
}
private data class IrForDirtyFilesAndCompiler(
val linker: JsIrLinker,
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
val loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
val dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>,
val irCompiler: JsIrCompilerICInterface
@@ -613,10 +627,14 @@ class CacheUpdater(
private fun loadIrForDirtyFilesAndInitCompiler(): IrForDirtyFilesAndCompiler {
dirtyFileStats.clear()
stopwatch.startNext("Incremental cache - initializing")
val updater = CacheUpdaterInternal()
stopwatch.startNext("Modified files - checking hashes and collecting")
val modifiedFiles = loadModifiedFiles()
val modifiedFiles = updater.loadModifiedFiles()
stopwatch.startNext("Modified files - collecting exported signatures")
val dirtyFileExports = collectExportedSymbolsForDirtyFiles(modifiedFiles)
val dirtyFileExports = updater.collectExportedSymbolsForDirtyFiles(modifiedFiles)
stopwatch.startNext("Modified files - loading and linking IR")
val jsIrLinkerLoader = JsIrLinkerLoader(compilerConfiguration, mainLibrary, dependencyGraph, irFactory())
@@ -625,20 +643,18 @@ class CacheUpdater(
var iterations = 0
var lastDirtyFiles: KotlinSourceFileMap<KotlinSourceFileExports> = dirtyFileExports
val hashCalculator = IdSignatureHashCalculator()
while (true) {
stopwatch.startNext("Dependencies ($iterations) - calculating transitive hashes for inline functions")
hashCalculator.updateInlineFunctionTransitiveHashes(loadedIr.loadedFragments.values)
updater.signatureHashCalculator.updateInlineFunctionTransitiveHashes(loadedIr.loadedFragments.values)
stopwatch.startNext("Dependencies ($iterations) - updating a dependency graph")
val dirtyMetadata = rebuildDirtySourceMetadata(hashCalculator, loadedIr.linker, loadedIr.loadedFragments, lastDirtyFiles)
val dirtyMetadata = updater.rebuildDirtySourceMetadata(loadedIr.linker, loadedIr.loadedFragments, lastDirtyFiles)
stopwatch.startNext("Dependencies ($iterations) - collecting files with updated exports and imports")
val filesWithModifiedExportsOrImports = collectFilesWithModifiedExportsAndImports(hashCalculator, dirtyMetadata)
val filesWithModifiedExportsOrImports = updater.collectFilesWithModifiedExportsAndImports(dirtyMetadata)
stopwatch.startNext("Dependencies ($iterations) - collecting exported signatures for files with updated exports and imports")
val filesToRebuild = collectFilesToRebuildSignatures(filesWithModifiedExportsOrImports)
val filesToRebuild = updater.collectFilesToRebuildSignatures(filesWithModifiedExportsOrImports)
if (filesToRebuild.isEmpty()) {
break
@@ -668,18 +684,21 @@ class CacheUpdater(
val dirtyFiles = dirtyFileExports.entries.associateTo(HashMap(dirtyFileExports.size)) { it.key to HashSet(it.value.keys) }
stopwatch.startNext("Processing IR - updating intrinsics and builtins dependencies")
updateStdlibIntrinsicDependencies(hashCalculator, loadedIr.linker, mainModule, loadedIr.loadedFragments, dirtyFiles)
updater.updateStdlibIntrinsicDependencies(loadedIr.linker, mainModule, loadedIr.loadedFragments, dirtyFiles)
stopwatch.startNext("Incremental cache - building artifacts")
val incrementalCachesArtifacts = updater.buildCacheArtifacts(loadedIr.linker, loadedIr.loadedFragments)
stopwatch.stop()
return IrForDirtyFilesAndCompiler(loadedIr.linker, loadedIr.loadedFragments, dirtyFiles, compilerForIC)
return IrForDirtyFilesAndCompiler(incrementalCachesArtifacts, loadedIr.loadedFragments, dirtyFiles, compilerForIC)
}
fun actualizeCaches(): List<ModuleArtifact> {
val (irLinker, irFragments, dirtyFiles, irCompiler) = loadIrForDirtyFilesAndInitCompiler()
val (incrementalCachesArtifacts, irFragments, dirtyFiles, irCompiler) = loadIrForDirtyFilesAndInitCompiler()
val rebuiltFragments = compileDirtyFiles(irCompiler, irFragments, dirtyFiles)
return buildModuleArtifactsAndCommitCache(irLinker, irFragments, rebuiltFragments)
return commitCacheAndBuildModuleArtifacts(incrementalCachesArtifacts, irFragments, rebuiltFragments)
}
}
@@ -6,23 +6,21 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.backend.common.serialization.IdSignatureDeserializer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstSerializer
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.impl.javaFile
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.protobuf.CodedOutputStream
import java.io.BufferedOutputStream
import java.io.File
class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
internal class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
companion object {
private const val CACHE_HEADER = "ic.header.bin"
private const val BINARY_AST_SUFFIX = "ast.bin"
private const val METADATA_SUFFIX = "metadata.bin"
private const val METADATA_TMP_SUFFIX = "metadata.tmp.bin"
}
private var forceRebuildJs = false
@@ -84,29 +82,11 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
}
}
fun buildModuleArtifactAndCommitCache(
moduleName: String,
externalModuleName: String?,
rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragment>,
signatureToIndexMapping: Map<KotlinSourceFile, Map<IdSignature, Int>>
): ModuleArtifact {
val serializer = JsIrAstSerializer()
fun buildIncrementalCacheArtifact(signatureToIndexMapping: Map<KotlinSourceFile, Map<IdSignature, Int>>): IncrementalCacheArtifact {
val fileArtifacts = kotlinLibraryHeader.sourceFiles.map { srcFile ->
val binaryAstFile = srcFile.getCacheFile(BINARY_AST_SUFFIX)
val rebuiltFileFragment = rebuiltFileFragments[srcFile]
if (rebuiltFileFragment != null) {
binaryAstFile.recreate()
BufferedOutputStream(binaryAstFile.outputStream()).use { bufferedOutStream ->
serializer.serialize(rebuiltFileFragment, bufferedOutStream)
}
}
commitSourceFileMetadata(srcFile, signatureToIndexMapping[srcFile] ?: emptyMap())
SrcFileArtifact(srcFile.path, rebuiltFileFragment, binaryAstFile)
commitSourceFileMetadata(srcFile.getCacheFile(BINARY_AST_SUFFIX), srcFile, signatureToIndexMapping[srcFile] ?: emptyMap())
}
return ModuleArtifact(moduleName, fileArtifacts, cacheDir, forceRebuildJs, externalModuleName)
return IncrementalCacheArtifact(cacheDir, forceRebuildJs, fileArtifacts)
}
data class ModifiedFiles(
@@ -125,6 +105,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
isConfigModified = cacheHeader.configHash != ICHash()
CacheHeader(klibFileHash, configHash)
}
cacheHeader.klibFileHash != klibFileHash -> CacheHeader(klibFileHash, configHash)
else -> return ModifiedFiles()
}
@@ -228,15 +209,19 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
} ?: KotlinSourceFileMetadataNotExist
}
private fun commitSourceFileMetadata(srcFile: KotlinSourceFile, signatureToIndexMapping: Map<IdSignature, Int>) {
private fun commitSourceFileMetadata(
binaryAstFile: File,
srcFile: KotlinSourceFile,
signatureToIndexMapping: Map<IdSignature, Int>
): SourceFileCacheArtifact {
val headerCacheFile = srcFile.getCacheFile(METADATA_SUFFIX)
val sourceFileMetadata = kotlinLibrarySourceFileMetadata[srcFile] ?: return
val sourceFileMetadata = kotlinLibrarySourceFileMetadata[srcFile]
?: return SourceFileCacheArtifact.DoNotChangeMetadata(srcFile, binaryAstFile)
if (sourceFileMetadata.isEmpty()) {
headerCacheFile.delete()
return
return SourceFileCacheArtifact.RemoveMetadata(srcFile, binaryAstFile, headerCacheFile)
}
if (sourceFileMetadata is KotlinSourceFileMetadataFromDisk) {
return
return SourceFileCacheArtifact.DoNotChangeMetadata(srcFile, binaryAstFile)
}
val signatureToIndexMappingSaved = signatureToIndexMappingFromMetadata[srcFile] ?: emptyMap()
@@ -270,9 +255,11 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
}
}
headerCacheFile.useCodedOutput {
val tmpCacheFile = srcFile.getCacheFile(METADATA_TMP_SUFFIX)
tmpCacheFile.useCodedOutput {
writeDirectDependencies(sourceFileMetadata.directDependencies)
writeInverseDependencies(sourceFileMetadata.inverseDependencies)
}
return SourceFileCacheArtifact.CommitMetadata(srcFile, binaryAstFile, headerCacheFile, tmpCacheFile)
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2022 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.ic
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstSerializer
import java.io.BufferedOutputStream
import java.io.File
internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) {
abstract fun commitMetadata()
fun commitBinaryAst(fragment: JsIrProgramFragment, serializer: JsIrAstSerializer) {
binaryAstFile.recreate()
BufferedOutputStream(binaryAstFile.outputStream()).use { bufferedOutStream ->
serializer.serialize(fragment, bufferedOutStream)
}
}
class DoNotChangeMetadata(srcFile: KotlinSourceFile, binaryAstFile: File) : SourceFileCacheArtifact(srcFile, binaryAstFile) {
override fun commitMetadata() {}
}
class CommitMetadata(
srcFile: KotlinSourceFile,
binaryAstFile: File,
private val metadataFile: File,
private val tmpMetadataFile: File
) : SourceFileCacheArtifact(srcFile, binaryAstFile) {
override fun commitMetadata() {
metadataFile.delete()
if (!tmpMetadataFile.renameTo(metadataFile)) {
tmpMetadataFile.copyTo(metadataFile, true)
tmpMetadataFile.delete()
}
}
}
class RemoveMetadata(
srcFile: KotlinSourceFile,
binaryAstFile: File,
private val metadataFile: File
) : SourceFileCacheArtifact(srcFile, binaryAstFile) {
override fun commitMetadata() {
metadataFile.delete()
}
}
}
internal class IncrementalCacheArtifact(
private val artifactsDir: File,
private val forceRebuildJs: Boolean,
private val srcCacheActions: List<SourceFileCacheArtifact>
) {
fun buildModuleArtifactAndCommitCache(
moduleName: String,
externalModuleName: String?,
rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragment>,
): ModuleArtifact {
val serializer = JsIrAstSerializer()
val fileArtifacts = srcCacheActions.map { srcFileAction ->
val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile]
if (rebuiltFileFragment != null) {
srcFileAction.commitBinaryAst(rebuiltFileFragment, serializer)
}
srcFileAction.commitMetadata()
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment, srcFileAction.binaryAstFile)
}
return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName)
}
}