Introduce ChangesCollector

Original commit: 6fedf07f56
This commit is contained in:
Alexey Tsvetkov
2017-08-10 15:59:56 +03:00
parent b5cf0960f4
commit 2fbd8c4beb
4 changed files with 60 additions and 55 deletions
@@ -21,11 +21,8 @@ import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants
import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.js.K2JSCompiler
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.ClassProtoData
import org.jetbrains.kotlin.incremental.Difference
import org.jetbrains.kotlin.incremental.difference as differenceImpl
import org.jetbrains.kotlin.incremental.PackagePartProtoData
import org.jetbrains.kotlin.incremental.ProtoData import org.jetbrains.kotlin.incremental.ProtoData
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.utils.TestMessageCollector import org.jetbrains.kotlin.incremental.utils.TestMessageCollector
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
@@ -72,28 +69,33 @@ abstract class AbstractJsProtoComparisonTest : AbstractProtoComparisonTest<Proto
val classes = hashMapOf<ClassId, ProtoData>() val classes = hashMapOf<ClassId, ProtoData>()
for ((sourceFile, protoBytes, _) in incrementalResults.packageParts) { for ((sourceFile, protoBytes, _) in incrementalResults.packageParts) {
val proto = ProtoBuf.PackageFragment.parseFrom(protoBytes, JsSerializerProtocol.extensionRegistry) classes.putAll(getProtoData(sourceFile, protoBytes))
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
proto.class_List.forEach {
val classId = nameResolver.getClassId(it.fqName)
classes[classId] = ClassProtoData(it, nameResolver)
}
proto.`package`.apply {
val packageFqName = if (hasExtension(JsProtoBuf.packageFqName)) {
nameResolver.getPackageFqName(getExtension(JsProtoBuf.packageFqName))
}
else FqName.ROOT
val packagePartClassId = ClassId(packageFqName, Name.identifier(sourceFile.nameWithoutExtension.capitalize() + "Kt"))
classes[packagePartClassId] = PackagePartProtoData(this, nameResolver)
}
} }
return classes return classes
} }
override fun difference(oldData: ProtoData, newData: ProtoData): Difference? = override fun ProtoData.toProtoData(): ProtoData? = this
differenceImpl(oldData, newData)
} }
fun getProtoData(sourceFile: File, metadata: ByteArray): Map<ClassId, ProtoData> {
val classes = hashMapOf<ClassId, ProtoData>()
val proto = ProtoBuf.PackageFragment.parseFrom(metadata, JsSerializerProtocol.extensionRegistry)
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
proto.class_List.forEach {
val classId = nameResolver.getClassId(it.fqName)
classes[classId] = ClassProtoData(it, nameResolver)
}
proto.`package`.apply {
val packageFqName = if (hasExtension(JsProtoBuf.packageFqName)) {
nameResolver.getPackageFqName(getExtension(JsProtoBuf.packageFqName))
}
else FqName.ROOT
val packagePartClassId = ClassId(packageFqName, Name.identifier(sourceFile.nameWithoutExtension.capitalize() + "Kt"))
classes[packagePartClassId] = PackagePartProtoData(this, nameResolver, packageFqName)
}
return classes
}
@@ -16,9 +16,7 @@
package org.jetbrains.kotlin.jps.incremental package org.jetbrains.kotlin.jps.incremental
import org.jetbrains.kotlin.incremental.Difference import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.LocalFileKotlinClass
import org.jetbrains.kotlin.incremental.difference
import org.jetbrains.kotlin.incremental.storage.ProtoMapValue import org.jetbrains.kotlin.incremental.storage.ProtoMapValue
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
@@ -36,25 +34,20 @@ abstract class AbstractJvmProtoComparisonTest : AbstractProtoComparisonTest<Loca
return localClassFiles.associateBy { it.classId } return localClassFiles.associateBy { it.classId }
} }
override fun difference(oldData: LocalFileKotlinClass, newData: LocalFileKotlinClass): Difference? { override fun LocalFileKotlinClass.toProtoData(): ProtoData? {
val oldProto = oldData.readProto() ?: return null
val newProto = newData.readProto() ?: return null
return difference(oldProto, newProto)
}
private fun KotlinJvmBinaryClass.readProto(): ProtoMapValue? {
assert(classHeader.metadataVersion.isCompatible()) { "Incompatible class ($classHeader): $location" } assert(classHeader.metadataVersion.isCompatible()) { "Incompatible class ($classHeader): $location" }
val bytes by lazy { BitEncoding.decodeBytes(classHeader.data!!) } val bytes by lazy { BitEncoding.decodeBytes(classHeader.data!!) }
val strings by lazy { classHeader.strings!! } val strings by lazy { classHeader.strings!! }
val packageFqName = classId.packageFqName
return when (classHeader.kind) { return when (classHeader.kind) {
KotlinClassHeader.Kind.CLASS -> { KotlinClassHeader.Kind.CLASS -> {
ProtoMapValue(false, bytes, strings) ProtoMapValue(false, bytes, strings).toProtoData(packageFqName)
} }
KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.FILE_FACADE,
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> { KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
ProtoMapValue(true, bytes, strings) ProtoMapValue(true, bytes, strings).toProtoData(packageFqName)
} }
else -> { else -> {
null null
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.jps.incremental package org.jetbrains.kotlin.jps.incremental
import org.jetbrains.kotlin.TestWithWorkingDir import org.jetbrains.kotlin.TestWithWorkingDir
import org.jetbrains.kotlin.incremental.Difference import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
@@ -25,7 +25,7 @@ import java.io.File
abstract class AbstractProtoComparisonTest<PROTO_DATA> : TestWithWorkingDir() { abstract class AbstractProtoComparisonTest<PROTO_DATA> : TestWithWorkingDir() {
protected abstract fun compileAndGetClasses(sourceDir: File, outputDir: File): Map<ClassId, PROTO_DATA> protected abstract fun compileAndGetClasses(sourceDir: File, outputDir: File): Map<ClassId, PROTO_DATA>
protected abstract fun difference(oldData: PROTO_DATA, newData: PROTO_DATA): Difference? protected abstract fun PROTO_DATA.toProtoData(): ProtoData?
protected open fun expectedOutputFile(testDir: File): File = protected open fun expectedOutputFile(testDir: File): File =
File(testDir, "result.out") File(testDir, "result.out")
@@ -48,32 +48,41 @@ abstract class AbstractProtoComparisonTest<PROTO_DATA> : TestWithWorkingDir() {
} }
(oldClassMap.keys.intersect(newClassMap.keys)).sortedBy { it.toString() }.forEach { classId -> (oldClassMap.keys.intersect(newClassMap.keys)).sortedBy { it.toString() }.forEach { classId ->
val oldData = oldClassMap[classId]!! val oldData = oldClassMap[classId]!!.toProtoData()
val newData = newClassMap[classId]!! val newData = newClassMap[classId]!!.toProtoData()
val diff = difference(oldData, newData)
if (diff == null) { if (oldData == null || newData == null) {
p.println("SKIPPED $classId") p.println("SKIPPED $classId")
return@forEach return@forEach
} }
diff.rawProtoDifference?.let { val rawProtoDifference = when {
oldData is ClassProtoData && newData is ClassProtoData -> {
ProtoCompareGenerated(oldData.nameResolver, newData.nameResolver).difference(oldData.proto, newData.proto)
}
oldData is PackagePartProtoData && newData is PackagePartProtoData -> {
ProtoCompareGenerated(oldData.nameResolver, newData.nameResolver).difference(oldData.proto, newData.proto)
}
else -> null
}
rawProtoDifference?.let {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
p.println("PROTO DIFFERENCE in $classId: ${it.sortedBy { it.name }.joinToString()}") p.println("PROTO DIFFERENCE in $classId: ${it.sortedBy { it.name }.joinToString()}")
} }
} }
val changes = arrayListOf<String>() val changesInfo = ChangesCollector().apply { collectProtoChanges(oldData, newData) }.changes()
if (diff.isClassAffected) { if (changesInfo.isEmpty()) {
changes.add("CLASS_SIGNATURE")
}
if (diff.changedMembersNames.isNotEmpty()) {
changes.add("MEMBERS\n ${diff.changedMembersNames.sorted()}")
}
if (changes.isEmpty()) {
return@forEach return@forEach
} }
val changes = changesInfo.map {
when (it) {
is ChangeInfo.SignatureChanged -> "CLASS_SIGNATURE"
is ChangeInfo.MembersChanged -> "MEMBERS\n ${it.names.sorted()}"
}
}.sorted()
p.println("CHANGES in $classId: ${changes.joinToString()}") p.println("CHANGES in $classId: ${changes.joinToString()}")
} }
@@ -314,8 +314,9 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
context.checkCanceled() context.checkCanceled()
val changesInfo = generatedFiles.entries.fold(CompilationResult.NO_CHANGES) { acc, (target, files) -> val changesCollector = ChangesCollector()
acc + updateIncrementalCache(files, incrementalCaches[target]!!) for ((target, files) in generatedFiles) {
updateIncrementalCache(files, incrementalCaches[target]!!, changesCollector)
} }
updateLookupStorage(chunk, lookupTracker, dataManager, dirtyFilesHolder, filesToCompile) updateLookupStorage(chunk, lookupTracker, dataManager, dirtyFilesHolder, filesToCompile)
@@ -323,7 +324,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
return OK return OK
} }
changesInfo.processChangesUsingLookups(filesToCompile.values().toSet(), dataManager, fsOperations, incrementalCaches.values) changesCollector.processChangesUsingLookups(filesToCompile.values().toSet(), dataManager, fsOperations, incrementalCaches.values)
return OK return OK
} }
@@ -432,7 +433,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
for (target in chunk.targets) { for (target in chunk.targets) {
val cache = incrementalCaches[target]!! val cache = incrementalCaches[target]!!
val removedAndDirtyFiles = filesToCompile[target] + dirtyFilesHolder.getRemovedFiles(target).map(::File) val removedAndDirtyFiles = filesToCompile[target] + dirtyFilesHolder.getRemovedFiles(target).map(::File)
cache.markOutputClassesDirty(removedAndDirtyFiles) cache.markDirty(removedAndDirtyFiles)
} }
} }
@@ -774,7 +775,7 @@ private class JpsICReporter : ICReporter {
} }
} }
private fun CompilationResult.processChangesUsingLookups( private fun ChangesCollector.processChangesUsingLookups(
compiledFiles: Set<File>, compiledFiles: Set<File>,
dataManager: BuildDataManager, dataManager: BuildDataManager,
fsOperations: FSOperationsHelper, fsOperations: FSOperationsHelper,