All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.ir.backend.js.ic.IncrementalCache.kt Maven / Gradle / Ivy
/*
* 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.backend.common.serialization.IdSignatureDeserializer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst
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.File
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 var forceRebuildJs = false
private val cacheDir = File(cachePath)
private val signatureToIndexMappingFromMetadata = mutableMapOf>()
private val libraryFile = KotlinLibraryFile(library)
class CacheHeader(val klibFileHash: ICHash = ICHash(), val configHash: ICHash = ICHash()) {
fun toProtoStream(out: CodedOutputStream) {
klibFileHash.toProtoStream(out)
configHash.toProtoStream(out)
}
companion object {
fun fromProtoStream(input: CodedInputStream): CacheHeader {
val klibFileHash = ICHash.fromProtoStream(input)
val configHash = ICHash.fromProtoStream(input)
return CacheHeader(klibFileHash, configHash)
}
}
}
private var cacheHeader = File(cacheDir, CACHE_HEADER).useCodedInputIfExists {
CacheHeader.fromProtoStream(this)
} ?: CacheHeader()
private fun loadCachedFingerprints() = File(cacheDir, CACHE_HEADER).useCodedInputIfExists {
// skip cache header
CacheHeader.fromProtoStream(this@useCodedInputIfExists)
buildMapUntil(readInt32()) {
val file = KotlinSourceFile.fromProtoStream(this@useCodedInputIfExists)
put(file, ICHash.fromProtoStream(this@useCodedInputIfExists))
}
} ?: emptyMap()
private val kotlinLibraryHeader: KotlinLibraryHeader by lazy { KotlinLibraryHeader(library) }
private class KotlinSourceFileMetadataFromDisk(
override val inverseDependencies: KotlinSourceFileMap>,
override val directDependencies: KotlinSourceFileMap>,
override val importedInlineFunctions: Map
) : KotlinSourceFileMetadata()
private object KotlinSourceFileMetadataNotExist : KotlinSourceFileMetadata() {
override val inverseDependencies = KotlinSourceFileMap>(emptyMap())
override val directDependencies = KotlinSourceFileMap>(emptyMap())
override val importedInlineFunctions = emptyMap()
}
private val kotlinLibrarySourceFileMetadata = mutableMapOf()
private fun KotlinSourceFile.getCacheFile(suffix: String) = File(cacheDir, "${File(path).name}.${path.stringHashForIC()}.$suffix")
private fun commitCacheHeader(fingerprints: List>) = File(cacheDir, CACHE_HEADER).useCodedOutput {
cacheHeader.toProtoStream(this)
writeInt32NoTag(fingerprints.size)
for ((srcFile, fingerprint) in fingerprints) {
srcFile.toProtoStream(this)
fingerprint.toProtoStream(this)
}
}
fun buildModuleArtifactAndCommitCache(
moduleName: String,
rebuiltFileFragments: Map,
signatureToIndexMapping: Map>
): ModuleArtifact {
val fileArtifacts = kotlinLibraryHeader.sourceFiles.map { srcFile ->
val binaryAstFile = srcFile.getCacheFile(BINARY_AST_SUFFIX)
val rebuiltFileFragment = rebuiltFileFragments[srcFile]
if (rebuiltFileFragment != null) {
binaryAstFile.apply { recreate() }.writeBytes(rebuiltFileFragment.binaryAst)
}
commitSourceFileMetadata(srcFile, signatureToIndexMapping[srcFile] ?: emptyMap())
SrcFileArtifact(srcFile.path, rebuiltFileFragment?.fragment, binaryAstFile)
}
return ModuleArtifact(moduleName, fileArtifacts, cacheDir, forceRebuildJs)
}
data class ModifiedFiles(
val modified: Map = emptyMap(),
val removed: Map = emptyMap(),
val newFiles: Set = emptySet()
)
fun collectModifiedFiles(configHash: ICHash): ModifiedFiles {
val klibFileHash = library.libraryFile.javaFile().fileHashForIC()
cacheHeader = when {
cacheHeader.configHash != configHash -> {
cacheDir.deleteRecursively()
CacheHeader(klibFileHash, configHash)
}
cacheHeader.klibFileHash != klibFileHash -> CacheHeader(klibFileHash, configHash)
else -> return ModifiedFiles()
}
val cachedFingerprints = loadCachedFingerprints()
val deletedFiles = cachedFingerprints.keys.toMutableSet()
val newFiles = mutableSetOf()
val newFingerprints = kotlinLibraryHeader.sourceFiles.mapIndexed { index, file -> file to library.fingerprint(index) }
val modifiedFiles = buildMap(newFingerprints.size) {
for ((file, fileNewFingerprint) in newFingerprints) {
val oldFingerprint = cachedFingerprints[file]
if (oldFingerprint == null) {
newFiles += file
}
if (oldFingerprint != fileNewFingerprint) {
val metadata = fetchSourceFileMetadata(file, false)
put(file, metadata)
}
deletedFiles.remove(file)
}
}
val removedFilesMetadata = deletedFiles.associateWith {
val metadata = fetchSourceFileMetadata(it, false)
it.getCacheFile(BINARY_AST_SUFFIX).delete()
it.getCacheFile(METADATA_SUFFIX).delete()
metadata
}
forceRebuildJs = deletedFiles.isNotEmpty()
commitCacheHeader(newFingerprints)
return ModifiedFiles(modifiedFiles, removedFilesMetadata, newFiles)
}
fun fetchSourceFileFullMetadata(srcFile: KotlinSourceFile): KotlinSourceFileMetadata {
return fetchSourceFileMetadata(srcFile, true)
}
fun updateSourceFileMetadata(srcFile: KotlinSourceFile, sourceFileMetadata: KotlinSourceFileMetadata) {
kotlinLibrarySourceFileMetadata[srcFile] = sourceFileMetadata
}
private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) =
kotlinLibrarySourceFileMetadata.getOrPut(srcFile) {
val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() }
fun IdSignatureDeserializer.deserializeIdSignatureAndSave(index: Int): IdSignature {
val signature = deserializeIdSignature(index)
signatureToIndexMapping[signature] = index
return signature
}
val deserializer: IdSignatureDeserializer by lazy {
kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile)
}
srcFile.getCacheFile(METADATA_SUFFIX).useCodedInputIfExists {
fun readDependencies() = buildMapUntil(readInt32()) {
val libraryFile = KotlinLibraryFile.fromProtoStream(this@useCodedInputIfExists)
val depends = buildMapUntil(readInt32()) {
val dependencySrcFile = KotlinSourceFile.fromProtoStream(this@useCodedInputIfExists)
val dependencySignatures = if (loadSignatures) {
buildSetUntil(readInt32()) { add(deserializer.deserializeIdSignatureAndSave(readInt32())) }
} else {
repeat(readInt32()) { readInt32() }
emptySet()
}
put(dependencySrcFile, dependencySignatures)
}
put(libraryFile, depends)
}
val directDependencies = KotlinSourceFileMap(readDependencies())
val reverseDependencies = KotlinSourceFileMap(readDependencies())
val importedInlineFunctions = if (loadSignatures) {
buildMapUntil(readInt32()) {
val signature = deserializer.deserializeIdSignatureAndSave(readInt32())
val transitiveHash = ICHash.fromProtoStream(this@useCodedInputIfExists)
put(signature, transitiveHash)
}
} else {
emptyMap()
}
KotlinSourceFileMetadataFromDisk(reverseDependencies, directDependencies, importedInlineFunctions)
} ?: KotlinSourceFileMetadataNotExist
}
private fun commitSourceFileMetadata(srcFile: KotlinSourceFile, signatureToIndexMapping: Map) {
val headerCacheFile = srcFile.getCacheFile(METADATA_SUFFIX)
val sourceFileMetadata = kotlinLibrarySourceFileMetadata[srcFile] ?: notFoundIcError("metadata", libraryFile, srcFile)
if (sourceFileMetadata.isEmpty()) {
headerCacheFile.delete()
return
}
if (sourceFileMetadata is KotlinSourceFileMetadataFromDisk) {
return
}
val signatureToIndexMappingSaved = signatureToIndexMappingFromMetadata[srcFile] ?: emptyMap()
fun serializeSignature(signature: IdSignature): Int {
val index = signatureToIndexMapping[signature] ?: signatureToIndexMappingSaved[signature]
return index ?: notFoundIcError("signature $signature", libraryFile, srcFile)
}
headerCacheFile.useCodedOutput {
fun writeDepends(depends: KotlinSourceFileMap>) {
writeInt32NoTag(depends.size)
for ((dependencyLibFile, dependencySrcFiles) in depends) {
dependencyLibFile.toProtoStream(this)
writeInt32NoTag(dependencySrcFiles.size)
for ((dependencySrcFile, signatures) in dependencySrcFiles) {
dependencySrcFile.toProtoStream(this)
writeInt32NoTag(signatures.size)
for (signature in signatures) {
writeInt32NoTag(serializeSignature(signature))
}
}
}
}
writeDepends(sourceFileMetadata.directDependencies)
writeDepends(sourceFileMetadata.inverseDependencies)
writeInt32NoTag(sourceFileMetadata.importedInlineFunctions.size)
for ((signature, transitiveHash) in sourceFileMetadata.importedInlineFunctions) {
writeInt32NoTag(serializeSignature(signature))
transitiveHash.toProtoStream(this)
}
}
}
}