From e28f1888e86ef6831cc27892c7ee9f02cdd08110 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 18 Jan 2016 18:49:33 +0300 Subject: [PATCH] Minor: extract most of JPS (old IC) specific part from IncrementalCacheImpl Original commit: 5889ba42933ebf32da9585b0b33900fd943694aa --- .../kotlin/jps/build/KotlinBuilder.kt | 6 +- .../jps/incremental/IncrementalCacheImpl.kt | 222 +++--------------- .../incremental/JpsIncrementalCacheImpl.kt | 216 +++++++++++++++++ 3 files changed, 250 insertions(+), 194 deletions(-) create mode 100644 jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt index 1fefd3dacc2..9b41fbbfa66 100644 --- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt @@ -695,7 +695,7 @@ private fun processChanges( compiledFiles: Set, allCompiledFiles: MutableSet, dataManager: BuildDataManager, - caches: Collection, + caches: Collection, compilationResult: CompilationResult, fsOperations: FSOperationsHelper ) { @@ -710,7 +710,7 @@ private fun processChanges( private fun CompilationResult.doProcessChanges( compiledFiles: Set, allCompiledFiles: MutableSet, - caches: Collection, + caches: Collection, fsOperations: FSOperationsHelper ) { KotlinBuilder.LOG.debug("compilationResult = $this") @@ -826,7 +826,7 @@ private fun getLookupTracker(project: JpsProject): LookupTracker { return lookupTracker } -private fun getIncrementalCaches(chunk: ModuleChunk, context: CompileContext): Map { +private fun getIncrementalCaches(chunk: ModuleChunk, context: CompileContext): Map { val dependentTargets = getDependentTargets(chunk, context) val dataManager = context.projectDescriptor.dataManager diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt index 5a7efdc5d5b..21537fd9d34 100644 --- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt +++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt @@ -17,20 +17,15 @@ package org.jetbrains.kotlin.jps.incremental import com.google.protobuf.MessageLite -import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName import com.intellij.util.SmartList import com.intellij.util.io.BooleanDataDescriptor import com.intellij.util.io.EnumeratorStringDescriptor -import gnu.trove.THashSet import org.jetbrains.annotations.TestOnly import org.jetbrains.jps.builders.storage.BuildDataPaths -import org.jetbrains.jps.builders.storage.StorageProvider import org.jetbrains.jps.incremental.ModuleBuildTarget -import org.jetbrains.jps.incremental.storage.BuildDataManager import org.jetbrains.jps.incremental.storage.PathStringDescriptor import org.jetbrains.kotlin.config.IncrementalCompilation -import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames import org.jetbrains.kotlin.jps.build.GeneratedJvmClass import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.incremental.storage.* @@ -49,30 +44,30 @@ import org.jetbrains.kotlin.serialization.deserialization.TypeTable import org.jetbrains.kotlin.serialization.deserialization.supertypes import org.jetbrains.kotlin.serialization.jvm.BitEncoding import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil -import org.jetbrains.org.objectweb.asm.* +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor +import org.jetbrains.org.objectweb.asm.FieldVisitor +import org.jetbrains.org.objectweb.asm.Opcodes import java.io.File import java.security.MessageDigest import java.util.* val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin" -class IncrementalCacheImpl( +open class IncrementalCacheImpl( private val target: ModuleBuildTarget, paths: BuildDataPaths ) : BasicMapsOwner(), IncrementalCache { companion object { - val PROTO_MAP = "proto" - val CONSTANTS_MAP = "constants" - val INLINE_FUNCTIONS = "inline-functions" - val PACKAGE_PARTS = "package-parts" - val MULTIFILE_CLASS_FACADES = "multifile-class-facades" - val MULTIFILE_CLASS_PARTS = "multifile-class-parts" - val SOURCE_TO_CLASSES = "source-to-classes" - val DIRTY_OUTPUT_CLASSES = "dirty-output-classes" - val DIRTY_INLINE_FUNCTIONS = "dirty-inline-functions" - val INLINED_TO = "inlined-to" - val SUBTYPES = "subtypes" - val SUPERTYPES = "supertypes" + private val PROTO_MAP = "proto" + private val CONSTANTS_MAP = "constants" + private val PACKAGE_PARTS = "package-parts" + private val MULTIFILE_CLASS_FACADES = "multifile-class-facades" + private val MULTIFILE_CLASS_PARTS = "multifile-class-parts" + private val SOURCE_TO_CLASSES = "source-to-classes" + private val DIRTY_OUTPUT_CLASSES = "dirty-output-classes" + private val SUBTYPES = "subtypes" + private val SUPERTYPES = "supertypes" private val MODULE_MAPPING_FILE_NAME = "." + ModuleMapping.MAPPING_FILE_EXT } @@ -86,35 +81,29 @@ class IncrementalCacheImpl( return registerMap(map) } - private val String.storageFile: File + protected val String.storageFile: File get() = File(baseDir, this + "." + CACHE_EXTENSION) private val protoMap = registerMap(ProtoMap(PROTO_MAP.storageFile)) private val constantsMap = registerMap(ConstantsMap(CONSTANTS_MAP.storageFile)) - private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile)) private val packagePartMap = registerMap(PackagePartMap(PACKAGE_PARTS.storageFile)) private val multifileClassFacadeMap = registerMap(MultifileClassFacadeMap(MULTIFILE_CLASS_FACADES.storageFile)) private val multifileClassPartMap = registerMap(MultifileClassPartMap(MULTIFILE_CLASS_PARTS.storageFile)) private val sourceToClassesMap = registerMap(SourceToClassesMap(SOURCE_TO_CLASSES.storageFile)) private val dirtyOutputClassesMap = registerMap(DirtyOutputClassesMap(DIRTY_OUTPUT_CLASSES.storageFile)) - private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile)) - private val inlinedTo = registerMap(InlineFunctionsFilesMap(INLINED_TO.storageFile)) private val subtypesMap = registerExperimentalMap(SubtypesMap(SUBTYPES.storageFile)) private val supertypesMap = registerExperimentalMap(SupertypesMap(SUPERTYPES.storageFile)) private val dependents = arrayListOf() private val outputDir by lazy(LazyThreadSafetyMode.NONE) { requireNotNull(target.outputDir) { "Target is expected to have output directory: $target" } } - private val dependentsWithThis: Sequence - get() = sequenceOf(this).plus(dependents.asSequence()) + protected val dependentsWithThis: Sequence + get() = sequenceOf(this).plus(dependents.asSequence()) internal val dependentCaches: Iterable - get() = dependents + get() = dependents override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) { - if (!IncrementalCompilation.isExperimental()) { - inlinedTo.add(fromPath, jvmSignature, toPath) - } } fun addDependentCache(cache: IncrementalCacheImpl) { @@ -132,28 +121,9 @@ class IncrementalCacheImpl( } } - fun getFilesToReinline(): Collection { - val result = THashSet(FileUtil.PATH_HASHING_STRATEGY) - - for ((className, functions) in dirtyInlineFunctionsMap.getEntries()) { - val classFilePath = getClassFilePath(className.internalName) - - for (cache in dependentsWithThis) { - val targetFiles = functions.flatMap { cache.inlinedTo[classFilePath, it] } - result.addAll(targetFiles) - } - } - - return result.map { File(it) } - } - fun getSubtypesOf(className: FqName): Sequence = subtypesMap[className].asSequence() - fun cleanDirtyInlineFunctions() { - dirtyInlineFunctionsMap.clean() - } - override fun getClassFilePath(internalClassName: String): String { return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath) } @@ -184,7 +154,7 @@ class IncrementalCacheImpl( protoMap.process(kotlinClass, isPackage = true) + constantsMap.process(kotlinClass) + - inlineFunctionsMap.process(kotlinClass, isPackage = true) + additionalProcessChangedClass(kotlinClass, isPackage = true) } header.isCompatibleMultifileClassKind() -> { val partNames = kotlinClass.classHeader.filePartClassNames?.toList() @@ -193,7 +163,7 @@ class IncrementalCacheImpl( // TODO NO_CHANGES? (delegates only) constantsMap.process(kotlinClass) + - inlineFunctionsMap.process(kotlinClass, isPackage = true) + additionalProcessChangedClass(kotlinClass, isPackage = true) } header.isCompatibleMultifileClassPartKind() -> { assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" } @@ -202,14 +172,14 @@ class IncrementalCacheImpl( protoMap.process(kotlinClass, isPackage = true) + constantsMap.process(kotlinClass) + - inlineFunctionsMap.process(kotlinClass, isPackage = true) + additionalProcessChangedClass(kotlinClass, isPackage = true) } header.isCompatibleClassKind() && !header.isLocalClass -> { addToClassStorage(kotlinClass) protoMap.process(kotlinClass, isPackage = false) + constantsMap.process(kotlinClass) + - inlineFunctionsMap.process(kotlinClass, isPackage = false) + additionalProcessChangedClass(kotlinClass, isPackage = false) } else -> CompilationResult.NO_CHANGES } @@ -218,6 +188,8 @@ class IncrementalCacheImpl( return changesInfo } + protected open fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) = CompilationResult.NO_CHANGES + private fun CompilationResult.logIfSomethingChanged(className: JvmClassName) { if (this == CompilationResult.NO_CHANGES) return @@ -287,15 +259,19 @@ class IncrementalCacheImpl( multifileClassFacadeMap.remove(it) multifileClassPartMap.remove(it) constantsMap.remove(it) - inlineFunctionsMap.remove(it) } + additionalProcessRemovedClasses(dirtyClasses) + removeAllFromClassStorage(dirtyClasses) dirtyOutputClassesMap.clean() return changesInfo } + protected open fun additionalProcessRemovedClasses(dirtyClasses: List) { + } + override fun getObsoletePackageParts(): Collection { val obsoletePackageParts = dirtyOutputClassesMap.getDirtyOutputClasses().filter { packagePartMap.isPackagePart(JvmClassName.byInternalName(it)) } @@ -450,88 +426,6 @@ class IncrementalCacheImpl( value.dumpMap(Any::toString) } - private inner class InlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringToLongMapExternalizer) { - private fun getInlineFunctionsMap(bytes: ByteArray): Map { - val result = HashMap() - - val inlineFunctions = inlineFunctionsJvmNames(bytes) - if (inlineFunctions.isEmpty()) return emptyMap() - - ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { - override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { - val dummyClassWriter = ClassWriter(Opcodes.ASM5) - - return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) { - override fun visitEnd() { - val jvmName = name + desc - if (jvmName !in inlineFunctions) return - - val dummyBytes = dummyClassWriter.toByteArray()!! - val hash = dummyBytes.md5() - result[jvmName] = hash - } - } - } - - }, 0) - - return result - } - - fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult { - return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.fileContents), isPackage) - } - - private fun put(className: JvmClassName, newMap: Map, isPackage: Boolean): CompilationResult { - val internalName = className.internalName - val oldMap = storage[internalName] ?: emptyMap() - - val added = hashSetOf() - val changed = hashSetOf() - val allFunctions = oldMap.keys + newMap.keys - - for (fn in allFunctions) { - val oldHash = oldMap[fn] - val newHash = newMap[fn] - - when { - oldHash == null -> added.add(fn) - oldHash != newHash -> changed.add(fn) - } - } - - when { - newMap.isNotEmpty() -> storage[internalName] = newMap - else -> storage.remove(internalName) - } - - if (changed.isNotEmpty()) { - dirtyInlineFunctionsMap.put(className, changed.toList()) - } - - val changes = - if (IncrementalCompilation.isExperimental()) { - val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars - // TODO get name in better way instead of using substringBefore - (added.asSequence() + changed.asSequence()).map { ChangeInfo.MembersChanged(fqName, listOf(it.substringBefore("("))) } - } - else { - emptySequence() - } - - return CompilationResult(inlineChanged = changed.isNotEmpty(), - inlineAdded = added.isNotEmpty(), - changes = changes) - } - - fun remove(className: JvmClassName) { - storage.remove(className.internalName) - } - - override fun dumpValue(value: Map): String = - value.dumpMap { java.lang.Long.toHexString(it) } - } - private inner class PackagePartMap(storageFile: File) : BasicStringMap(storageFile, BooleanDataDescriptor.INSTANCE) { fun addPackagePart(className: JvmClassName) { storage[className.internalName] = true @@ -662,44 +556,6 @@ class IncrementalCacheImpl( override fun dumpValue(value: Boolean) = "" } - - private inner class DirtyInlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringCollectionExternalizer) { - fun getEntries(): Map> = - storage.keys.toMapBy(JvmClassName::byInternalName) { storage[it]!! } - - fun put(className: JvmClassName, changedFunctions: List) { - storage[className.internalName] = changedFunctions - } - - override fun dumpValue(value: Collection) = value.dumpCollection() - } - - - /** - * Mapping: (sourceFile+inlineFunction)->(targetFiles) - * - * Where: - * * sourceFile - path to some kotlin source - * * inlineFunction - jvmSignature of some inline function in source file - * * target files - collection of files inlineFunction has been inlined to - */ - private inner class InlineFunctionsFilesMap(storageFile: File) : BasicMap>(storageFile, PathFunctionPairKeyDescriptor, PathCollectionExternalizer) { - fun add(sourcePath: String, jvmSignature: String, targetPath: String) { - val key = PathFunctionPair(sourcePath, jvmSignature) - storage.append(key, targetPath) - } - - operator fun get(sourcePath: String, jvmSignature: String): Collection { - val key = PathFunctionPair(sourcePath, jvmSignature) - return storage[key] ?: emptySet() - } - - override fun dumpKey(key: PathFunctionPair): String = - "(${key.path}, ${key.function})" - - override fun dumpValue(value: Collection) = - value.dumpCollection() - } } sealed class ChangeInfo(val fqName: FqName) { @@ -738,23 +594,7 @@ data class CompilationResult( changes + other.changes) } -private class KotlinIncrementalStorageProvider( - private val target: ModuleBuildTarget, - private val paths: BuildDataPaths -) : StorageProvider() { - - override fun equals(other: Any?) = other is KotlinIncrementalStorageProvider && target == other.target - - override fun hashCode() = target.hashCode() - - override fun createStorage(targetDataDir: File): IncrementalCacheImpl = - IncrementalCacheImpl(target, paths) -} - -fun BuildDataManager.getKotlinCache(target: ModuleBuildTarget): IncrementalCacheImpl = - getStorage(target, KotlinIncrementalStorageProvider(target, dataPaths)) - -private fun ByteArray.md5(): Long { +fun ByteArray.md5(): Long { val d = MessageDigest.getInstance("MD5").digest(this)!! return ((d[0].toLong() and 0xFFL) or ((d[1].toLong() and 0xFFL) shl 8) @@ -768,7 +608,7 @@ private fun ByteArray.md5(): Long { } @TestOnly -private fun , V> Map.dumpMap(dumpValue: (V)->String): String = +fun , V> Map.dumpMap(dumpValue: (V)->String): String = buildString { append("{") for (key in keys.sorted()) { diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt new file mode 100644 index 00000000000..601e45a2f62 --- /dev/null +++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt @@ -0,0 +1,216 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.jps.incremental + +import com.intellij.openapi.util.io.FileUtil +import gnu.trove.THashSet +import org.jetbrains.jps.builders.storage.BuildDataPaths +import org.jetbrains.jps.builders.storage.StorageProvider +import org.jetbrains.jps.incremental.ModuleBuildTarget +import org.jetbrains.jps.incremental.storage.BuildDataManager +import org.jetbrains.jps.incremental.storage.StorageOwner +import org.jetbrains.kotlin.config.IncrementalCompilation +import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames +import org.jetbrains.kotlin.jps.incremental.storage.* +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.org.objectweb.asm.* +import java.io.File +import java.util.* + +class JpsIncrementalCacheImpl( + target: ModuleBuildTarget, + paths: BuildDataPaths +) : IncrementalCacheImpl(target, paths), StorageOwner { + + private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile)) + private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile)) + private val inlinedTo = registerMap(InlineFunctionsFilesMap(INLINED_TO.storageFile)) + + override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) { + if (!IncrementalCompilation.isExperimental()) { + inlinedTo.add(fromPath, jvmSignature, toPath) + } + } + + override fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) = + inlineFunctionsMap.process(kotlinClass, isPackage) + + override fun additionalProcessRemovedClasses(dirtyClasses: List) { + dirtyClasses.forEach { inlineFunctionsMap.remove(it) } + } + + fun getFilesToReinline(): Collection { + val result = THashSet(FileUtil.PATH_HASHING_STRATEGY) + + for ((className, functions) in dirtyInlineFunctionsMap.getEntries()) { + val classFilePath = getClassFilePath(className.internalName) + + for (cache in dependentsWithThis) { + val targetFiles = functions.flatMap { (cache as JpsIncrementalCacheImpl).inlinedTo[classFilePath, it] } + result.addAll(targetFiles) + } + } + + return result.map { File(it) } + } + + fun cleanDirtyInlineFunctions() { + dirtyInlineFunctionsMap.clean() + } + + private inner class InlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringToLongMapExternalizer) { + private fun getInlineFunctionsMap(bytes: ByteArray): Map { + val result = HashMap() + + val inlineFunctions = inlineFunctionsJvmNames(bytes) + if (inlineFunctions.isEmpty()) return emptyMap() + + ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { + override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { + val dummyClassWriter = ClassWriter(Opcodes.ASM5) + + return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) { + override fun visitEnd() { + val jvmName = name + desc + if (jvmName !in inlineFunctions) return + + val dummyBytes = dummyClassWriter.toByteArray()!! + val hash = dummyBytes.md5() + result[jvmName] = hash + } + } + } + + }, 0) + + return result + } + + fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult { + return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.fileContents), isPackage) + } + + private fun put(className: JvmClassName, newMap: Map, isPackage: Boolean): CompilationResult { + val internalName = className.internalName + val oldMap = storage[internalName] ?: emptyMap() + + val added = hashSetOf() + val changed = hashSetOf() + val allFunctions = oldMap.keys + newMap.keys + + for (fn in allFunctions) { + val oldHash = oldMap[fn] + val newHash = newMap[fn] + + when { + oldHash == null -> added.add(fn) + oldHash != newHash -> changed.add(fn) + } + } + + when { + newMap.isNotEmpty() -> storage[internalName] = newMap + else -> storage.remove(internalName) + } + + if (changed.isNotEmpty()) { + dirtyInlineFunctionsMap.put(className, changed.toList()) + } + + val changes = + if (IncrementalCompilation.isExperimental()) { + val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars + // TODO get name in better way instead of using substringBefore + (added.asSequence() + changed.asSequence()).map { ChangeInfo.MembersChanged(fqName, listOf(it.substringBefore("("))) } + } + else { + emptySequence() + } + + return CompilationResult(inlineChanged = changed.isNotEmpty(), + inlineAdded = added.isNotEmpty(), + changes = changes) + } + + fun remove(className: JvmClassName) { + storage.remove(className.internalName) + } + + override fun dumpValue(value: Map): String = + value.dumpMap { java.lang.Long.toHexString(it) } + } + + private inner class DirtyInlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringCollectionExternalizer) { + fun getEntries(): Map> = + storage.keys.toMapBy(JvmClassName::byInternalName) { storage[it]!! } + + fun put(className: JvmClassName, changedFunctions: List) { + storage[className.internalName] = changedFunctions + } + + override fun dumpValue(value: Collection) = value.dumpCollection() + } + + /** + * Mapping: (sourceFile+inlineFunction)->(targetFiles) + * + * Where: + * * sourceFile - path to some kotlin source + * * inlineFunction - jvmSignature of some inline function in source file + * * target files - collection of files inlineFunction has been inlined to + */ + private inner class InlineFunctionsFilesMap(storageFile: File) : BasicMap>(storageFile, PathFunctionPairKeyDescriptor, PathCollectionExternalizer) { + fun add(sourcePath: String, jvmSignature: String, targetPath: String) { + val key = PathFunctionPair(sourcePath, jvmSignature) + storage.append(key, targetPath) + } + + operator fun get(sourcePath: String, jvmSignature: String): Collection { + val key = PathFunctionPair(sourcePath, jvmSignature) + return storage[key] ?: emptySet() + } + + override fun dumpKey(key: PathFunctionPair): String = + "(${key.path}, ${key.function})" + + override fun dumpValue(value: Collection) = + value.dumpCollection() + } + + companion object { + private val INLINE_FUNCTIONS = "inline-functions" + private val DIRTY_INLINE_FUNCTIONS = "dirty-inline-functions" + private val INLINED_TO = "inlined-to" + } +} + +private class KotlinIncrementalStorageProvider( + private val target: ModuleBuildTarget, + private val paths: BuildDataPaths +) : StorageProvider() { + + override fun equals(other: Any?) = other is KotlinIncrementalStorageProvider && target == other.target + + override fun hashCode() = target.hashCode() + + override fun createStorage(targetDataDir: File): JpsIncrementalCacheImpl = + JpsIncrementalCacheImpl(target, paths) +} + +fun BuildDataManager.getKotlinCache(target: ModuleBuildTarget): JpsIncrementalCacheImpl = + getStorage(target, KotlinIncrementalStorageProvider(target, dataPaths)) +