IC of sealed classes

Supported case then children of sealed classes could be declared
anywhere in a module. If list of classes implementing sealing class
changes the sealed class and all its inheritors should be recompiled
(now sealed class should be compiled together with children in order
to calculate all possible inheritors at compile time) and and
invalidated (as they could have when operators).
This commit is contained in:
Andrey Uskov
2020-12-09 17:33:09 +03:00
committed by Andrey Uskov
parent a0651cdba7
commit 36f99156fd
11 changed files with 183 additions and 20 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.incremental
import com.intellij.util.io.EnumeratorStringDescriptor import com.intellij.util.io.EnumeratorStringDescriptor
import org.jetbrains.kotlin.incremental.storage.* import org.jetbrains.kotlin.incremental.storage.*
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.TypeTable import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.deserialization.supertypes import org.jetbrains.kotlin.metadata.deserialization.supertypes
@@ -34,12 +35,15 @@ interface IncrementalCacheCommon {
val thisWithDependentCaches: Iterable<AbstractIncrementalCache<*>> val thisWithDependentCaches: Iterable<AbstractIncrementalCache<*>>
fun classesFqNamesBySources(files: Iterable<File>): Collection<FqName> fun classesFqNamesBySources(files: Iterable<File>): Collection<FqName>
fun getSubtypesOf(className: FqName): Sequence<FqName> fun getSubtypesOf(className: FqName): Sequence<FqName>
fun getSupertypesOf(className: FqName): Sequence<FqName>
fun getSourceFileIfClass(fqName: FqName): File? fun getSourceFileIfClass(fqName: FqName): File?
fun markDirty(removedAndCompiledSources: Collection<File>) fun markDirty(removedAndCompiledSources: Collection<File>)
fun clearCacheForRemovedClasses(changesCollector: ChangesCollector) fun clearCacheForRemovedClasses(changesCollector: ChangesCollector)
fun getComplementaryFilesRecursive(dirtyFiles: Collection<File>): Collection<File> fun getComplementaryFilesRecursive(dirtyFiles: Collection<File>): Collection<File>
fun updateComplementaryFiles(dirtyFiles: Collection<File>, expectActualTracker: ExpectActualTrackerImpl) fun updateComplementaryFiles(dirtyFiles: Collection<File>, expectActualTracker: ExpectActualTrackerImpl)
fun dump(): String fun dump(): String
fun isSealed(className: FqName): Boolean?
} }
/** /**
@@ -50,6 +54,7 @@ abstract class AbstractIncrementalCache<ClassName>(
protected val pathConverter: FileToPathConverter protected val pathConverter: FileToPathConverter
) : BasicMapsOwner(workingDir), IncrementalCacheCommon { ) : BasicMapsOwner(workingDir), IncrementalCacheCommon {
companion object { companion object {
private val CLASS_ATTRIBUTES = "class-attributes"
private val SUBTYPES = "subtypes" private val SUBTYPES = "subtypes"
private val SUPERTYPES = "supertypes" private val SUPERTYPES = "supertypes"
private val CLASS_FQ_NAME_TO_SOURCE = "class-fq-name-to-source" private val CLASS_FQ_NAME_TO_SOURCE = "class-fq-name-to-source"
@@ -71,6 +76,7 @@ abstract class AbstractIncrementalCache<ClassName>(
result result
} }
internal val classAttributesMap = registerMap(ClassAttributesMap(CLASS_ATTRIBUTES.storageFile))
private val subtypesMap = registerMap(SubtypesMap(SUBTYPES.storageFile)) private val subtypesMap = registerMap(SubtypesMap(SUBTYPES.storageFile))
private val supertypesMap = registerMap(SupertypesMap(SUPERTYPES.storageFile)) private val supertypesMap = registerMap(SupertypesMap(SUPERTYPES.storageFile))
protected val classFqNameToSourceMap = registerMap(ClassFqNameToSourceMap(CLASS_FQ_NAME_TO_SOURCE.storageFile, pathConverter)) protected val classFqNameToSourceMap = registerMap(ClassFqNameToSourceMap(CLASS_FQ_NAME_TO_SOURCE.storageFile, pathConverter))
@@ -90,6 +96,14 @@ abstract class AbstractIncrementalCache<ClassName>(
override fun getSubtypesOf(className: FqName): Sequence<FqName> = override fun getSubtypesOf(className: FqName): Sequence<FqName> =
subtypesMap[className].asSequence() subtypesMap[className].asSequence()
override fun getSupertypesOf(className: FqName): Sequence<FqName> {
return supertypesMap[className].asSequence()
}
override fun isSealed(className: FqName): Boolean? {
return classAttributesMap[className]?.isSealed
}
override fun getSourceFileIfClass(fqName: FqName): File? = override fun getSourceFileIfClass(fqName: FqName): File? =
classFqNameToSourceMap[fqName] classFqNameToSourceMap[fqName]
@@ -118,6 +132,7 @@ abstract class AbstractIncrementalCache<ClassName>(
supertypesMap[child] = parents supertypesMap[child] = parents
classFqNameToSourceMap[child] = srcFile classFqNameToSourceMap[child] = srcFile
classAttributesMap[child] = ICClassesAttributes(ProtoBuf.Modality.SEALED == Flags.MODALITY.get(proto.flags))
} }
protected fun removeAllFromClassStorage(removedClasses: Collection<FqName>, changesCollector: ChangesCollector) { protected fun removeAllFromClassStorage(removedClasses: Collection<FqName>, changesCollector: ChangesCollector) {
@@ -152,7 +167,10 @@ abstract class AbstractIncrementalCache<ClassName>(
} }
} }
removedFqNames.forEach { classFqNameToSourceMap.remove(it) } removedFqNames.forEach {
classFqNameToSourceMap.remove(it)
classAttributesMap.remove(it)
}
} }
protected class ClassFqNameToSourceMap( protected class ClassFqNameToSourceMap(
@@ -19,12 +19,14 @@ package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.supertypes
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.serialization.deserialization.getClassId import org.jetbrains.kotlin.serialization.deserialization.getClassId
class ChangesCollector { class ChangesCollector {
private val removedMembers = hashMapOf<FqName, MutableSet<String>>() private val removedMembers = hashMapOf<FqName, MutableSet<String>>()
private val changedParents = hashMapOf<FqName, MutableSet<FqName>>()
private val changedMembers = hashMapOf<FqName, MutableSet<String>>() private val changedMembers = hashMapOf<FqName, MutableSet<String>>()
private val areSubclassesAffected = hashMapOf<FqName, Boolean>() private val areSubclassesAffected = hashMapOf<FqName, Boolean>()
@@ -47,6 +49,10 @@ class ChangesCollector {
changes.add(ChangeInfo.SignatureChanged(fqName, areSubclassesAffected)) changes.add(ChangeInfo.SignatureChanged(fqName, areSubclassesAffected))
} }
for ((fqName, changedParents) in changedParents) {
changes.add(ChangeInfo.ParentsChanged(fqName, changedParents))
}
return changes return changes
} }
@@ -79,12 +85,12 @@ class ChangesCollector {
} }
if (oldData == null) { if (oldData == null) {
newData!!.collectAll(isRemoved = false, collectAllMembersForNewClass = collectAllMembersForNewClass) newData!!.collectAll(isRemoved = false, isAdded = true, collectAllMembersForNewClass = collectAllMembersForNewClass)
return return
} }
if (newData == null) { if (newData == null) {
oldData.collectAll(isRemoved = true) oldData.collectAll(isRemoved = true, isAdded = false)
return return
} }
@@ -98,6 +104,7 @@ class ChangesCollector {
collectSignature(oldData, diff.areSubclassesAffected) collectSignature(oldData, diff.areSubclassesAffected)
} }
collectChangedMembers(fqName, diff.changedMembersNames) collectChangedMembers(fqName, diff.changedMembersNames)
addChangedParents(fqName, diff.changedSupertypes)
} }
is PackagePartProtoData -> { is PackagePartProtoData -> {
collectSignature(oldData, areSubclassesAffected = true) collectSignature(oldData, areSubclassesAffected = true)
@@ -121,10 +128,11 @@ class ChangesCollector {
private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> = private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet() members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
private fun ProtoData.collectAll(isRemoved: Boolean, collectAllMembersForNewClass: Boolean = false) = //TODO remember all sealed parent classes
private fun ProtoData.collectAll(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) =
when (this) { when (this) {
is PackagePartProtoData -> collectAllFromPackage(isRemoved) is PackagePartProtoData -> collectAllFromPackage(isRemoved)
is ClassProtoData -> collectAllFromClass(isRemoved, collectAllMembersForNewClass) is ClassProtoData -> collectAllFromClass(isRemoved, isAdded, collectAllMembersForNewClass)
} }
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) { private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
@@ -143,7 +151,7 @@ class ChangesCollector {
} }
} }
private fun ClassProtoData.collectAllFromClass(isRemoved: Boolean, collectAllMembersForNewClass: Boolean = false) { private fun ClassProtoData.collectAllFromClass(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) {
val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName() val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName()
val kind = Flags.CLASS_KIND.get(proto.flags) val kind = Flags.CLASS_KIND.get(proto.flags)
@@ -162,6 +170,23 @@ class ChangesCollector {
collectSignature(classFqName, areSubclassesAffected = true) collectSignature(classFqName, areSubclassesAffected = true)
} }
if (isRemoved || isAdded) {
collectChangedParents(classFqName, proto.supertypeList)
}
}
private fun addChangedParents(fqName: FqName, parents: Collection<FqName>) {
if (parents.isNotEmpty()) {
changedParents.getOrPut(fqName) { HashSet() }.addAll(parents)
}
}
private fun ClassProtoData.collectChangedParents(fqName: FqName, parents: Collection<ProtoBuf.Type>) {
val changedParentsFqNames = parents.map { type ->
nameResolver.getClassId(type.className).asSingleFqName()
}
addChangedParents(fqName, changedParentsFqNames)
} }
private fun ClassProtoData.getNonPrivateMemberNames(): Set<String> { private fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
@@ -145,6 +145,7 @@ open class IncrementalJvmCache(
} }
protoMap.remove(className, changesCollector) protoMap.remove(className, changesCollector)
classFqNameToSourceMap.remove(className.fqNameForClassNameWithoutDollars) classFqNameToSourceMap.remove(className.fqNameForClassNameWithoutDollars)
classAttributesMap.remove(className.fqNameForClassNameWithoutDollars)
internalNameToSource.remove(className.internalName) internalNameToSource.remove(className.internalName)
// TODO NO_CHANGES? (delegates only) // TODO NO_CHANGES? (delegates only)
@@ -568,6 +569,7 @@ sealed class ChangeInfo(val fqName: FqName) {
class SignatureChanged(fqName: FqName, val areSubclassesAffected: Boolean) : ChangeInfo(fqName) class SignatureChanged(fqName: FqName, val areSubclassesAffected: Boolean) : ChangeInfo(fqName)
class ParentsChanged(fqName: FqName, val parentsChanged: Collection<FqName>) : ChangeInfo(fqName)
protected open fun toStringProperties(): String = "fqName = $fqName" protected open fun toStringProperties(): String = "fqName = $fqName"
@@ -136,7 +136,8 @@ fun LookupStorage.update(
data class DirtyData( data class DirtyData(
val dirtyLookupSymbols: Collection<LookupSymbol> = emptyList(), val dirtyLookupSymbols: Collection<LookupSymbol> = emptyList(),
val dirtyClassesFqNames: Collection<FqName> = emptyList() val dirtyClassesFqNames: Collection<FqName> = emptyList(),
val dirtyClassesFqNamesForceRecompile: Collection<FqName> = emptyList()
) )
fun ChangesCollector.getDirtyData( fun ChangesCollector.getDirtyData(
@@ -146,6 +147,9 @@ fun ChangesCollector.getDirtyData(
val dirtyLookupSymbols = HashSet<LookupSymbol>() val dirtyLookupSymbols = HashSet<LookupSymbol>()
val dirtyClassesFqNames = HashSet<FqName>() val dirtyClassesFqNames = HashSet<FqName>()
val sealedParents = HashMap<FqName, MutableSet<FqName>>()
val notSealedParents = HashSet<FqName>()
for (change in changes()) { for (change in changes()) {
reporter.reportVerbose { "Process $change" } reporter.reportVerbose { "Process $change" }
@@ -170,10 +174,35 @@ fun ChangesCollector.getDirtyData(
} }
fqNames.mapTo(dirtyLookupSymbols) { LookupSymbol(SAM_LOOKUP_NAME.asString(), it.asString()) } fqNames.mapTo(dirtyLookupSymbols) { LookupSymbol(SAM_LOOKUP_NAME.asString(), it.asString()) }
} else if (change is ChangeInfo.ParentsChanged) {
fun FqName.isSealed(): Boolean {
if (notSealedParents.contains(this)) return false
if (sealedParents.containsKey(this)) return true
return isSealed(this, caches).also { sealed ->
if (sealed) {
sealedParents[this] = HashSet()
} else {
notSealedParents.add(this)
}
}
}
change.parentsChanged.forEach { parent ->
if (parent.isSealed()) {
sealedParents.getOrPut(parent) { HashSet() }.add(change.fqName)
}
}
} }
} }
return DirtyData(dirtyLookupSymbols, dirtyClassesFqNames) val forceRecompile = HashSet<FqName>().apply {
addAll(sealedParents.keys)
//we should recompile all inheritors with parent sealed class: add known subtypes
addAll(sealedParents.keys.flatMap { withSubtypes(it, caches) })
//we should recompile all inheritors with parent sealed class: add new subtypes
addAll(sealedParents.values.flatten())
}
return DirtyData(dirtyLookupSymbols, dirtyClassesFqNames, forceRecompile)
} }
fun mapLookupSymbolsToFiles( fun mapLookupSymbolsToFiles(
@@ -217,6 +246,11 @@ fun mapClassesFqNamesToFiles(
return fqNameToAffectedFiles.values.flattenTo(HashSet()) return fqNameToAffectedFiles.values.flattenTo(HashSet())
} }
fun isSealed(
fqName: FqName,
caches: Iterable<IncrementalCacheCommon>
): Boolean = caches.any { it.isSealed(fqName) ?: false }
fun withSubtypes( fun withSubtypes(
typeFqName: FqName, typeFqName: FqName,
caches: Iterable<IncrementalCacheCommon> caches: Iterable<IncrementalCacheCommon>
@@ -28,12 +28,14 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags
import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import java.util.* import java.util.*
data class Difference( data class Difference(
val isClassAffected: Boolean = false, val isClassAffected: Boolean = false,
val areSubclassesAffected: Boolean = false, val areSubclassesAffected: Boolean = false,
val changedMembersNames: Set<String> = emptySet() val changedMembersNames: Set<String> = emptySet(),
val changedSupertypes: Set<FqName> = emptySet()
) )
sealed class ProtoData sealed class ProtoData
@@ -187,6 +189,7 @@ class DifferenceCalculatorForClass(
var isClassAffected = false var isClassAffected = false
var areSubclassesAffected = false var areSubclassesAffected = false
val changedSupertypes = HashSet<FqName>()
val names = hashSetOf<String>() val names = hashSetOf<String>()
val classIsSealed = newProto.isSealed && oldProto.isSealed val classIsSealed = newProto.isSealed && oldProto.isSealed
@@ -247,12 +250,21 @@ class DifferenceCalculatorForClass(
ProtoBufClassKind.FLAGS, ProtoBufClassKind.FLAGS,
ProtoBufClassKind.FQ_NAME, ProtoBufClassKind.FQ_NAME,
ProtoBufClassKind.TYPE_PARAMETER_LIST, ProtoBufClassKind.TYPE_PARAMETER_LIST,
ProtoBufClassKind.SUPERTYPE_LIST,
ProtoBufClassKind.SUPERTYPE_ID_LIST,
ProtoBufClassKind.JS_EXT_CLASS_ANNOTATION_LIST -> { ProtoBufClassKind.JS_EXT_CLASS_ANNOTATION_LIST -> {
isClassAffected = true isClassAffected = true
areSubclassesAffected = true areSubclassesAffected = true
} }
ProtoBufClassKind.SUPERTYPE_LIST,
ProtoBufClassKind.SUPERTYPE_ID_LIST -> {
isClassAffected = true
areSubclassesAffected = true
val oldSupertypes = oldProto.supertypeList.map { oldNameResolver.getClassId(it.className).asSingleFqName() }
val newSupertypes = newProto.supertypeList.map { newNameResolver.getClassId(it.className).asSingleFqName() }
val changed = (oldSupertypes union newSupertypes) subtract (oldSupertypes intersect newSupertypes)
changedSupertypes.addAll(changed)
}
ProtoBufClassKind.JVM_EXT_CLASS_MODULE_NAME, ProtoBufClassKind.JVM_EXT_CLASS_MODULE_NAME,
ProtoBufClassKind.JS_EXT_CLASS_CONTAINING_FILE_ID -> { ProtoBufClassKind.JS_EXT_CLASS_CONTAINING_FILE_ID -> {
// TODO // TODO
@@ -281,7 +293,7 @@ class DifferenceCalculatorForClass(
} }
} }
return Difference(isClassAffected, areSubclassesAffected, names) return Difference(isClassAffected, areSubclassesAffected, names, changedSupertypes)
} }
} }
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2015 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.incremental.storage
import com.intellij.util.io.DataExternalizer
import org.jetbrains.kotlin.name.FqName
import java.io.DataInput
import java.io.DataOutput
import java.io.File
internal data class ICClassesAttributes(val isSealed: Boolean)
internal object ICClassesAttributesExternalizer : DataExternalizer<ICClassesAttributes> {
override fun read(input: DataInput): ICClassesAttributes {
return ICClassesAttributes(input.readBoolean())
}
override fun save(output: DataOutput, value: ICClassesAttributes) {
output.writeBoolean(value.isSealed)
}
}
internal open class ClassAttributesMap(
storageFile: File
) : BasicStringMap<ICClassesAttributes>(storageFile, ICClassesAttributesExternalizer) {
override fun dumpValue(value: ICClassesAttributes): String = value.toString()
operator fun set(key: FqName, value: ICClassesAttributes) {
storage[key.asString()] = value
}
operator fun get(key: FqName): ICClassesAttributes? = storage[key.asString()]
fun remove(key: FqName) {
storage.remove(key.asString())
}
}
@@ -310,9 +310,10 @@ abstract class IncrementalCompilerRunner<
} }
if (compilationMode is CompilationMode.Rebuild) break if (compilationMode is CompilationMode.Rebuild) break
val (dirtyLookupSymbols, dirtyClassFqNames) = changesCollector.getDirtyData(listOf(caches.platformCache), reporter) val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = changesCollector.getDirtyData(listOf(caches.platformCache), reporter)
val compiledInThisIterationSet = sourcesToCompile.toHashSet() val compiledInThisIterationSet = sourcesToCompile.toHashSet()
val forceToRecompileFiles = mapClassesFqNamesToFiles(listOf(caches.platformCache), forceRecompile, reporter)
with(dirtySources) { with(dirtySources) {
clear() clear()
addAll(mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, reporter, excludes = compiledInThisIterationSet)) addAll(mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, reporter, excludes = compiledInThisIterationSet))
@@ -324,6 +325,9 @@ abstract class IncrementalCompilerRunner<
excludes = compiledInThisIterationSet excludes = compiledInThisIterationSet
) )
) )
if (!compiledInThisIterationSet.containsAll(forceToRecompileFiles)) {
addAll(forceToRecompileFiles)
}
} }
buildDirtyLookupSymbols.addAll(dirtyLookupSymbols) buildDirtyLookupSymbols.addAll(dirtyLookupSymbols)
@@ -125,7 +125,7 @@ class IncrementalJsCompilerRunner(
val removedClassesChanges = getRemovedClassesChanges(caches, changedFiles) val removedClassesChanges = getRemovedClassesChanges(caches, changedFiles)
dirtyFiles.addByDirtySymbols(removedClassesChanges.dirtyLookupSymbols) dirtyFiles.addByDirtySymbols(removedClassesChanges.dirtyLookupSymbols)
dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNames) dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNames)
dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNamesForceRecompile)
return CompilationMode.Incremental(dirtyFiles) return CompilationMode.Incremental(dirtyFiles)
} }
@@ -227,7 +227,7 @@ class IncrementalJvmCompilerRunner(
dirtyFiles.addByDirtySymbols(androidLayoutChanges) dirtyFiles.addByDirtySymbols(androidLayoutChanges)
dirtyFiles.addByDirtySymbols(removedClassesChanges.dirtyLookupSymbols) dirtyFiles.addByDirtySymbols(removedClassesChanges.dirtyLookupSymbols)
dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNames) dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNames)
dirtyFiles.addByDirtyClasses(removedClassesChanges.dirtyClassesFqNamesForceRecompile)
return CompilationMode.Incremental(dirtyFiles) return CompilationMode.Incremental(dirtyFiles)
} }
@@ -90,6 +90,8 @@ abstract class AbstractProtoComparisonTest<PROTO_DATA> : TestWithWorkingDir() {
when (it) { when (it) {
is ChangeInfo.SignatureChanged -> "CLASS_SIGNATURE" is ChangeInfo.SignatureChanged -> "CLASS_SIGNATURE"
is ChangeInfo.MembersChanged -> "MEMBERS\n ${it.names.sorted()}" is ChangeInfo.MembersChanged -> "MEMBERS\n ${it.names.sorted()}"
is ChangeInfo.ParentsChanged -> "PARENTS\n ${it.parentsChanged.map { it.asString()}.sorted()}"
} }
}.sorted() }.sorted()
@@ -250,7 +250,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
removedClasses.forEach { changesCollector.collectSignature(FqName(it), areSubclassesAffected = true) } removedClasses.forEach { changesCollector.collectSignature(FqName(it), areSubclassesAffected = true) }
val affectedByRemovedClasses = changesCollector.getDirtyFiles(incrementalCaches.values, kotlinContext.lookupStorageManager) val affectedByRemovedClasses = changesCollector.getDirtyFiles(incrementalCaches.values, kotlinContext.lookupStorageManager)
fsOperations.markFilesForCurrentRound(affectedByRemovedClasses) fsOperations.markFilesForCurrentRound(affectedByRemovedClasses.dirtyFiles + affectedByRemovedClasses.forceRecompileTogether)
} }
override fun chunkBuildFinished(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildFinished(context: CompileContext, chunk: ModuleChunk) {
@@ -695,21 +695,36 @@ private fun ChangesCollector.processChangesUsingLookups(
reporter.reportVerbose { "Start processing changes" } reporter.reportVerbose { "Start processing changes" }
val dirtyFiles = getDirtyFiles(allCaches, lookupStorageManager) val dirtyFiles = getDirtyFiles(allCaches, lookupStorageManager)
fsOperations.markInChunkOrDependents(dirtyFiles.asIterable(), excludeFiles = compiledFiles) // if list of inheritors of sealed class has changed it should be recompiled with all the inheritors
// Here we have a small optimization. Do not recompile the bunch if ALL these files were recompiled during the previous round.
val excludeFiles = if (compiledFiles.containsAll(dirtyFiles.forceRecompileTogether))
compiledFiles
else
compiledFiles.minus(dirtyFiles.forceRecompileTogether)
fsOperations.markInChunkOrDependents(
(dirtyFiles.dirtyFiles + dirtyFiles.forceRecompileTogether).asIterable(),
excludeFiles = excludeFiles
)
reporter.reportVerbose { "End of processing changes" } reporter.reportVerbose { "End of processing changes" }
} }
data class FilesToRecompile(val dirtyFiles: Set<File>, val forceRecompileTogether: Set<File>)
private fun ChangesCollector.getDirtyFiles( private fun ChangesCollector.getDirtyFiles(
caches: Iterable<IncrementalCacheCommon>, caches: Iterable<IncrementalCacheCommon>,
lookupStorageManager: JpsLookupStorageManager lookupStorageManager: JpsLookupStorageManager
): Set<File> { ): FilesToRecompile {
val reporter = JpsICReporter() val reporter = JpsICReporter()
val (dirtyLookupSymbols, dirtyClassFqNames) = getDirtyData(caches, reporter) val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = getDirtyData(caches, reporter)
val dirtyFilesFromLookups = lookupStorageManager.withLookupStorage { val dirtyFilesFromLookups = lookupStorageManager.withLookupStorage {
mapLookupSymbolsToFiles(it, dirtyLookupSymbols, reporter) mapLookupSymbolsToFiles(it, dirtyLookupSymbols, reporter)
} }
return dirtyFilesFromLookups + mapClassesFqNamesToFiles(caches, dirtyClassFqNames, reporter) return FilesToRecompile(
dirtyFilesFromLookups + mapClassesFqNamesToFiles(caches, dirtyClassFqNames, reporter),
mapClassesFqNamesToFiles(caches, forceRecompile, reporter)
)
} }
private fun getLookupTracker(project: JpsProject, representativeTarget: KotlinModuleBuildTarget<*>): LookupTracker { private fun getLookupTracker(project: JpsProject, representativeTarget: KotlinModuleBuildTarget<*>): LookupTracker {