[IC] Refactor IC maps to reuse code and ensure consistency - Part 2
In commit 4e89dcf, we have prepared the API for IC maps in top
interfaces and provide the implementation in abstract classes.
In this commit, we refactor IC maps so that they directly inherit/reuse
the implementation from the superclasses without having to reimplement
the APIs for a map.
Test: Existing tests (refactoring change)
^KT-63456: In progress
Authored-by: Hung Nguyen <hungnv@google.com>
Merge-request: KOTLIN-MR-801
Merged-by: Evgenii Mazhukin <evgenii.mazhukin@jetbrains.com>
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import org.jetbrains.kotlin.incremental.storage.*
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
/**
|
||||
* Incremental cache common for JVM and JS, ClassName type aware
|
||||
@@ -94,13 +92,13 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
private val complementaryFilesMap = registerMap(ComplementarySourceFilesMap(COMPLEMENTARY_FILES.storageFile, icContext))
|
||||
|
||||
override fun classesFqNamesBySources(files: Iterable<File>): Collection<FqName> =
|
||||
files.flatMapTo(HashSet()) { sourceToClassesMap.getFqNames(it) }
|
||||
files.flatMapTo(mutableSetOf()) { sourceToClassesMap.getFqNames(it).orEmpty() }
|
||||
|
||||
override fun getSubtypesOf(className: FqName): Sequence<FqName> =
|
||||
subtypesMap[className].asSequence()
|
||||
subtypesMap[className].orEmpty().asSequence()
|
||||
|
||||
override fun getSupertypesOf(className: FqName): Sequence<FqName> {
|
||||
return supertypesMap[className].asSequence()
|
||||
return supertypesMap[className].orEmpty().asSequence()
|
||||
}
|
||||
|
||||
override fun isSealed(className: FqName): Boolean? {
|
||||
@@ -112,10 +110,10 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
|
||||
override fun markDirty(removedAndCompiledSources: Collection<File>) {
|
||||
for (sourceFile in removedAndCompiledSources) {
|
||||
sourceToClassesMap[sourceFile].forEach { className ->
|
||||
sourceToClassesMap[sourceFile]?.forEach { className ->
|
||||
markDirty(className)
|
||||
}
|
||||
sourceToClassesMap.clearOutputsForSource(sourceFile)
|
||||
sourceToClassesMap.remove(sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +135,9 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
.toSet()
|
||||
val child = nameResolver.getClassId(proto.fqName).asSingleFqName()
|
||||
|
||||
parents.forEach { subtypesMap.add(it, child) }
|
||||
parents.forEach { subtypesMap.append(it, child) }
|
||||
|
||||
val removedSupertypes = supertypesMap[child].filter { it !in parents }
|
||||
val removedSupertypes = supertypesMap[child].orEmpty().filter { it !in parents }
|
||||
removedSupertypes.forEach { subtypesMap.removeValues(it, setOf(child)) }
|
||||
|
||||
supertypesMap[child] = parents
|
||||
@@ -163,8 +161,8 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
val childrenFqNames = hashSetOf<FqName>()
|
||||
|
||||
for (removedFqName in removedFqNames) {
|
||||
parentsFqNames.addAll(cache.supertypesMap[removedFqName])
|
||||
childrenFqNames.addAll(cache.subtypesMap[removedFqName])
|
||||
parentsFqNames.addAll(cache.supertypesMap[removedFqName].orEmpty())
|
||||
childrenFqNames.addAll(cache.subtypesMap[removedFqName].orEmpty())
|
||||
|
||||
cache.supertypesMap.remove(removedFqName)
|
||||
cache.subtypesMap.remove(removedFqName)
|
||||
@@ -188,20 +186,12 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
protected class ClassFqNameToSourceMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<String>(storageFile, EnumeratorStringDescriptor(), PathStringDescriptor, icContext) {
|
||||
operator fun set(fqName: FqName, sourceFile: File) {
|
||||
storage[fqName.asString()] = pathConverter.toPath(sourceFile)
|
||||
}
|
||||
|
||||
operator fun get(fqName: FqName): File? =
|
||||
storage[fqName.asString()]?.let(pathConverter::toFile)
|
||||
|
||||
fun remove(fqName: FqName) {
|
||||
storage.remove(fqName.asString())
|
||||
}
|
||||
|
||||
override fun dumpValue(value: String) = value
|
||||
}
|
||||
) : AbstractBasicMap<FqName, File>(
|
||||
storageFile,
|
||||
FqNameExternalizer.toDescriptor(),
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext
|
||||
)
|
||||
|
||||
override fun getComplementaryFilesRecursive(dirtyFiles: Collection<File>): Collection<File> {
|
||||
val complementaryFiles = HashSet<File>()
|
||||
@@ -216,10 +206,10 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
continue
|
||||
}
|
||||
processedFiles.add(file)
|
||||
complementaryFilesMap[file].forEach {
|
||||
complementaryFilesMap[file]?.forEach {
|
||||
if (complementaryFiles.add(it) && !processedFiles.contains(it)) filesQueue.add(it)
|
||||
}
|
||||
val classes2recompile = sourceToClassesMap.getFqNames(file)
|
||||
val classes2recompile = sourceToClassesMap.getFqNames(file).orEmpty()
|
||||
classes2recompile.filter { !processedClasses.contains(it) }.forEach { class2recompile ->
|
||||
processedClasses.add(class2recompile)
|
||||
val sealedClasses = findSealedSupertypes(class2recompile, listOf(this))
|
||||
@@ -248,11 +238,11 @@ abstract class AbstractIncrementalCache<ClassName>(
|
||||
for (actual in actuals) {
|
||||
actualToExpect.getOrPut(actual) { hashSetOf() }.add(expect)
|
||||
}
|
||||
complementaryFilesMap[expect] = actuals.union(complementaryFilesMap[expect])
|
||||
complementaryFilesMap[expect] = actuals.union(complementaryFilesMap[expect].orEmpty())
|
||||
}
|
||||
|
||||
for ((actual, expects) in actualToExpect) {
|
||||
complementaryFilesMap[actual] = expects.union(complementaryFilesMap[actual])
|
||||
complementaryFilesMap[actual] = expects.union(complementaryFilesMap[actual].orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import org.jetbrains.kotlin.build.report.DoNothingICReporter
|
||||
import org.jetbrains.kotlin.build.report.ICReporter
|
||||
import org.jetbrains.kotlin.incremental.storage.FileToAbsolutePathConverter
|
||||
import org.jetbrains.kotlin.incremental.storage.BasicFileToPathConverter
|
||||
import org.jetbrains.kotlin.incremental.storage.FileToPathConverter
|
||||
import java.io.File
|
||||
|
||||
class IncrementalCompilationContext(
|
||||
// The root directories of source files and output files are different, so we may need different `FileToPathConverter`s
|
||||
val pathConverterForSourceFiles: FileToPathConverter = FileToAbsolutePathConverter,
|
||||
val pathConverterForOutputFiles: FileToPathConverter = FileToAbsolutePathConverter,
|
||||
// The root directories of source files and output files are different, so we need different `FileToPathConverter`s
|
||||
val pathConverterForSourceFiles: FileToPathConverter = BasicFileToPathConverter,
|
||||
val pathConverterForOutputFiles: FileToPathConverter = BasicFileToPathConverter,
|
||||
val storeFullFqNamesInLookupCache: Boolean = false,
|
||||
val transaction: CompilationTransaction = NonRecoverableCompilationTransaction(),
|
||||
val reporter: ICReporter = DoNothingICReporter,
|
||||
@@ -46,7 +48,6 @@ class IncrementalCompilationContext(
|
||||
keepIncrementalCompilationCachesInMemory
|
||||
)
|
||||
|
||||
// FIXME: Remove `pathConverter` and require its users to decide whether to use `pathConverterForSourceFiles` or
|
||||
// `pathConverterForOutputFiles`
|
||||
val pathConverter = pathConverterForSourceFiles
|
||||
val fileDescriptorForSourceFiles: KeyDescriptor<File> = pathConverterForSourceFiles.getFileDescriptor()
|
||||
val fileDescriptorForOutputFiles: KeyDescriptor<File> = pathConverterForOutputFiles.getFileDescriptor()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.build.GeneratedFile
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
|
||||
import org.jetbrains.kotlin.incremental.js.IrTranslationResultValue
|
||||
@@ -79,7 +80,7 @@ open class IncrementalJsCache(
|
||||
removedAndCompiledSources.forEach { sourceFile ->
|
||||
sourceToJsOutputsMap.remove(sourceFile)
|
||||
// The common prefix of all FQN parents has to be the file package
|
||||
sourceToClassesMap[sourceFile].map { it.parentOrNull()?.asString() ?: "" }.minByOrNull { it.length }?.let {
|
||||
sourceToClassesMap[sourceFile].orEmpty().map { it.parentOrNull()?.asString() ?: "" }.minByOrNull { it.length }?.let {
|
||||
packageMetadata.remove(it)
|
||||
}
|
||||
}
|
||||
@@ -99,7 +100,7 @@ open class IncrementalJsCache(
|
||||
}
|
||||
|
||||
fun getOutputsBySource(sourceFile: File): Collection<File> {
|
||||
return sourceToJsOutputsMap[sourceFile]
|
||||
return sourceToJsOutputsMap[sourceFile].orEmpty()
|
||||
}
|
||||
|
||||
fun compareAndUpdate(incrementalResults: IncrementalResultsConsumerImpl, changesCollector: ChangesCollector) {
|
||||
@@ -132,7 +133,7 @@ open class IncrementalJsCache(
|
||||
}
|
||||
|
||||
for ((packageName, metadata) in incrementalResults.packageMetadata) {
|
||||
packageMetadata.put(packageName, metadata)
|
||||
packageMetadata[packageName] = metadata
|
||||
}
|
||||
|
||||
for ((srcFile, irData) in incrementalResults.irFileData) {
|
||||
@@ -142,7 +143,7 @@ open class IncrementalJsCache(
|
||||
}
|
||||
|
||||
private fun registerOutputForFile(srcFile: File, name: FqName) {
|
||||
sourceToClassesMap.add(srcFile, name)
|
||||
sourceToClassesMap.append(srcFile, name)
|
||||
dirtyOutputClassesMap.notDirty(name)
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ open class IncrementalJsCache(
|
||||
|
||||
fun nonDirtyPackageParts(): Map<File, TranslationResultValue> =
|
||||
hashMapOf<File, TranslationResultValue>().apply {
|
||||
for (file in translationResults.keys()) {
|
||||
for (file in translationResults.keys) {
|
||||
|
||||
if (file !in dirtySources) {
|
||||
put(file, translationResults[file]!!)
|
||||
@@ -175,7 +176,7 @@ open class IncrementalJsCache(
|
||||
|
||||
fun nonDirtyIrParts(): Map<File, IrTranslationResultValue> =
|
||||
hashMapOf<File, IrTranslationResultValue>().apply {
|
||||
for (file in irTranslationResults.keys()) {
|
||||
for (file in irTranslationResults.keys) {
|
||||
|
||||
if (file !in dirtySources) {
|
||||
put(file, irTranslationResults[file]!!)
|
||||
@@ -189,7 +190,7 @@ open class IncrementalJsCache(
|
||||
for (generatedFile in generatedFiles) {
|
||||
for (source in generatedFile.sourceFiles) {
|
||||
if (dirtySources.contains(source))
|
||||
sourceToJsOutputsMap.add(source, generatedFile.outputFile)
|
||||
sourceToJsOutputsMap.append(source, generatedFile.outputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,34 +229,32 @@ private class TranslationResultMap(
|
||||
storageFile: File,
|
||||
private val protoData: ProtoDataProvider,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) :
|
||||
BasicStringMap<TranslationResultValue>(storageFile, TranslationResultValueExternalizer, icContext) {
|
||||
) : AbstractBasicMap<File, TranslationResultValue>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
TranslationResultValueExternalizer,
|
||||
icContext
|
||||
) {
|
||||
|
||||
@TestOnly
|
||||
override fun dumpValue(value: TranslationResultValue): String =
|
||||
"Metadata: ${value.metadata.md5()}, Binary AST: ${value.binaryAst.md5()}, InlineData: ${value.inlineData.md5()}"
|
||||
|
||||
@Synchronized
|
||||
fun put(sourceFile: File, newMetadata: ByteArray, newBinaryAst: ByteArray, newInlineData: ByteArray) {
|
||||
storage[pathConverter.toPath(sourceFile)] =
|
||||
this[sourceFile] =
|
||||
TranslationResultValue(metadata = newMetadata, binaryAst = newBinaryAst, inlineData = newInlineData)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun get(sourceFile: File): TranslationResultValue? =
|
||||
storage[pathConverter.toPath(sourceFile)]
|
||||
|
||||
fun keys(): Collection<File> =
|
||||
storage.keys.map { pathConverter.toFile(it) }
|
||||
|
||||
@Synchronized
|
||||
fun remove(sourceFile: File, changesCollector: ChangesCollector) {
|
||||
val path = pathConverter.toPath(sourceFile)
|
||||
val protoBytes = storage[path]!!.metadata
|
||||
val protoBytes = this[sourceFile]!!.metadata
|
||||
val protoMap = protoData(sourceFile, protoBytes)
|
||||
|
||||
for ((_, protoData) in protoMap) {
|
||||
changesCollector.collectProtoChanges(oldData = protoData, newData = null)
|
||||
}
|
||||
storage.remove(path)
|
||||
remove(sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,8 +312,14 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
private class IrTranslationResultMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) :
|
||||
BasicStringMap<IrTranslationResultValue>(storageFile, IrTranslationResultValueExternalizer, icContext) {
|
||||
) : AbstractBasicMap<File, IrTranslationResultValue>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
IrTranslationResultValueExternalizer,
|
||||
icContext
|
||||
) {
|
||||
|
||||
@TestOnly
|
||||
override fun dumpValue(value: IrTranslationResultValue): String =
|
||||
"Filedata: ${value.fileData.md5()}, " +
|
||||
"Types: ${value.types.md5()}, " +
|
||||
@@ -323,6 +328,7 @@ private class IrTranslationResultMap(
|
||||
"Declarations: ${value.declarations.md5()}, " +
|
||||
"Bodies: ${value.bodies.md5()}"
|
||||
|
||||
@Synchronized
|
||||
fun put(
|
||||
sourceFile: File,
|
||||
newFiledata: ByteArray,
|
||||
@@ -335,20 +341,9 @@ private class IrTranslationResultMap(
|
||||
newFileMetadata: ByteArray,
|
||||
debugInfos: ByteArray?,
|
||||
) {
|
||||
storage[pathConverter.toPath(sourceFile)] =
|
||||
this[sourceFile] =
|
||||
IrTranslationResultValue(newFiledata, newTypes, newSignatures, newStrings, newDeclarations, newBodies, fqn, newFileMetadata, debugInfos)
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): IrTranslationResultValue? =
|
||||
storage[pathConverter.toPath(sourceFile)]
|
||||
|
||||
fun keys(): Collection<File> =
|
||||
storage.keys.map { pathConverter.toFile(it) }
|
||||
|
||||
fun remove(sourceFile: File) {
|
||||
val path = pathConverter.toPath(sourceFile)
|
||||
storage.remove(path)
|
||||
}
|
||||
}
|
||||
|
||||
private class ProtoDataProvider(private val serializerProtocol: SerializerExtensionProtocol) {
|
||||
@@ -396,16 +391,21 @@ fun getProtoData(sourceFile: File, metadata: ByteArray): Map<ClassId, ProtoData>
|
||||
private class InlineFunctionsMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer, icContext) {
|
||||
) : AbstractBasicMap<File, Map<String, Long>>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
StringToLongMapExternalizer,
|
||||
icContext
|
||||
) {
|
||||
|
||||
@Synchronized
|
||||
fun process(srcFile: File, newMap: Map<String, Long>, changesCollector: ChangesCollector) {
|
||||
val key = pathConverter.toPath(srcFile)
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
val oldMap = this[srcFile] ?: emptyMap()
|
||||
|
||||
if (newMap.isNotEmpty()) {
|
||||
storage[key] = newMap
|
||||
this[srcFile] = newMap
|
||||
} else {
|
||||
storage.remove(key)
|
||||
remove(srcFile)
|
||||
}
|
||||
|
||||
for (fn in oldMap.keys + newMap.keys) {
|
||||
@@ -415,11 +415,7 @@ private class InlineFunctionsMap(
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(sourceFile: File) {
|
||||
storage.remove(pathConverter.toPath(sourceFile))
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
override fun dumpValue(value: Map<String, Long>): String =
|
||||
value.dumpMap { java.lang.Long.toHexString(it) }
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
|
||||
import com.intellij.util.containers.CollectionFactory
|
||||
import com.intellij.util.io.BooleanDataDescriptor
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import gnu.trove.THashSet
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||
import org.jetbrains.kotlin.incremental.storage.*
|
||||
@@ -88,13 +85,10 @@ open class IncrementalJvmCache(
|
||||
// used in gradle
|
||||
@Suppress("unused")
|
||||
fun classesBySources(sources: Iterable<File>): Iterable<JvmClassName> =
|
||||
sources.flatMap { sourceToClassesMap[it] }
|
||||
|
||||
fun sourceInCache(file: File): Boolean =
|
||||
sourceToClassesMap.contains(file)
|
||||
sources.flatMap { sourceToClassesMap[it].orEmpty() }
|
||||
|
||||
fun sourcesByInternalName(internalName: String): Collection<File> =
|
||||
internalNameToSource.getFiles(internalName)
|
||||
internalNameToSource[internalName].orEmpty()
|
||||
|
||||
fun getAllPartsOfMultifileFacade(facade: JvmClassName): Collection<String>? {
|
||||
return multifileFacadeToParts[facade]
|
||||
@@ -111,7 +105,7 @@ open class IncrementalJvmCache(
|
||||
val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
|
||||
protoMap.storeModuleMapping(jvmClassName, file.readBytes())
|
||||
dirtyOutputClassesMap.notDirty(jvmClassName)
|
||||
sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) }
|
||||
sourceFiles.forEach { sourceToClassesMap.append(it, jvmClassName) }
|
||||
}
|
||||
|
||||
open fun saveFileToCache(generatedClass: GeneratedJvmClass, changesCollector: ChangesCollector) {
|
||||
@@ -133,7 +127,7 @@ open class IncrementalJvmCache(
|
||||
|
||||
if (sourceFiles != null) {
|
||||
sourceFiles.forEach {
|
||||
sourceToClassesMap.add(it, className)
|
||||
sourceToClassesMap.append(it, className)
|
||||
}
|
||||
internalNameToSource[className.internalName] = sourceFiles
|
||||
}
|
||||
@@ -176,7 +170,7 @@ open class IncrementalJvmCache(
|
||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||
}
|
||||
packagePartMap.addPackagePart(className)
|
||||
partToMultifileFacade.set(className.internalName, kotlinClassInfo.multifileClassName!!)
|
||||
partToMultifileFacade[className] = kotlinClassInfo.multifileClassName!!
|
||||
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
@@ -235,7 +229,7 @@ open class IncrementalJvmCache(
|
||||
fun saveJavaClassProto(source: File?, serializedJavaClass: SerializedJavaClass, collector: ChangesCollector) {
|
||||
val jvmClassName = JvmClassName.byClassId(serializedJavaClass.classId)
|
||||
javaSourcesProtoMap.process(jvmClassName, serializedJavaClass, collector)
|
||||
source?.let { sourceToClassesMap.add(source, jvmClassName) }
|
||||
source?.let { sourceToClassesMap.append(source, jvmClassName) }
|
||||
addToClassStorage(serializedJavaClass.toProtoData(), source)
|
||||
// collector.addJavaProto(ClassProtoData(proto, nameResolver))
|
||||
dirtyOutputClassesMap.notDirty(jvmClassName)
|
||||
@@ -263,7 +257,7 @@ open class IncrementalJvmCache(
|
||||
|
||||
val facadesWithRemovedParts = hashMapOf<JvmClassName, MutableSet<String>>()
|
||||
for (dirtyClass in dirtyClasses) {
|
||||
val facade = partToMultifileFacade.get(dirtyClass) ?: continue
|
||||
val facade = partToMultifileFacade[dirtyClass] ?: continue
|
||||
val facadeClassName = JvmClassName.byInternalName(facade)
|
||||
val removedParts = facadesWithRemovedParts.getOrPut(facadeClassName) { hashSetOf() }
|
||||
removedParts.add(dirtyClass.internalName)
|
||||
@@ -311,7 +305,7 @@ open class IncrementalJvmCache(
|
||||
override fun getObsoleteMultifileClasses(): Collection<String> {
|
||||
val obsoleteMultifileClasses = linkedSetOf<String>()
|
||||
for (dirtyClass in dirtyOutputClassesMap.getDirtyOutputClasses()) {
|
||||
val dirtyFacade = partToMultifileFacade.get(dirtyClass) ?: continue
|
||||
val dirtyFacade = partToMultifileFacade[dirtyClass] ?: continue
|
||||
obsoleteMultifileClasses.add(dirtyFacade)
|
||||
}
|
||||
debugLog("Obsolete multifile class facades: $obsoleteMultifileClasses")
|
||||
@@ -514,59 +508,32 @@ open class IncrementalJvmCache(
|
||||
private inner class MultifileClassFacadeMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) :
|
||||
BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer, icContext) {
|
||||
|
||||
@Synchronized
|
||||
operator fun set(className: JvmClassName, partNames: Collection<String>) {
|
||||
storage[className.internalName] = partNames
|
||||
}
|
||||
|
||||
operator fun get(className: JvmClassName): Collection<String>? =
|
||||
storage[className.internalName]
|
||||
|
||||
operator fun contains(className: JvmClassName): Boolean =
|
||||
className.internalName in storage
|
||||
|
||||
@Synchronized
|
||||
fun remove(className: JvmClassName) {
|
||||
storage.remove(className.internalName)
|
||||
}
|
||||
|
||||
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
||||
}
|
||||
) : AppendableBasicMap<JvmClassName, String>(
|
||||
storageFile,
|
||||
JvmClassNameExternalizer.toDescriptor(),
|
||||
StringExternalizer,
|
||||
icContext
|
||||
)
|
||||
|
||||
private inner class MultifileClassPartMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) :
|
||||
BasicStringMap<String>(storageFile, EnumeratorStringDescriptor.INSTANCE, icContext) {
|
||||
|
||||
fun get(partName: JvmClassName): String? =
|
||||
storage[partName.internalName]
|
||||
|
||||
@Synchronized
|
||||
fun remove(className: JvmClassName) {
|
||||
storage.remove(className.internalName)
|
||||
}
|
||||
|
||||
override fun dumpValue(value: String): String = value
|
||||
}
|
||||
) : AbstractBasicMap<JvmClassName, String>(
|
||||
storageFile,
|
||||
JvmClassNameExternalizer.toDescriptor(),
|
||||
StringExternalizer,
|
||||
icContext
|
||||
)
|
||||
|
||||
inner class InternalNameToSourcesMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<Collection<String>>(storageFile, EnumeratorStringDescriptor(), PathCollectionExternalizer, icContext) {
|
||||
operator fun set(internalName: String, sourceFiles: Collection<File>) {
|
||||
storage[internalName] = pathConverter.toPaths(sourceFiles)
|
||||
}
|
||||
|
||||
fun getFiles(internalName: String): Collection<File> =
|
||||
pathConverter.toFiles(storage[internalName] ?: emptyList())
|
||||
|
||||
override fun dumpValue(value: Collection<String>): String =
|
||||
value.dumpCollection()
|
||||
}
|
||||
) : AppendableBasicMap<String, File>(
|
||||
storageFile,
|
||||
StringExternalizer.toDescriptor(),
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext
|
||||
)
|
||||
|
||||
private inner class InlineFunctionsMap(
|
||||
storageFile: File,
|
||||
@@ -628,9 +595,6 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
}
|
||||
|
||||
private object PathCollectionExternalizer :
|
||||
CollectionExternalizerV2<String, Collection<String>>(PathStringDescriptor, { THashSet(CollectionFactory.createFilePathSet()) })
|
||||
|
||||
sealed class ChangeInfo(val fqName: FqName) {
|
||||
open class MembersChanged(fqName: FqName, val names: Collection<String>) : ChangeInfo(fqName) {
|
||||
override fun toStringProperties(): String = super.toStringProperties() + ", names = $names"
|
||||
|
||||
@@ -96,7 +96,7 @@ open class LookupStorage(
|
||||
val filtered = mutableSetOf<Int>()
|
||||
|
||||
for (fileId in fileIds) {
|
||||
val path = idToFile.getFile(fileId)?.path
|
||||
val path = idToFile[fileId]?.path
|
||||
|
||||
if (path != null) {
|
||||
paths.add(path)
|
||||
@@ -171,7 +171,7 @@ open class LookupStorage(
|
||||
lookupMap[hash] = lookupMap[hash]!!.filter { it in idToFile }.toSet()
|
||||
}
|
||||
|
||||
val oldFileToId = fileToId.toMap()
|
||||
val oldFileToId = fileToId.keys.associateWith { fileToId[it]!! }
|
||||
val oldIdToNewId = HashMap<Int, Int>(oldFileToId.size)
|
||||
idToFile.clear()
|
||||
fileToId.clear()
|
||||
@@ -302,17 +302,17 @@ private class TrackedLookupMap(private val lookupMap: LookupMap, private val tra
|
||||
val addedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||
val removedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||
|
||||
val keys: Collection<LookupSymbolKey>
|
||||
val keys: Set<LookupSymbolKey>
|
||||
get() = lookupMap.keys
|
||||
|
||||
operator fun get(key: LookupSymbolKey): Collection<Int>? = lookupMap[key]
|
||||
operator fun get(key: LookupSymbolKey): Set<Int>? = lookupMap[key]
|
||||
|
||||
operator fun set(key: LookupSymbolKey, fileIds: Set<Int>) {
|
||||
recordSet(key)
|
||||
lookupMap[key] = fileIds
|
||||
}
|
||||
|
||||
fun append(key: LookupSymbolKey, fileIds: Collection<Int>) {
|
||||
fun append(key: LookupSymbolKey, fileIds: Set<Int>) {
|
||||
recordSet(key)
|
||||
lookupMap.append(key, fileIds)
|
||||
}
|
||||
@@ -324,23 +324,19 @@ private class TrackedLookupMap(private val lookupMap: LookupMap, private val tra
|
||||
|
||||
private fun recordSet(key: LookupSymbolKey) {
|
||||
if (!trackChanges) return
|
||||
if (lookupMap[key] == null) {
|
||||
if (key in removedKeys!!) {
|
||||
removedKeys.remove(key)
|
||||
} else {
|
||||
addedKeys!!.add(key)
|
||||
}
|
||||
when (key) {
|
||||
in lookupMap -> Unit
|
||||
in removedKeys!! -> removedKeys.remove(key)
|
||||
else -> addedKeys!!.add(key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordRemove(key: LookupSymbolKey) {
|
||||
if (!trackChanges) return
|
||||
if (lookupMap[key] != null) {
|
||||
if (key in addedKeys!!) {
|
||||
addedKeys.remove(key)
|
||||
} else {
|
||||
removedKeys!!.add(key)
|
||||
}
|
||||
when (key) {
|
||||
!in lookupMap -> Unit
|
||||
in addedKeys!! -> addedKeys.remove(key)
|
||||
else -> removedKeys!!.add(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.IOUtil
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
@@ -30,7 +29,9 @@ interface BasicMap<KEY, VALUE> : PersistentStorage<KEY, VALUE> {
|
||||
|
||||
/** Removes all entries. */
|
||||
fun clear() {
|
||||
keys.forEach { remove(it) }
|
||||
synchronized(this) {
|
||||
keys.forEach { remove(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,22 +41,29 @@ interface BasicMap<KEY, VALUE> : PersistentStorage<KEY, VALUE> {
|
||||
* Make sure the storage has been closed first before calling this method.
|
||||
*/
|
||||
fun deleteStorageFiles() {
|
||||
check(IOUtil.deleteAllFilesStartingWith(storageFile)) {
|
||||
"Unable to delete storage file(s) with name prefix: ${storageFile.path}"
|
||||
synchronized(this) {
|
||||
check(IOUtil.deleteAllFilesStartingWith(storageFile)) {
|
||||
"Unable to delete storage file(s) with name prefix: ${storageFile.path}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun dump(): String {
|
||||
val map: Map<KEY, VALUE> = synchronized(this) {
|
||||
keys.associateWith { this[it]!! }
|
||||
}
|
||||
val printableMap: Map<String, String> = map.map {
|
||||
dumpKey(it.key) to dumpValue(it.value)
|
||||
}.sortedBy { it.first }.toMap()
|
||||
|
||||
return StringBuilder().apply {
|
||||
Printer(this).apply {
|
||||
println("${storageFile.name.substringBefore(".tab")} (${this@BasicMap::class.java.simpleName})")
|
||||
pushIndent()
|
||||
|
||||
for (key in keys.sortedBy { dumpKey(it) }) {
|
||||
println("${dumpKey(key)} -> ${dumpValue(this@BasicMap[key]!!)}")
|
||||
printableMap.forEach {
|
||||
println("${it.key} -> ${it.value}")
|
||||
}
|
||||
|
||||
popIndent()
|
||||
}
|
||||
}.toString()
|
||||
@@ -65,59 +73,113 @@ interface BasicMap<KEY, VALUE> : PersistentStorage<KEY, VALUE> {
|
||||
fun dumpKey(key: KEY): String = key.toString()
|
||||
|
||||
@TestOnly
|
||||
fun dumpValue(value: VALUE): String = value.toString()
|
||||
fun dumpValue(value: VALUE): String {
|
||||
return if (value is Collection<*>) {
|
||||
// Sort the elements so that we can reliably compare `Collection`s in tests (in case the order of the elements is not stable).
|
||||
value.sortedBy { it.toString() }.toString()
|
||||
} else {
|
||||
value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BasicMapBase<KEY, VALUE>(
|
||||
protected val storage: PersistentStorage<KEY, VALUE>,
|
||||
) : PersistentStorageWrapper<KEY, VALUE>(storage), BasicMap<KEY, VALUE>
|
||||
// TODO(KT-63456): Remove `protected val` (currently this property is still used by BasicStringMap)
|
||||
protected val persistentStorage: PersistentStorage<KEY, VALUE>,
|
||||
) : PersistentStorageWrapper<KEY, VALUE>(persistentStorage), BasicMap<KEY, VALUE>
|
||||
|
||||
abstract class AppendableBasicMapBase<KEY, E, VALUE : Collection<E>>(
|
||||
protected val storage: AppendablePersistentStorage<KEY, E, VALUE>,
|
||||
) : AppendablePersistentStorageWrapper<KEY, E, VALUE>(storage), BasicMap<KEY, VALUE>
|
||||
abstract class AppendableBasicMapBase<KEY, E>(
|
||||
storage: AppendablePersistentStorage<KEY, E>,
|
||||
) : AppendablePersistentStorageWrapper<KEY, E>(storage), BasicMap<KEY, Collection<E>>
|
||||
|
||||
abstract class AbstractBasicMap<KEY, VALUE>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<KEY>,
|
||||
valueExternalizer: DataExternalizer<VALUE>,
|
||||
protected val icContext: IncrementalCompilationContext,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicMapBase<KEY, VALUE>(
|
||||
createPersistentStorage(storageFile, keyDescriptor, valueExternalizer, icContext)
|
||||
) {
|
||||
protected val pathConverter
|
||||
get() = icContext.pathConverter
|
||||
}
|
||||
)
|
||||
|
||||
abstract class AppendableAbstractBasicMap<KEY, E, VALUE : Collection<E>>(
|
||||
abstract class AppendableBasicMap<KEY, E>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<KEY>,
|
||||
elementExternalizer: DataExternalizer<E>,
|
||||
protected val icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicMapBase<KEY, E, VALUE>(
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicMapBase<KEY, E>(
|
||||
createAppendablePersistentStorage(storageFile, keyDescriptor, elementExternalizer, icContext)
|
||||
) {
|
||||
protected val pathConverter
|
||||
get() = icContext.pathConverter
|
||||
)
|
||||
|
||||
abstract class AppendableSetBasicMap<KEY, E>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<KEY>,
|
||||
elementExternalizer: DataExternalizer<E>,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : PersistentStorage<KEY, Set<E>>, BasicMap<KEY, Set<E>> {
|
||||
|
||||
private val storage = createAppendablePersistentStorage(storageFile, keyDescriptor, elementExternalizer, icContext)
|
||||
|
||||
override val storageFile: File = storage.storageFile
|
||||
|
||||
@get:Synchronized
|
||||
override val keys: Set<KEY>
|
||||
get() = storage.keys
|
||||
|
||||
@Synchronized
|
||||
override fun contains(key: KEY): Boolean =
|
||||
storage.contains(key)
|
||||
|
||||
@Synchronized
|
||||
override fun get(key: KEY): Set<E>? {
|
||||
// Note: To optimize this getter, consider changing the type of `storage` from PersistentStorage<KEY, Collection<E>> to
|
||||
// PersistentStorage<KEY, Set<E>> so that we don't have to call `toSet()`. The downside is that it will make the code more complex,
|
||||
// so we'll do it only if it's necessary.
|
||||
return storage[key]?.toSet()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun set(key: KEY, value: Set<E>) {
|
||||
storage[key] = value
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun remove(key: KEY) {
|
||||
storage.remove(key)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun flush() {
|
||||
storage.flush()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
storage.close()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun append(key: KEY, element: E) {
|
||||
storage.append(key, setOf(element))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun append(key: KEY, elements: Set<E>) {
|
||||
storage.append(key, elements)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BasicStringMap<VALUE>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<String>,
|
||||
valueExternalizer: DataExternalizer<VALUE>,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AbstractBasicMap<String, VALUE>(storageFile, keyDescriptor, valueExternalizer, icContext) {
|
||||
|
||||
constructor(storageFile: File, valueExternalizer: DataExternalizer<VALUE>, icContext: IncrementalCompilationContext) :
|
||||
this(storageFile, EnumeratorStringDescriptor.INSTANCE, valueExternalizer, icContext)
|
||||
}
|
||||
|
||||
abstract class AppendableBasicStringMap<E, VALUE : Collection<E>>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<String>,
|
||||
elementExternalizer: DataExternalizer<E>,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableAbstractBasicMap<String, E, VALUE>(storageFile, keyDescriptor, elementExternalizer, icContext) {
|
||||
|
||||
constructor(storageFile: File, elementExternalizer: DataExternalizer<E>, icContext: IncrementalCompilationContext) :
|
||||
this(storageFile, EnumeratorStringDescriptor.INSTANCE, elementExternalizer, icContext)
|
||||
) : AbstractBasicMap<String, VALUE>(
|
||||
storageFile,
|
||||
StringExternalizer.toDescriptor(),
|
||||
valueExternalizer,
|
||||
icContext
|
||||
) {
|
||||
// TODO(KT-63456): Remove this property
|
||||
// (To do this, we need to refactor all subclasses of BasicStringMap such that they don't use this property. For examples of what the
|
||||
// outcome of the refactoring should look like, see other subclasses of AbstractBasicMap.)
|
||||
protected val storage: PersistentStorage<String, VALUE> = persistentStorage
|
||||
}
|
||||
|
||||
@@ -16,49 +16,38 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
|
||||
internal open class ClassOneToManyMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicStringMap<String, Collection<String>>(storageFile, EnumeratorStringDescriptor.INSTANCE, icContext) {
|
||||
override fun dumpValue(value: Collection<String>): String = value.toSet().dumpCollection()
|
||||
) : AppendableSetBasicMap<FqName, FqName>(
|
||||
storageFile,
|
||||
LegacyFqNameExternalizer.toDescriptor(),
|
||||
LegacyFqNameExternalizer,
|
||||
icContext
|
||||
) {
|
||||
|
||||
@Synchronized
|
||||
fun add(key: FqName, value: FqName) {
|
||||
storage.append(key.asString(), value.asString())
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun get(key: FqName): Collection<FqName> =
|
||||
storage[key.asString()]?.mapTo(mutableSetOf(), ::FqName) ?: setOf()
|
||||
|
||||
@Synchronized
|
||||
operator fun set(key: FqName, values: Collection<FqName>) {
|
||||
if (values.isEmpty()) {
|
||||
override operator fun set(key: FqName, value: Set<FqName>) {
|
||||
if (value.isNotEmpty()) {
|
||||
super.set(key, value)
|
||||
} else {
|
||||
remove(key)
|
||||
return
|
||||
}
|
||||
|
||||
storage[key.asString()] = values.map(FqName::asString)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(key: FqName) {
|
||||
storage.remove(key.asString())
|
||||
}
|
||||
|
||||
// Access to caches could be done from multiple threads (e.g. JPS worker and RMI). The underlying collection is already synchronized,
|
||||
// thus we need synchronization of this method and all modification methods.
|
||||
@Synchronized
|
||||
fun removeValues(key: FqName, removed: Set<FqName>) {
|
||||
val notRemoved = this[key].filter { it !in removed }
|
||||
this[key] = notRemoved
|
||||
this[key] = this[key].orEmpty() - removed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class SubtypesMap(
|
||||
@@ -70,3 +59,22 @@ internal class SupertypesMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : ClassOneToManyMap(storageFile, icContext)
|
||||
|
||||
/**
|
||||
* Use [LegacyFqNameExternalizer] instead of [FqNameExternalizer] for [SubtypesMap] for now because they internally use different types of
|
||||
* `DataExternalizer<String>`, and the `compiler-reference-index` module in the Kotlin IDEA plugin currently can only read the old data
|
||||
* format (see KTIJ-27258).
|
||||
*
|
||||
* Once we fix that bug, we can remove this class and use [FqNameExternalizer].
|
||||
*/
|
||||
private object LegacyFqNameExternalizer : DataExternalizer<FqName> {
|
||||
|
||||
override fun save(output: DataOutput, fqName: FqName) {
|
||||
EnumeratorStringDescriptor.INSTANCE.save(output, fqName.asString())
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): FqName {
|
||||
return FqName(EnumeratorStringDescriptor.INSTANCE.read(input))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-22
@@ -5,33 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import java.io.File
|
||||
|
||||
class ComplementarySourceFilesMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicStringMap<String, Collection<String>>(
|
||||
) : AppendableSetBasicMap<File, File>(
|
||||
storageFile,
|
||||
PathStringDescriptor,
|
||||
EnumeratorStringDescriptor.INSTANCE,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext
|
||||
) {
|
||||
|
||||
operator fun set(sourceFile: File, complementaryFiles: Collection<File>) {
|
||||
storage[pathConverter.toPath(sourceFile)] = pathConverter.toPaths(complementaryFiles)
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): Collection<File> {
|
||||
val paths = storage[pathConverter.toPath(sourceFile)].orEmpty()
|
||||
return pathConverter.toFiles(paths).toSet()
|
||||
}
|
||||
|
||||
override fun dumpValue(value: Collection<String>) =
|
||||
value.dumpCollection()
|
||||
|
||||
fun remove(file: File): Collection<File> =
|
||||
get(file).also { storage.remove(pathConverter.toPath(file)) }
|
||||
}
|
||||
)
|
||||
|
||||
@@ -20,27 +20,11 @@ import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import java.io.File
|
||||
|
||||
internal class FileToIdMap(
|
||||
file: File,
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<Int>(file, IntExternalizer, icContext) {
|
||||
override fun dumpValue(value: Int): String = value.toString()
|
||||
|
||||
operator fun get(file: File): Int? = storage[pathConverter.toPath(file)]
|
||||
|
||||
operator fun set(file: File, id: Int) {
|
||||
storage[pathConverter.toPath(file)] = id
|
||||
}
|
||||
|
||||
fun remove(file: File) {
|
||||
storage.remove(pathConverter.toPath(file))
|
||||
}
|
||||
|
||||
fun toMap(): Map<File, Int> {
|
||||
val result = HashMap<File, Int>()
|
||||
for (key in storage.keys) {
|
||||
val value = storage[key] ?: continue
|
||||
result[pathConverter.toFile(key)] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
) : AbstractBasicMap<File, Int>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
IntExternalizer,
|
||||
icContext
|
||||
)
|
||||
|
||||
@@ -5,22 +5,54 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
|
||||
/** Converts a [File] to a path of type [String] to store in IC caches, and vice versa. */
|
||||
/** Converts a [File] to a path of type [String] (and vice versa) for serialization of file paths in IC caches. */
|
||||
interface FileToPathConverter {
|
||||
fun toPath(file: File): String
|
||||
fun toFile(path: String): File
|
||||
fun getFileDescriptor(): KeyDescriptor<File> = FileDescriptor(this)
|
||||
}
|
||||
|
||||
fun FileToPathConverter.toPaths(files: Collection<File>): List<String> =
|
||||
files.map { toPath(it) }
|
||||
|
||||
fun FileToPathConverter.toFiles(paths: Collection<String>): List<File> =
|
||||
paths.map { toFile(it) }
|
||||
|
||||
object FileToAbsolutePathConverter : FileToPathConverter {
|
||||
override fun toPath(file: File): String = file.normalize().absolutePath
|
||||
|
||||
object BasicFileToPathConverter : FileToPathConverter {
|
||||
override fun toPath(file: File): String = file.path
|
||||
override fun toFile(path: String): File = File(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* [KeyDescriptor] for a [File] which uses the given [pathConverter] for serialization of file paths.
|
||||
*
|
||||
* To support build cache relocatability, the user of this class can provide a [RelocatableFileToPathConverter] as the [pathConverter].
|
||||
*/
|
||||
private class FileDescriptor(private val pathConverter: FileToPathConverter) : KeyDescriptor<File> {
|
||||
|
||||
override fun save(output: DataOutput, file: File) {
|
||||
StringExternalizer.save(output, pathConverter.toPath(file))
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): File {
|
||||
return pathConverter.toFile(StringExternalizer.read(input))
|
||||
}
|
||||
|
||||
override fun getHashCode(file: File): Int {
|
||||
// It's important to use the pathConverter to get a possibly relocatable path first before computing the hash code because the
|
||||
// returned hash code affects the contents of IC caches.
|
||||
// For example, if we get the hash code directly without using pathConverter, the hash codes of the following files will be
|
||||
// different and therefore the contents of the IC caches will also be different (i.e., the IC caches will not be relocatable):
|
||||
// File("/path/to/project1/src/foo.kt").hashCode() = 123
|
||||
// File("/path/to/project2/src/foo.kt").hashCode() = 456
|
||||
// If we use a RelocatableFileToPathConverter instead, the file paths will be normalized first, so we'll get the same hash codes:
|
||||
// [In "/path/to/project1"] File("src/foo.kt").hashCode() = 789
|
||||
// [In "/path/to/project2"] File("src/foo.kt").hashCode() = 789
|
||||
// It's also important to get the hash code using `File.hashCode()` instead of `String.hashCode()` because the hashCode() method
|
||||
// needs to account for case-insensitive filesystems.
|
||||
return File(pathConverter.toPath(file)).hashCode()
|
||||
}
|
||||
|
||||
override fun isEqual(file1: File, file2: File): Boolean {
|
||||
return file1 == file2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,23 +16,42 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.ExternalIntegerKeyDescriptor
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
|
||||
internal class IdToFileMap(
|
||||
file: File,
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AbstractBasicMap<Int, String>(file, ExternalIntegerKeyDescriptor(), EnumeratorStringDescriptor.INSTANCE, icContext) {
|
||||
override fun dumpKey(key: Int): String = key.toString()
|
||||
) : AbstractBasicMap<Int, File>(
|
||||
storageFile,
|
||||
LegacyIntExternalizer.toDescriptor(),
|
||||
LegacyFileExternalizer(icContext.pathConverterForSourceFiles),
|
||||
icContext
|
||||
)
|
||||
|
||||
override fun dumpValue(value: String): String = value
|
||||
/** Similar to [LegacyFileExternalizer]. */
|
||||
private val LegacyIntExternalizer = ExternalIntegerKeyDescriptor.INSTANCE
|
||||
|
||||
fun getFile(id: Int): File? = storage[id]?.let { pathConverter.toFile(it) }
|
||||
/**
|
||||
* Use [LegacyFqNameExternalizer] instead of [org.jetbrains.kotlin.incremental.IncrementalCompilationContext.fileDescriptorForOutputFiles]
|
||||
* for [IdToFileMap] for now because they internally use different types of `DataExternalizer<String>`, and the `compiler-reference-index`
|
||||
* module in the Kotlin IDEA plugin currently can only read the old data format (see KTIJ-27258).
|
||||
*
|
||||
* Once we fix that bug, we can remove this class and use
|
||||
* [org.jetbrains.kotlin.incremental.IncrementalCompilationContext.fileDescriptorForOutputFiles].
|
||||
*/
|
||||
private class LegacyFileExternalizer(private val pathConverter: FileToPathConverter) : DataExternalizer<File> {
|
||||
|
||||
operator fun set(id: Int, file: File) {
|
||||
storage[id] = pathConverter.toPath(file)
|
||||
override fun save(output: DataOutput, file: File) {
|
||||
EnumeratorStringDescriptor.INSTANCE.save(output, pathConverter.toPath(file))
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): File {
|
||||
return pathConverter.toFile(EnumeratorStringDescriptor.INSTANCE.read(input))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ open class InMemoryStorage<KEY, VALUE>(
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun get(key: KEY): VALUE? = addedEntries[key] ?: modifiedEntries[key] ?: when (key) {
|
||||
in appendedEntries -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
((storage[key]!! as Collection<*>) + (appendedEntries[key]!! as Collection<*>)) as VALUE
|
||||
}
|
||||
in removedKeys -> null
|
||||
else -> storage[key]
|
||||
}
|
||||
override fun get(key: KEY): VALUE? =
|
||||
addedEntries[key]
|
||||
?: modifiedEntries[key]
|
||||
// appendedEntries is handled by AppendableInMemoryStorage#get
|
||||
?: when (key) {
|
||||
in removedKeys -> null
|
||||
else -> storage[key]
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun set(key: KEY, value: VALUE) = when (key) {
|
||||
@@ -110,7 +110,7 @@ open class InMemoryStorage<KEY, VALUE>(
|
||||
modifiedEntries.forEach {
|
||||
storage[it.key] = it.value
|
||||
}
|
||||
check(appendedEntries.isEmpty()) { "appendedEntries is not empty" }
|
||||
// appendedEntries is handled by AppendableInMemoryStorage#applyChanges
|
||||
removedKeys.forEach {
|
||||
storage.remove(it)
|
||||
}
|
||||
@@ -139,18 +139,23 @@ open class InMemoryStorage<KEY, VALUE>(
|
||||
|
||||
}
|
||||
|
||||
/** [InMemoryStorage] where a map entry's value is a [Collection]. */
|
||||
/** [InMemoryStorage] where a map entry's value is a [Collection] of elements of type [E]. */
|
||||
@ThreadSafe
|
||||
class AppendableInMemoryStorage<KEY, E, VALUE : Collection<E>>(
|
||||
private val storage: AppendablePersistentStorage<KEY, E, VALUE>,
|
||||
) : InMemoryStorage<KEY, VALUE>(storage), AppendablePersistentStorage<KEY, E, VALUE> {
|
||||
class AppendableInMemoryStorage<KEY, E>(
|
||||
private val storage: AppendablePersistentStorage<KEY, E>,
|
||||
) : InMemoryStorage<KEY, Collection<E>>(storage), AppendablePersistentStorage<KEY, E> {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Synchronized
|
||||
override fun append(key: KEY, elements: VALUE) = when (key) {
|
||||
in addedEntries -> addedEntries[key] = (addedEntries[key]!! + elements) as VALUE
|
||||
in modifiedEntries -> modifiedEntries[key] = (modifiedEntries[key]!! + elements) as VALUE
|
||||
in appendedEntries -> appendedEntries[key] = (appendedEntries[key]!! + elements) as VALUE
|
||||
override fun get(key: KEY): Collection<E>? = when (key) {
|
||||
in appendedEntries -> storage[key]!! + appendedEntries[key]!!
|
||||
else -> super.get(key)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: KEY, elements: Collection<E>) = when (key) {
|
||||
in addedEntries -> addedEntries[key] = addedEntries[key]!! + elements
|
||||
in modifiedEntries -> modifiedEntries[key] = modifiedEntries[key]!! + elements
|
||||
in appendedEntries -> appendedEntries[key] = appendedEntries[key]!! + elements
|
||||
in removedKeys -> {
|
||||
removedKeys.remove(key)
|
||||
// Note: We update `modifiedEntries` (not `appendedEntries`), because if the entry is removed and then appended, it is
|
||||
|
||||
@@ -95,19 +95,19 @@ open class LazyStorage<KEY, VALUE>(
|
||||
|
||||
}
|
||||
|
||||
/** [LazyStorage] where a map entry's value is a [Collection]. */
|
||||
/** [LazyStorage] where a map entry's value is a [Collection] of elements of type [E]. */
|
||||
@ThreadSafe
|
||||
class AppendableLazyStorage<KEY, E, VALUE : Collection<E>>(
|
||||
class AppendableLazyStorage<KEY, E>(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<KEY>,
|
||||
elementExternalizer: DataExternalizer<E>,
|
||||
) : LazyStorage<KEY, VALUE>(storageFile, keyDescriptor, AppendableCollectionExternalizer(elementExternalizer)),
|
||||
AppendablePersistentStorage<KEY, E, VALUE> {
|
||||
) : LazyStorage<KEY, Collection<E>>(storageFile, keyDescriptor, AppendableCollectionExternalizer(elementExternalizer)),
|
||||
AppendablePersistentStorage<KEY, E> {
|
||||
|
||||
private val appendableCollectionExternalizer = AppendableCollectionExternalizer(elementExternalizer)
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: KEY, elements: VALUE) {
|
||||
override fun append(key: KEY, elements: Collection<E>) {
|
||||
getStorageOrCreateNew().appendData(
|
||||
key,
|
||||
AppendablePersistentMap.ValueDataAppender { appendableCollectionExternalizer.append(it, elements) }
|
||||
@@ -116,25 +116,25 @@ class AppendableLazyStorage<KEY, E, VALUE : Collection<E>>(
|
||||
}
|
||||
|
||||
/**
|
||||
* [DataExternalizer] for a [Collection] of type [E].
|
||||
* [DataExternalizer] for a [Collection] of elements of type [E].
|
||||
*
|
||||
* IMPORTANT: It is a *private* class because it is meant to be used only with a [PersistentHashMap] (e.g., the [read] method reads until
|
||||
* the stream ends, [append] can be called multiple times and its implementation is identical to [save] -- these only work with a
|
||||
* [PersistentHashMap]).
|
||||
*/
|
||||
private class AppendableCollectionExternalizer<E, VALUE : Collection<E>>(
|
||||
private class AppendableCollectionExternalizer<E>(
|
||||
private val elementExternalizer: DataExternalizer<E>,
|
||||
) : DataExternalizer<VALUE> {
|
||||
) : DataExternalizer<Collection<E>> {
|
||||
|
||||
fun append(output: DataOutput, elements: VALUE) {
|
||||
fun append(output: DataOutput, elements: Collection<E>) {
|
||||
save(output, elements)
|
||||
}
|
||||
|
||||
override fun save(output: DataOutput, value: VALUE) {
|
||||
override fun save(output: DataOutput, value: Collection<E>) {
|
||||
value.forEach { elementExternalizer.save(output, it) }
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): VALUE {
|
||||
override fun read(input: DataInput): Collection<E> {
|
||||
val result = ArrayList<E>()
|
||||
val stream = input as DataInputStream
|
||||
|
||||
@@ -142,8 +142,7 @@ private class AppendableCollectionExternalizer<E, VALUE : Collection<E>>(
|
||||
result.add(elementExternalizer.read(stream))
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as VALUE
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,13 +163,13 @@ fun <KEY, VALUE> createPersistentStorage(
|
||||
}
|
||||
}
|
||||
|
||||
fun <KEY, E, VALUE : Collection<E>> createAppendablePersistentStorage(
|
||||
fun <KEY, E> createAppendablePersistentStorage(
|
||||
storageFile: File,
|
||||
keyDescriptor: KeyDescriptor<KEY>,
|
||||
elementExternalizer: DataExternalizer<E>,
|
||||
icContext: IncrementalCompilationContext,
|
||||
): AppendablePersistentStorage<KEY, E, VALUE> {
|
||||
return AppendableLazyStorage<KEY, E, VALUE>(storageFile, keyDescriptor, elementExternalizer).let { storage ->
|
||||
): AppendablePersistentStorage<KEY, E> {
|
||||
return AppendableLazyStorage(storageFile, keyDescriptor, elementExternalizer).let { storage ->
|
||||
if (icContext.keepIncrementalCompilationCachesInMemory) {
|
||||
AppendableInMemoryStorage(storage).also {
|
||||
icContext.transaction.registerInMemoryStorageWrapper(it)
|
||||
|
||||
@@ -22,26 +22,15 @@ import java.io.File
|
||||
class LookupMap(
|
||||
storage: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) :
|
||||
AppendableAbstractBasicMap<LookupSymbolKey, Int, Collection<Int>>(
|
||||
storage,
|
||||
LookupSymbolKeyDescriptor(icContext.storeFullFqNamesInLookupCache),
|
||||
IntExternalizer,
|
||||
icContext,
|
||||
) {
|
||||
|
||||
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
||||
|
||||
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
||||
) : AppendableSetBasicMap<LookupSymbolKey, Int>(
|
||||
storage,
|
||||
LookupSymbolKeyDescriptor(icContext.storeFullFqNamesInLookupCache),
|
||||
IntExternalizer,
|
||||
icContext,
|
||||
) {
|
||||
|
||||
@Synchronized
|
||||
fun add(name: String, scope: String, fileId: Int) {
|
||||
storage.append(LookupSymbolKey(name, scope), fileId)
|
||||
append(LookupSymbolKey(name, scope), fileId)
|
||||
}
|
||||
|
||||
override fun get(key: LookupSymbolKey): Collection<Int>? = storage[key]?.toSet()
|
||||
|
||||
operator fun set(key: LookupSymbolKey, fileIds: Set<Int>) {
|
||||
storage[key] = fileIds
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,16 +50,15 @@ interface PersistentStorage<KEY, VALUE> : Closeable {
|
||||
override fun close()
|
||||
}
|
||||
|
||||
/** [PersistentStorage] where a map entry's value is a [Collection]. */
|
||||
interface AppendablePersistentStorage<KEY, E, VALUE : Collection<E>> : PersistentStorage<KEY, VALUE> {
|
||||
/** [PersistentStorage] where a map entry's value is a [Collection] of elements of type [E]. */
|
||||
interface AppendablePersistentStorage<KEY, E> : PersistentStorage<KEY, Collection<E>> {
|
||||
|
||||
/** Adds the given [elements] to the collection corresponding to the given [key]. */
|
||||
fun append(key: KEY, elements: VALUE)
|
||||
fun append(key: KEY, elements: Collection<E>)
|
||||
|
||||
/** Adds the given [element] to the collection corresponding to the given [key]. */
|
||||
fun append(key: KEY, element: E) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
append(key, listOf(element) as VALUE)
|
||||
append(key, listOf(element))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,14 +110,14 @@ abstract class PersistentStorageWrapper<KEY, VALUE>(
|
||||
}
|
||||
}
|
||||
|
||||
/** [PersistentStorageWrapper] where a map entry's value is a [Collection]. */
|
||||
/** [PersistentStorageWrapper] where a map entry's value is a [Collection] of elements of type [E]. */
|
||||
@ThreadSafe
|
||||
abstract class AppendablePersistentStorageWrapper<KEY, E, VALUE : Collection<E>>(
|
||||
private val appendableStorage: AppendablePersistentStorage<KEY, E, VALUE>,
|
||||
) : PersistentStorageWrapper<KEY, VALUE>(appendableStorage), AppendablePersistentStorage<KEY, E, VALUE> {
|
||||
abstract class AppendablePersistentStorageWrapper<KEY, E>(
|
||||
private val appendableStorage: AppendablePersistentStorage<KEY, E>,
|
||||
) : PersistentStorageWrapper<KEY, Collection<E>>(appendableStorage), AppendablePersistentStorage<KEY, E> {
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: KEY, elements: VALUE) {
|
||||
override fun append(key: KEY, elements: Collection<E>) {
|
||||
appendableStorage.append(key, elements)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.incremental.storage
|
||||
|
||||
import org.jetbrains.kotlin.utils.ThreadSafe
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* [PersistentStorage] which maps [KEY] to [VALUE] as viewed by the users of this class, but delegates operations to another [storage] which
|
||||
* maps [INTERNAL_KEY] to [INTERNAL_VALUE].
|
||||
*
|
||||
* The users of this class need to provide the transformations from [KEY] to [INTERNAL_KEY], [VALUE] to [INTERNAL_VALUE], and vice versa.
|
||||
*/
|
||||
@ThreadSafe
|
||||
abstract class PersistentStorageAdapter<KEY, VALUE, INTERNAL_KEY, INTERNAL_VALUE>(
|
||||
private val storage: PersistentStorage<INTERNAL_KEY, INTERNAL_VALUE>,
|
||||
private val publicToInternalKey: (KEY) -> INTERNAL_KEY,
|
||||
private val internalToPublicKey: (INTERNAL_KEY) -> KEY,
|
||||
private val publicToInternalValue: (VALUE) -> INTERNAL_VALUE,
|
||||
private val internalToPublicValue: (INTERNAL_VALUE) -> VALUE,
|
||||
) : PersistentStorage<KEY, VALUE>, BasicMap<KEY, VALUE> {
|
||||
|
||||
override val storageFile: File = storage.storageFile
|
||||
|
||||
@get:Synchronized
|
||||
override val keys: Set<KEY>
|
||||
get() = storage.keys.mapTo(LinkedHashSet()) { internalToPublicKey(it) }
|
||||
|
||||
@Synchronized
|
||||
override fun contains(key: KEY): Boolean =
|
||||
storage.contains(publicToInternalKey(key))
|
||||
|
||||
@Synchronized
|
||||
override fun get(key: KEY): VALUE? =
|
||||
storage[publicToInternalKey(key)]?.let { internalToPublicValue(it) }
|
||||
|
||||
@Synchronized
|
||||
override fun set(key: KEY, value: VALUE) {
|
||||
storage[publicToInternalKey(key)] = publicToInternalValue(value)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun remove(key: KEY) {
|
||||
storage.remove(publicToInternalKey(key))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun flush() {
|
||||
storage.close()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
storage.close()
|
||||
}
|
||||
}
|
||||
|
||||
/** [PersistentStorageAdapter] where a map entry's value is a [Collection]. */
|
||||
@ThreadSafe
|
||||
abstract class AppendablePersistentStorageAdapter<KEY, E, VALUE : Collection<E>, INTERNAL_KEY, INTERNAL_E, INTERNAL_VALUE : Collection<INTERNAL_E>>(
|
||||
private val storage: AppendablePersistentStorage<INTERNAL_KEY, INTERNAL_E, INTERNAL_VALUE>,
|
||||
private val publicToInternalKey: (KEY) -> INTERNAL_KEY,
|
||||
internalToPublicKey: (INTERNAL_KEY) -> KEY,
|
||||
private val publicToInternalValue: (VALUE) -> INTERNAL_VALUE,
|
||||
internalToPublicValue: (INTERNAL_VALUE) -> VALUE,
|
||||
) : PersistentStorageAdapter<KEY, VALUE, INTERNAL_KEY, INTERNAL_VALUE>(
|
||||
storage,
|
||||
publicToInternalKey,
|
||||
internalToPublicKey,
|
||||
publicToInternalValue,
|
||||
internalToPublicValue
|
||||
), AppendablePersistentStorage<KEY, E, VALUE> {
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: KEY, elements: VALUE) {
|
||||
storage.append(publicToInternalKey(key), publicToInternalValue(elements))
|
||||
}
|
||||
}
|
||||
+2
@@ -17,6 +17,8 @@ import java.io.File
|
||||
class RelocatableFileToPathConverter(private val baseDir: File) : FileToPathConverter {
|
||||
|
||||
override fun toPath(file: File): String {
|
||||
check(file.isAbsolute) { "Expected absolute path but found relative path: ${file.path}" }
|
||||
|
||||
// Note: If the given file is located outside `baseDir`, the relative path will start with "../". It's not "clean", but it can work.
|
||||
// TODO: Re-design the code such that `baseDir` always contains the given file (also add a precondition check here).
|
||||
return file.relativeTo(baseDir).invariantSeparatorsPath
|
||||
|
||||
@@ -5,44 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import java.io.File
|
||||
|
||||
class SourceToJsOutputMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicStringMap<String, Collection<String>>(storageFile, EnumeratorStringDescriptor.INSTANCE, icContext) {
|
||||
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
||||
|
||||
@Synchronized
|
||||
fun add(key: File, value: File) {
|
||||
storage.append(pathConverter.toPath(key), pathConverter.toPath(value))
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): Collection<File> =
|
||||
storage[pathConverter.toPath(sourceFile)]?.mapTo(mutableSetOf()) { pathConverter.toFile(it) } ?: setOf()
|
||||
|
||||
|
||||
@Synchronized
|
||||
operator fun set(key: File, values: Collection<File>) {
|
||||
if (values.isEmpty()) {
|
||||
remove(key)
|
||||
return
|
||||
}
|
||||
|
||||
storage[pathConverter.toPath(key)] = values.toSet().map { pathConverter.toPath(it) }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(key: File) {
|
||||
storage.remove(pathConverter.toPath(key))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeValues(key: File, removed: Set<File>) {
|
||||
val notRemoved = this[key].filter { it !in removed }
|
||||
this[key] = notRemoved
|
||||
}
|
||||
}
|
||||
) : AppendableSetBasicMap<File, File>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext.fileDescriptorForOutputFiles,
|
||||
icContext
|
||||
)
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
|
||||
internal class SourceToJvmNameMap(
|
||||
@@ -37,30 +38,27 @@ internal abstract class AbstractSourceToOutputMap<Name>(
|
||||
private val nameTransformer: NameTransformer<Name>,
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : AppendableBasicStringMap<String, Collection<String>>(
|
||||
) : AppendableSetBasicMap<File, Name>(
|
||||
storageFile,
|
||||
PathStringDescriptor,
|
||||
EnumeratorStringDescriptor.INSTANCE,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
NameExternalizer(nameTransformer),
|
||||
icContext
|
||||
) {
|
||||
fun clearOutputsForSource(sourceFile: File) {
|
||||
remove(pathConverter.toPath(sourceFile))
|
||||
|
||||
@Synchronized
|
||||
fun getFqNames(sourceFile: File): Collection<FqName>? =
|
||||
this[sourceFile]?.map { nameTransformer.asFqName(nameTransformer.asString(it)) }
|
||||
|
||||
}
|
||||
|
||||
private class NameExternalizer<Name>(private val nameTransformer: NameTransformer<Name>) : DataExternalizer<Name> {
|
||||
|
||||
override fun save(output: DataOutput, name: Name) {
|
||||
StringExternalizer.save(output, nameTransformer.asString(name))
|
||||
}
|
||||
|
||||
fun add(sourceFile: File, className: Name) {
|
||||
storage.append(pathConverter.toPath(sourceFile), nameTransformer.asString(className))
|
||||
override fun read(input: DataInput): Name {
|
||||
return nameTransformer.asName(StringExternalizer.read(input))
|
||||
}
|
||||
|
||||
fun contains(sourceFile: File): Boolean =
|
||||
pathConverter.toPath(sourceFile) in storage
|
||||
|
||||
operator fun get(sourceFile: File): Collection<Name> =
|
||||
storage[pathConverter.toPath(sourceFile)].orEmpty().toSet().map(nameTransformer::asName)
|
||||
|
||||
fun getFqNames(sourceFile: File): Collection<FqName> =
|
||||
storage[pathConverter.toPath(sourceFile)].orEmpty().toSet().map(nameTransformer::asFqName)
|
||||
|
||||
override fun dumpValue(value: Collection<String>) =
|
||||
value.dumpCollection()
|
||||
|
||||
}
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.containers.hash.EqualityPolicy
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.IOUtil
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import org.jetbrains.kotlin.inline.InlineFunction
|
||||
@@ -31,10 +29,21 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.*
|
||||
|
||||
class DefaultEqualityPolicy<T> : EqualityPolicy<T> {
|
||||
override fun getHashCode(value: T): Int = value.hashCode()
|
||||
override fun isEqual(value1: T, value2: T): Boolean = (value1 == value2)
|
||||
}
|
||||
|
||||
fun <T> DataExternalizer<T>.toDescriptor(): KeyDescriptor<T> =
|
||||
object : KeyDescriptor<T>,
|
||||
DataExternalizer<T> by this,
|
||||
EqualityPolicy<T> by DefaultEqualityPolicy<T>() {
|
||||
}
|
||||
|
||||
class LookupSymbolKeyDescriptor(
|
||||
/** If `true`, original values are saved; if `false`, only hashes are saved. */
|
||||
private val storeFullFqNames: Boolean = false
|
||||
) : KeyDescriptor<LookupSymbolKey> {
|
||||
) : KeyDescriptor<LookupSymbolKey>, EqualityPolicy<LookupSymbolKey> by DefaultEqualityPolicy() {
|
||||
|
||||
override fun read(input: DataInput): LookupSymbolKey {
|
||||
// Note: The value of the storeFullFqNames variable below may or may not be the same as LookupSymbolKeyDescriptor.storeFullFqNames.
|
||||
@@ -67,10 +76,6 @@ class LookupSymbolKeyDescriptor(
|
||||
output.writeInt(value.scopeHash)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHashCode(value: LookupSymbolKey): Int = value.hashCode()
|
||||
|
||||
override fun isEqual(val1: LookupSymbolKey, val2: LookupSymbolKey): Boolean = val1 == val2
|
||||
}
|
||||
|
||||
object FqNameExternalizer : DataExternalizer<FqName> {
|
||||
@@ -220,21 +225,6 @@ object StringExternalizer : DataExternalizer<String> {
|
||||
override fun read(input: DataInput): String = IOUtil.readString(input)
|
||||
}
|
||||
|
||||
object PathStringDescriptor : EnumeratorStringDescriptor() {
|
||||
override fun getHashCode(path: String): Int {
|
||||
return if (StringUtil.isEmpty(path)) 0 else FileUtil.toCanonicalPath(path).hashCode()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: String, val2: String?): Boolean {
|
||||
if (val1 == val2) return true
|
||||
if (val2 == null) return false
|
||||
|
||||
val path1 = FileUtil.toCanonicalPath(val1)
|
||||
val path2 = FileUtil.toCanonicalPath(val2)
|
||||
return path1 == path2
|
||||
}
|
||||
}
|
||||
|
||||
/** [DataExternalizer] that delegates to another [DataExternalizer] depending on the type of the object to externalize. */
|
||||
class DelegateDataExternalizer<T>(
|
||||
val types: List<Class<out T>>,
|
||||
@@ -328,7 +318,7 @@ private class CollectionExternalizerForPersistentHashMap<T>(
|
||||
* (see [CollectionExternalizerForPersistentHashMap]).
|
||||
*
|
||||
* Currently, we can't change the name or implementation of this class because it is still used by the `compiler-reference-index` module in
|
||||
* the Kotlin IDEA plugin and that code relies on this name and implementation being unchanged (see KT-62288).
|
||||
* the Kotlin IDEA plugin and that code relies on this name and implementation being unchanged (see KTIJ-27258).
|
||||
*
|
||||
* Once we remove that dependency, we can remove this class.
|
||||
*/
|
||||
@@ -345,7 +335,7 @@ object IntCollectionExternalizer :
|
||||
/** [DataExternalizer] for a [Collection]. */
|
||||
open class CollectionExternalizerV2<T, C : Collection<T>>(
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: (size: Int) -> MutableCollection<T>,
|
||||
private val newCollection: (size: Int) -> MutableCollection<T> = { size -> ArrayList(size) },
|
||||
) : DataExternalizer<C> {
|
||||
|
||||
override fun save(output: DataOutput, collection: C) {
|
||||
@@ -369,8 +359,6 @@ open class CollectionExternalizerV2<T, C : Collection<T>>(
|
||||
}
|
||||
}
|
||||
|
||||
object StringCollectionExternalizer : CollectionExternalizerV2<String, Collection<String>>(StringExternalizer, { size -> ArrayList(size) })
|
||||
|
||||
class ListExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
|
||||
CollectionExternalizerV2<T, List<T>>(elementExternalizer, { size -> ArrayList(size) })
|
||||
|
||||
|
||||
+1
-8
@@ -50,20 +50,13 @@ abstract class IncrementalCachesManager<PlatformCache : AbstractIncrementalCache
|
||||
|
||||
val closer = Closer.create()
|
||||
caches.forEach {
|
||||
closer.register(CacheCloser(it))
|
||||
closer.register(it)
|
||||
}
|
||||
closer.close()
|
||||
|
||||
isClosed = true
|
||||
}
|
||||
|
||||
private class CacheCloser(private val cache: BasicMapsOwner) : Closeable {
|
||||
|
||||
override fun close() {
|
||||
cache.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
open class IncrementalJvmCachesManager(
|
||||
|
||||
+3
-3
@@ -38,8 +38,8 @@ import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.incremental.parsing.classesFqNames
|
||||
import org.jetbrains.kotlin.incremental.storage.BasicFileToPathConverter
|
||||
import org.jetbrains.kotlin.incremental.storage.FileLocations
|
||||
import org.jetbrains.kotlin.incremental.storage.FileToAbsolutePathConverter
|
||||
import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
|
||||
import org.jetbrains.kotlin.incremental.util.ExceptionLocation
|
||||
import org.jetbrains.kotlin.incremental.util.reportException
|
||||
@@ -90,8 +90,8 @@ abstract class IncrementalCompilerRunner<
|
||||
fileLocations: FileLocations?,
|
||||
transaction: CompilationTransaction,
|
||||
) = IncrementalCompilationContext(
|
||||
pathConverterForSourceFiles = fileLocations?.let { it.getRelocatablePathConverterForSourceFiles() } ?: FileToAbsolutePathConverter,
|
||||
pathConverterForOutputFiles = fileLocations?.let { it.getRelocatablePathConverterForOutputFiles() } ?: FileToAbsolutePathConverter,
|
||||
pathConverterForSourceFiles = fileLocations?.let { it.getRelocatablePathConverterForSourceFiles() } ?: BasicFileToPathConverter,
|
||||
pathConverterForOutputFiles = fileLocations?.let { it.getRelocatablePathConverterForOutputFiles() } ?: BasicFileToPathConverter,
|
||||
transaction = transaction,
|
||||
reporter = reporter,
|
||||
trackChangesInLookupCache = shouldTrackChangesInLookupCache,
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@ class InputsCache(
|
||||
// generatedFiles can contain multiple entries with the same source file
|
||||
// for example Kapt3 IC will generate a .java stub and .class stub for each source file
|
||||
fun registerOutputForSourceFiles(generatedFiles: List<GeneratedFile>) {
|
||||
val sourceToOutput = MultiMap<File, File>()
|
||||
val sourceToOutput = MultiMap.createLinked<File, File>()
|
||||
|
||||
for (generatedFile in generatedFiles) {
|
||||
for (source in generatedFile.sourceFiles) {
|
||||
@@ -61,7 +61,7 @@ class InputsCache(
|
||||
}
|
||||
|
||||
for ((source, outputs) in sourceToOutput.entrySet()) {
|
||||
sourceToOutputMap[source] = outputs
|
||||
sourceToOutputMap[source] = outputs.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-17
@@ -18,41 +18,39 @@ package org.jetbrains.kotlin.incremental.snapshots
|
||||
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.storage.BasicStringMap
|
||||
import org.jetbrains.kotlin.incremental.storage.PathStringDescriptor
|
||||
import org.jetbrains.kotlin.incremental.storage.AbstractBasicMap
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class FileSnapshotMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<FileSnapshot>(storageFile, PathStringDescriptor, FileSnapshotExternalizer, icContext) {
|
||||
|
||||
override fun dumpValue(value: FileSnapshot): String =
|
||||
value.toString()
|
||||
|
||||
) : AbstractBasicMap<File, FileSnapshot>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
FileSnapshotExternalizer,
|
||||
icContext
|
||||
) {
|
||||
@Synchronized
|
||||
fun compareAndUpdate(newFiles: Iterable<File>): ChangedFiles.Known {
|
||||
val snapshotProvider = SimpleFileSnapshotProviderImpl()
|
||||
val newOrModified = ArrayList<File>()
|
||||
val removed = ArrayList<File>()
|
||||
|
||||
val newPaths = newFiles.mapTo(HashSet(), transform = pathConverter::toPath)
|
||||
for (oldPath in storage.keys) {
|
||||
if (oldPath !in newPaths) {
|
||||
storage.remove(oldPath)
|
||||
removed.add(pathConverter.toFile(oldPath))
|
||||
val newFilesSet = newFiles.toSet()
|
||||
for (oldFile in keys) {
|
||||
if (oldFile !in newFilesSet) {
|
||||
remove(oldFile)
|
||||
removed.add(oldFile)
|
||||
}
|
||||
}
|
||||
|
||||
for (path in newPaths) {
|
||||
val file = pathConverter.toFile(path)
|
||||
val oldSnapshot = storage[path]
|
||||
for (file in newFilesSet) {
|
||||
val oldSnapshot = this[file]
|
||||
val newSnapshot = snapshotProvider[file]
|
||||
|
||||
if (oldSnapshot == null || oldSnapshot != newSnapshot) {
|
||||
newOrModified.add(file)
|
||||
storage[path] = newSnapshot
|
||||
this[file] = newSnapshot
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-15
@@ -6,24 +6,21 @@
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import java.io.File
|
||||
|
||||
class SourceToOutputFilesMap(
|
||||
storageFile: File,
|
||||
icContext: IncrementalCompilationContext,
|
||||
) : BasicStringMap<Collection<String>>(storageFile, PathStringDescriptor, StringCollectionExternalizer, icContext) {
|
||||
operator fun set(sourceFile: File, outputFiles: Collection<File>) {
|
||||
storage[icContext.pathConverterForSourceFiles.toPath(sourceFile)] =
|
||||
outputFiles.toSet().map(icContext.pathConverterForOutputFiles::toPath)
|
||||
) : AppendableSetBasicMap<File, File>(
|
||||
storageFile,
|
||||
icContext.fileDescriptorForSourceFiles,
|
||||
icContext.fileDescriptorForOutputFiles,
|
||||
icContext
|
||||
) {
|
||||
@Synchronized
|
||||
fun getAndRemove(sourceFile: File): Set<File>? {
|
||||
return get(sourceFile).also {
|
||||
remove(sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): Collection<File>? =
|
||||
storage[icContext.pathConverterForSourceFiles.toPath(sourceFile)]?.map(icContext.pathConverterForOutputFiles::toFile)
|
||||
|
||||
override fun dumpValue(value: Collection<String>) =
|
||||
value.dumpCollection()
|
||||
|
||||
fun getAndRemove(file: File): Collection<File>? =
|
||||
get(file).also { storage.remove(icContext.pathConverterForSourceFiles.toPath(file)) }
|
||||
}
|
||||
}
|
||||
|
||||
+21
-17
@@ -60,49 +60,53 @@ class SourceToOutputFilesMapTest {
|
||||
|
||||
@Test
|
||||
fun testSetOneGetReturnsOne() {
|
||||
stofMap[fooDotKt] = listOf(fooDotClass)
|
||||
stofMap[fooDotKt] = setOf(fooDotClass)
|
||||
|
||||
assertEquals(listOf(fooDotClass), stofMap[fooDotKt])
|
||||
assertEquals(setOf(fooDotClass), stofMap[fooDotKt])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetDupeGetReturnsUnique() {
|
||||
stofMap[fooDotKt] = listOf(fooDotClass, fooDotClass)
|
||||
stofMap.append(fooDotKt, fooDotClass)
|
||||
stofMap.append(fooDotKt, fooDotClass)
|
||||
|
||||
assertEquals(listOf(fooDotClass), stofMap[fooDotKt])
|
||||
assertEquals(setOf(fooDotClass), stofMap[fooDotKt])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetOverwriteGetReturnsNew() {
|
||||
val fooKtDotClass = classesDir.resolve("FooKt.class")
|
||||
stofMap[fooDotKt] = listOf(fooDotClass)
|
||||
stofMap[fooDotKt] = listOf(fooKtDotClass)
|
||||
stofMap[fooDotKt] = setOf(fooDotClass)
|
||||
stofMap[fooDotKt] = setOf(fooKtDotClass)
|
||||
|
||||
assertEquals(listOf(fooKtDotClass), stofMap[fooDotKt])
|
||||
assertEquals(setOf(fooKtDotClass), stofMap[fooDotKt])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetRelativeFails() {
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
stofMap[fooDotKt] = listOf(File("relativePath"))
|
||||
fun testSetRelativePathFails() {
|
||||
assertFailsWith<IllegalStateException> {
|
||||
stofMap[fooDotKt] = setOf(File("relativePath"))
|
||||
}
|
||||
assertFailsWith<IllegalStateException> {
|
||||
stofMap[File("relativePath")] = setOf(fooDotClass)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetRelativeFails() {
|
||||
stofMap[fooDotKt] = listOf(fooDotClass)
|
||||
fun testGetRelativePathFails() {
|
||||
stofMap[fooDotKt] = setOf(fooDotClass)
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
stofMap[fooDotKt.relativeTo(srcDir)]
|
||||
assertFailsWith<IllegalStateException> {
|
||||
stofMap[File("relativePath")]
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetAndRemove() {
|
||||
stofMap[fooDotKt] = listOf(fooDotClass)
|
||||
stofMap[fooDotKt] = setOf(fooDotClass)
|
||||
|
||||
assertEquals(listOf(fooDotClass), stofMap.getAndRemove(fooDotKt))
|
||||
assertEquals(setOf(fooDotClass), stofMap.getAndRemove(fooDotKt))
|
||||
assertNull(stofMap[fooDotKt])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,6 @@ class CodeConformanceTest : TestCase() {
|
||||
"These files are affected:\n%s",
|
||||
allowedFiles = listOf(
|
||||
"analysis/light-classes-base/src/org/jetbrains/kotlin/asJava/classes/KotlinClassInnerStuffCache.kt",
|
||||
"build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt",
|
||||
"compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/CliVirtualFileFinder.kt",
|
||||
"compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt",
|
||||
"compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/index/JvmDependenciesIndexImpl.kt",
|
||||
|
||||
Reference in New Issue
Block a user