Introduce ChangesCollector
This commit is contained in:
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||||
|
import org.jetbrains.kotlin.serialization.Flags
|
||||||
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||||
|
|
||||||
|
class ChangesCollector {
|
||||||
|
private val removedMembers = hashMapOf<FqName, MutableSet<String>>()
|
||||||
|
private val changedMembers = hashMapOf<FqName, MutableSet<String>>()
|
||||||
|
private val areSubclassesAffected = hashMapOf<FqName, Boolean>()
|
||||||
|
|
||||||
|
fun changes(): List<ChangeInfo> {
|
||||||
|
val changes = arrayListOf<ChangeInfo>()
|
||||||
|
|
||||||
|
for ((fqName, members) in removedMembers) {
|
||||||
|
if (members.isNotEmpty()) {
|
||||||
|
changes.add(ChangeInfo.Removed(fqName, members))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((fqName, members) in changedMembers) {
|
||||||
|
if (members.isNotEmpty()) {
|
||||||
|
changes.add(ChangeInfo.MembersChanged(fqName, members))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((fqName, areSubclassesAffected) in areSubclassesAffected) {
|
||||||
|
changes.add(ChangeInfo.SignatureChanged(fqName, areSubclassesAffected))
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T, R> MutableMap<T, MutableSet<R>>.getSet(key: T) =
|
||||||
|
getOrPut(key) { HashSet() }
|
||||||
|
|
||||||
|
private fun collectChangedMember(scope: FqName, name: String) {
|
||||||
|
changedMembers.getSet(scope).add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectRemovedMember(scope: FqName, name: String) {
|
||||||
|
removedMembers.getSet(scope).add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectChangedMembers(scope: FqName, names: Collection<String>) {
|
||||||
|
if (names.isNotEmpty()) {
|
||||||
|
changedMembers.getSet(scope).addAll(names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectRemovedMembers(scope: FqName, names: Collection<String>) {
|
||||||
|
if (names.isNotEmpty()) {
|
||||||
|
removedMembers.getSet(scope).addAll(names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collectProtoChanges(oldData: ProtoData?, newData: ProtoData?) {
|
||||||
|
if (oldData == null && newData == null) {
|
||||||
|
throw IllegalStateException("Old and new value are null")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldData == null) {
|
||||||
|
newData!!.collectAll(isRemoved = false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newData == null) {
|
||||||
|
oldData.collectAll(isRemoved = true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when (oldData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
when (newData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
val fqName = oldData.nameResolver.getClassId(oldData.proto.fqName).asSingleFqName()
|
||||||
|
val diff = DifferenceCalculatorForClass(oldData, newData).difference()
|
||||||
|
if (diff.isClassAffected) {
|
||||||
|
collectSignature(oldData, diff.areSubclassesAffected)
|
||||||
|
}
|
||||||
|
collectChangedMembers(fqName, diff.changedMembersNames)
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
collectSignature(oldData, areSubclassesAffected = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
when (newData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
collectSignature(newData, areSubclassesAffected = false)
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
val diff = DifferenceCalculatorForPackageFacade(oldData, newData).difference()
|
||||||
|
collectChangedMembers(oldData.packageFqName, diff.changedMembersNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
|
||||||
|
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||||
|
|
||||||
|
private fun ProtoData.collectAll(isRemoved: Boolean) =
|
||||||
|
when (this) {
|
||||||
|
is PackagePartProtoData -> collectAllFromPackage(isRemoved)
|
||||||
|
is ClassProtoData -> collectAllFromClass(isRemoved)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
||||||
|
val memberNames =
|
||||||
|
proto.getNonPrivateNames(
|
||||||
|
nameResolver,
|
||||||
|
ProtoBuf.Package::getFunctionList,
|
||||||
|
ProtoBuf.Package::getPropertyList
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isRemoved) {
|
||||||
|
collectRemovedMembers(packageFqName, memberNames)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
collectChangedMembers(packageFqName, memberNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ClassProtoData.collectAllFromClass(isRemoved: Boolean) {
|
||||||
|
val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName()
|
||||||
|
val kind = Flags.CLASS_KIND.get(proto.flags)
|
||||||
|
|
||||||
|
if (kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
|
||||||
|
val memberNames =
|
||||||
|
proto.getNonPrivateNames(
|
||||||
|
nameResolver,
|
||||||
|
ProtoBuf.Class::getConstructorList,
|
||||||
|
ProtoBuf.Class::getFunctionList,
|
||||||
|
ProtoBuf.Class::getPropertyList
|
||||||
|
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
||||||
|
|
||||||
|
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
||||||
|
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
||||||
|
memberNames.forEach { collectMember(classFqName, it) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
collectSignature(classFqName, areSubclassesAffected = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collectMemberIfValueWasChanged(scope: FqName, name: String, oldValue: Any?, newValue: Any?) {
|
||||||
|
if (oldValue == null && newValue == null) {
|
||||||
|
throw IllegalStateException("Old and new value are null for $scope#$name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldValue != null && newValue == null) {
|
||||||
|
collectRemovedMember(scope, name)
|
||||||
|
}
|
||||||
|
else if (oldValue != newValue) {
|
||||||
|
collectChangedMember(scope, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectSignature(classData: ClassProtoData, areSubclassesAffected: Boolean) {
|
||||||
|
val fqName = classData.nameResolver.getClassId(classData.proto.fqName).asSingleFqName()
|
||||||
|
collectSignature(fqName, areSubclassesAffected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collectSignature(fqName: FqName, areSubclassesAffected: Boolean) {
|
||||||
|
val prevValue = this.areSubclassesAffected[fqName] ?: false
|
||||||
|
this.areSubclassesAffected[fqName] = prevValue || areSubclassesAffected
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,14 +18,11 @@ package org.jetbrains.kotlin.incremental
|
|||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
|
import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
|
||||||
import com.intellij.util.SmartList
|
|
||||||
import com.intellij.util.io.BooleanDataDescriptor
|
import com.intellij.util.io.BooleanDataDescriptor
|
||||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||||
import gnu.trove.THashSet
|
import gnu.trove.THashSet
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||||
import org.jetbrains.kotlin.incremental.ChangeInfo.MembersChanged
|
|
||||||
import org.jetbrains.kotlin.incremental.ChangeInfo.Removed
|
|
||||||
import org.jetbrains.kotlin.incremental.storage.*
|
import org.jetbrains.kotlin.incremental.storage.*
|
||||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
||||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||||
@@ -33,11 +30,7 @@ import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
|||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.serialization.Flags
|
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
|
||||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
@@ -106,15 +99,14 @@ open class IncrementalCacheImpl(
|
|||||||
return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath)
|
return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveModuleMappingToCache(sourceFiles: Collection<File>, file: File): CompilationResult {
|
fun saveModuleMappingToCache(sourceFiles: Collection<File>, file: File) {
|
||||||
val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
|
val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
|
||||||
protoMap.storeModuleMapping(jvmClassName, file.readBytes())
|
protoMap.storeModuleMapping(jvmClassName, file.readBytes())
|
||||||
dirtyOutputClassesMap.notDirty(MODULE_MAPPING_FILE_NAME)
|
dirtyOutputClassesMap.notDirty(MODULE_MAPPING_FILE_NAME)
|
||||||
sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) }
|
sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) }
|
||||||
return CompilationResult.NO_CHANGES
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun saveFileToCache(generatedClass: GeneratedJvmClass): CompilationResult {
|
open fun saveFileToCache(generatedClass: GeneratedJvmClass, changesCollector: ChangesCollector) {
|
||||||
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
||||||
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
||||||
val className = kotlinClass.className
|
val className = kotlinClass.className
|
||||||
@@ -126,19 +118,17 @@ open class IncrementalCacheImpl(
|
|||||||
|
|
||||||
internalNameToSource[className.internalName] = sourceFiles
|
internalNameToSource[className.internalName] = sourceFiles
|
||||||
|
|
||||||
if (kotlinClass.classId.isLocal) {
|
if (kotlinClass.classId.isLocal) return
|
||||||
return CompilationResult.NO_CHANGES
|
|
||||||
}
|
|
||||||
|
|
||||||
val header = kotlinClass.classHeader
|
val header = kotlinClass.classHeader
|
||||||
val changesInfo = when (header.kind) {
|
when (header.kind) {
|
||||||
KotlinClassHeader.Kind.FILE_FACADE -> {
|
KotlinClassHeader.Kind.FILE_FACADE -> {
|
||||||
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
||||||
packagePartMap.addPackagePart(className)
|
packagePartMap.addPackagePart(className)
|
||||||
|
|
||||||
protoMap.process(kotlinClass, isPackage = true) +
|
protoMap.process(kotlinClass, changesCollector)
|
||||||
constantsMap.process(kotlinClass, isPackage = true) +
|
constantsMap.process(kotlinClass, changesCollector)
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage = true)
|
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||||
}
|
}
|
||||||
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
||||||
val partNames = kotlinClass.classHeader.data?.toList()
|
val partNames = kotlinClass.classHeader.data?.toList()
|
||||||
@@ -148,118 +138,43 @@ open class IncrementalCacheImpl(
|
|||||||
// the class' proto wouldn't ever be deleted,
|
// the class' proto wouldn't ever be deleted,
|
||||||
// because we don't write proto for multifile facades.
|
// because we don't write proto for multifile facades.
|
||||||
// As a workaround we can remove proto values for multifile facades.
|
// As a workaround we can remove proto values for multifile facades.
|
||||||
val additionalChangeInfo = if (className in protoMap) {
|
if (className in protoMap) {
|
||||||
val info = ChangeInfo.SignatureChanged(className.fqNameForClassNameWithoutDollars, areSubclassesAffected = true)
|
changesCollector.collectSignature(className.fqNameForClassNameWithoutDollars, areSubclassesAffected = true)
|
||||||
CompilationResult(changes = sequenceOf(info))
|
|
||||||
}
|
}
|
||||||
else CompilationResult.NO_CHANGES
|
protoMap.remove(className, changesCollector)
|
||||||
protoMap.remove(className)
|
|
||||||
classFqNameToSourceMap.remove(className.fqNameForClassNameWithoutDollars)
|
classFqNameToSourceMap.remove(className.fqNameForClassNameWithoutDollars)
|
||||||
internalNameToSource.remove(className.internalName)
|
internalNameToSource.remove(className.internalName)
|
||||||
|
|
||||||
// TODO NO_CHANGES? (delegates only)
|
// TODO NO_CHANGES? (delegates only)
|
||||||
constantsMap.process(kotlinClass, isPackage = true) +
|
constantsMap.process(kotlinClass, changesCollector)
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage = true) +
|
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||||
additionalChangeInfo
|
|
||||||
}
|
}
|
||||||
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
||||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||||
packagePartMap.addPackagePart(className)
|
packagePartMap.addPackagePart(className)
|
||||||
partToMultifileFacade.set(className.internalName, header.multifileClassName!!)
|
partToMultifileFacade.set(className.internalName, header.multifileClassName!!)
|
||||||
|
|
||||||
protoMap.process(kotlinClass, isPackage = true) +
|
protoMap.process(kotlinClass, changesCollector)
|
||||||
constantsMap.process(kotlinClass, isPackage = true) +
|
constantsMap.process(kotlinClass, changesCollector)
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage = true)
|
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||||
}
|
}
|
||||||
KotlinClassHeader.Kind.CLASS -> {
|
KotlinClassHeader.Kind.CLASS -> {
|
||||||
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
||||||
addToClassStorage(kotlinClass, sourceFiles.first())
|
addToClassStorage(kotlinClass, sourceFiles.first())
|
||||||
|
|
||||||
protoMap.process(kotlinClass, isPackage = false) +
|
protoMap.process(kotlinClass, changesCollector)
|
||||||
constantsMap.process(kotlinClass, isPackage = false) +
|
constantsMap.process(kotlinClass, changesCollector)
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage = false)
|
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||||
}
|
|
||||||
else -> CompilationResult.NO_CHANGES
|
|
||||||
}
|
|
||||||
|
|
||||||
changesInfo.logIfSomethingChanged(className)
|
|
||||||
return changesInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CompilationResult.logIfSomethingChanged(className: JvmClassName) {
|
|
||||||
if (this == CompilationResult.NO_CHANGES) return
|
|
||||||
|
|
||||||
debugLog("$className is changed: $this")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeChanges(className: JvmClassName, createChangeInfo: (FqName, Collection<String>) -> ChangeInfo): List<ChangeInfo> {
|
|
||||||
if (className.internalName == MODULE_MAPPING_FILE_NAME) return emptyList()
|
|
||||||
|
|
||||||
val mapValue = protoMap[className] ?: return emptyList()
|
|
||||||
|
|
||||||
return when {
|
|
||||||
mapValue.isPackageFacade -> {
|
|
||||||
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(mapValue.bytes, mapValue.strings)
|
|
||||||
computePackageChanges(className.packageFqName, packageProto, nameResolver, createChangeInfo)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(mapValue.bytes, mapValue.strings)
|
|
||||||
computeClassChanges(nameResolver, classProto, createChangeInfo)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
|
fun clearCacheForRemovedClasses(changesCollector: ChangesCollector) {
|
||||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
|
||||||
|
|
||||||
private fun computePackageChanges(
|
|
||||||
packageFqName: FqName,
|
|
||||||
protoData: ProtoBuf.Package,
|
|
||||||
nameResolver: NameResolver,
|
|
||||||
createChangeInfo: (FqName, Collection<String>) -> ChangeInfo
|
|
||||||
): List<ChangeInfo> {
|
|
||||||
val memberNames =
|
|
||||||
protoData.getNonPrivateNames(
|
|
||||||
nameResolver,
|
|
||||||
ProtoBuf.Package::getFunctionList,
|
|
||||||
ProtoBuf.Package::getPropertyList
|
|
||||||
)
|
|
||||||
|
|
||||||
return listOf(createChangeInfo(packageFqName, memberNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeClassChanges(nameResolver: NameResolver, classProto: ProtoBuf.Class, createChangeInfo: (FqName, Collection<String>) -> ChangeInfo): List<ChangeInfo> {
|
|
||||||
val classFqName = nameResolver.getClassId(classProto.fqName).asSingleFqName()
|
|
||||||
val kind = Flags.CLASS_KIND.get(classProto.flags)
|
|
||||||
|
|
||||||
return if (kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
|
|
||||||
val memberNames =
|
|
||||||
classProto.getNonPrivateNames(
|
|
||||||
nameResolver,
|
|
||||||
ProtoBuf.Class::getConstructorList,
|
|
||||||
ProtoBuf.Class::getFunctionList,
|
|
||||||
ProtoBuf.Class::getPropertyList
|
|
||||||
) + classProto.enumEntryList.map { nameResolver.getString(it.name) }
|
|
||||||
|
|
||||||
val companionObjectChanged = createChangeInfo(classFqName.parent(), listOfNotNull(classFqName.shortName().asString()))
|
|
||||||
val companionObjectMembersChanged = createChangeInfo(classFqName, memberNames)
|
|
||||||
|
|
||||||
listOf(companionObjectMembersChanged, companionObjectChanged)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listOf(ChangeInfo.SignatureChanged(classFqName, areSubclassesAffected = true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearCacheForRemovedClasses(): CompilationResult {
|
|
||||||
val dirtyClasses = dirtyOutputClassesMap
|
val dirtyClasses = dirtyOutputClassesMap
|
||||||
.getDirtyOutputClasses()
|
.getDirtyOutputClasses()
|
||||||
.map(JvmClassName::byInternalName)
|
.map(JvmClassName::byInternalName)
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
val changes = dirtyClasses.flatMap { computeChanges(it, ::Removed) }.asSequence()
|
|
||||||
val changesInfo = CompilationResult(changes = changes)
|
|
||||||
|
|
||||||
val facadesWithRemovedParts = hashMapOf<JvmClassName, MutableSet<String>>()
|
val facadesWithRemovedParts = hashMapOf<JvmClassName, MutableSet<String>>()
|
||||||
for (dirtyClass in dirtyClasses) {
|
for (dirtyClass in dirtyClasses) {
|
||||||
val facade = partToMultifileFacade.get(dirtyClass.internalName) ?: continue
|
val facade = partToMultifileFacade.get(dirtyClass.internalName) ?: continue
|
||||||
@@ -281,7 +196,7 @@ open class IncrementalCacheImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
dirtyClasses.forEach {
|
dirtyClasses.forEach {
|
||||||
protoMap.remove(it)
|
protoMap.remove(it, changesCollector)
|
||||||
packagePartMap.remove(it)
|
packagePartMap.remove(it)
|
||||||
multifileFacadeToParts.remove(it)
|
multifileFacadeToParts.remove(it)
|
||||||
partToMultifileFacade.remove(it)
|
partToMultifileFacade.remove(it)
|
||||||
@@ -293,7 +208,6 @@ open class IncrementalCacheImpl(
|
|||||||
removeAllFromClassStorage(dirtyClasses.map { it.fqNameForClassNameWithoutDollars })
|
removeAllFromClassStorage(dirtyClasses.map { it.fqNameForClassNameWithoutDollars })
|
||||||
|
|
||||||
dirtyOutputClassesMap.clean()
|
dirtyOutputClassesMap.clean()
|
||||||
return changesInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObsoletePackageParts(): Collection<String> {
|
override fun getObsoletePackageParts(): Collection<String> {
|
||||||
@@ -335,10 +249,8 @@ open class IncrementalCacheImpl(
|
|||||||
|
|
||||||
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
||||||
|
|
||||||
fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult {
|
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||||
val header = kotlinClass.classHeader
|
return put(kotlinClass, changesCollector)
|
||||||
val bytes = BitEncoding.decodeBytes(header.data!!)
|
|
||||||
return put(kotlinClass.className, bytes, header.strings!!, isPackage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A module mapping (.kotlin_module file) is stored in a cache,
|
// A module mapping (.kotlin_module file) is stored in a cache,
|
||||||
@@ -348,47 +260,22 @@ open class IncrementalCacheImpl(
|
|||||||
// from files compiled during last round.
|
// from files compiled during last round.
|
||||||
// However there is no need to compare old and new data in this case
|
// However there is no need to compare old and new data in this case
|
||||||
// (also that would fail with exception).
|
// (also that would fail with exception).
|
||||||
fun storeModuleMapping(className: JvmClassName, bytes: ByteArray): CompilationResult {
|
fun storeModuleMapping(className: JvmClassName, bytes: ByteArray) {
|
||||||
storage[className.internalName] = ProtoMapValue(isPackageFacade = false, bytes = bytes, strings = emptyArray())
|
storage[className.internalName] = ProtoMapValue(isPackageFacade = false, bytes = bytes, strings = emptyArray())
|
||||||
return CompilationResult()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun put(
|
private fun put(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||||
className: JvmClassName,
|
val header = kotlinClass.classHeader
|
||||||
bytes: ByteArray,
|
|
||||||
strings: Array<String>,
|
val key = kotlinClass.className.internalName
|
||||||
isPackage: Boolean
|
|
||||||
): CompilationResult {
|
|
||||||
val key = className.internalName
|
|
||||||
val oldData = storage[key]
|
val oldData = storage[key]
|
||||||
val data = ProtoMapValue(isPackage, bytes, strings)
|
val newData = ProtoMapValue(header.kind != KotlinClassHeader.Kind.CLASS,
|
||||||
|
BitEncoding.decodeBytes(header.data!!),
|
||||||
|
header.strings!!)
|
||||||
|
storage[key] = newData
|
||||||
|
|
||||||
if (oldData == null ||
|
val packageFqName = kotlinClass.className.packageFqName
|
||||||
!Arrays.equals(bytes, oldData.bytes) ||
|
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName))
|
||||||
!Arrays.equals(strings, oldData.strings) ||
|
|
||||||
isPackage != oldData.isPackageFacade
|
|
||||||
) {
|
|
||||||
storage[key] = data
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldData == null) {
|
|
||||||
val changes = computeChanges(className, ::MembersChanged).asSequence()
|
|
||||||
return CompilationResult(changes = changes)
|
|
||||||
}
|
|
||||||
|
|
||||||
val difference = difference(oldData, data)
|
|
||||||
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
|
|
||||||
val changeList = SmartList<ChangeInfo>()
|
|
||||||
|
|
||||||
if (difference.isClassAffected) {
|
|
||||||
changeList.add(ChangeInfo.SignatureChanged(fqName, difference.areSubclassesAffected))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (difference.changedMembersNames.isNotEmpty()) {
|
|
||||||
changeList.add(ChangeInfo.MembersChanged(fqName, difference.changedMembersNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompilationResult(changes = changeList.asSequence())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(className: JvmClassName): Boolean =
|
operator fun contains(className: JvmClassName): Boolean =
|
||||||
@@ -397,8 +284,11 @@ open class IncrementalCacheImpl(
|
|||||||
operator fun get(className: JvmClassName): ProtoMapValue? =
|
operator fun get(className: JvmClassName): ProtoMapValue? =
|
||||||
storage[className.internalName]
|
storage[className.internalName]
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
fun remove(className: JvmClassName, changesCollector: ChangesCollector) {
|
||||||
storage.remove(className.internalName)
|
val key = className.internalName
|
||||||
|
val oldValue = storage[key] ?: return
|
||||||
|
changesCollector.collectProtoChanges(oldData = oldValue.toProtoData(className.packageFqName), newData = null)
|
||||||
|
storage.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dumpValue(value: ProtoMapValue): String {
|
override fun dumpValue(value: ProtoMapValue): String {
|
||||||
@@ -406,8 +296,9 @@ open class IncrementalCacheImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: reuse code with InlineFunctionsMap?
|
||||||
private inner class ConstantsMap(storageFile: File) : BasicStringMap<Map<String, Any>>(storageFile, ConstantsMapExternalizer) {
|
private inner class ConstantsMap(storageFile: File) : BasicStringMap<Map<String, Any>>(storageFile, ConstantsMapExternalizer) {
|
||||||
private fun getConstantsMap(bytes: ByteArray): Map<String, Any>? {
|
private fun getConstantsMap(bytes: ByteArray): Map<String, Any> {
|
||||||
val result = HashMap<String, Any>()
|
val result = HashMap<String, Any>()
|
||||||
|
|
||||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
|
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
|
||||||
@@ -420,45 +311,27 @@ open class IncrementalCacheImpl(
|
|||||||
}
|
}
|
||||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||||
|
|
||||||
return if (result.isEmpty()) null else result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(className: JvmClassName): Boolean =
|
operator fun contains(className: JvmClassName): Boolean =
|
||||||
className.internalName in storage
|
className.internalName in storage
|
||||||
|
|
||||||
fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult {
|
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||||
return put(kotlinClass.className, getConstantsMap(kotlinClass.fileContents), isPackage)
|
val key = kotlinClass.className.internalName
|
||||||
}
|
val oldMap = storage[key] ?: emptyMap()
|
||||||
|
|
||||||
private fun put(className: JvmClassName, constantsMap: Map<String, Any>?, isPackage: Boolean): CompilationResult {
|
val newMap = getConstantsMap(kotlinClass.fileContents)
|
||||||
val key = className.internalName
|
if (newMap.isNotEmpty()) {
|
||||||
|
storage[key] = newMap
|
||||||
val oldMap = storage[key]
|
|
||||||
if (oldMap == constantsMap) return CompilationResult.NO_CHANGES
|
|
||||||
|
|
||||||
if (constantsMap != null) {
|
|
||||||
storage[key] = constantsMap
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
remove(className)
|
storage.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
val changes =
|
for (const in oldMap.keys + newMap.keys) {
|
||||||
if (constantsMap == null || constantsMap.isEmpty() ||
|
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(), const, oldMap[const], newMap[const])
|
||||||
oldMap == null || oldMap.isEmpty()
|
}
|
||||||
) {
|
|
||||||
emptySequence<ChangeInfo>()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// we need only changed constants everything other should be covered by diff
|
|
||||||
val changedNames = oldMap.filter { constantsMap.containsKey(it.key) && constantsMap[it.key] != it.value }.map { it.key }
|
|
||||||
|
|
||||||
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
|
|
||||||
|
|
||||||
sequenceOf(ChangeInfo.MembersChanged(fqName, changedNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompilationResult(changes = changes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
fun remove(className: JvmClassName) {
|
||||||
@@ -602,39 +475,27 @@ open class IncrementalCacheImpl(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult {
|
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||||
return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents), isPackage)
|
val key = kotlinClass.className.internalName
|
||||||
}
|
val oldMap = storage[key] ?: emptyMap()
|
||||||
|
|
||||||
private fun put(className: JvmClassName, newMap: Map<String, Long>, isPackage: Boolean): CompilationResult {
|
val newMap = getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||||
val internalName = className.internalName
|
if (newMap.isNotEmpty()) {
|
||||||
val oldMap = storage[internalName] ?: emptyMap()
|
storage[key] = newMap
|
||||||
|
}
|
||||||
val added = hashSetOf<String>()
|
else {
|
||||||
val changed = hashSetOf<String>()
|
storage.remove(key)
|
||||||
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 {
|
for (fn in oldMap.keys + newMap.keys) {
|
||||||
newMap.isNotEmpty() -> storage[internalName] = newMap
|
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(), functionNameBySignature(fn), oldMap[fn], newMap[fn])
|
||||||
else -> storage.remove(internalName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
|
|
||||||
// TODO get name in better way instead of using substringBefore
|
|
||||||
val changes = (added.asSequence() + changed.asSequence()).map { ChangeInfo.MembersChanged(fqName, listOf(it.substringBefore("("))) }
|
|
||||||
return CompilationResult(changes = changes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO get name in better way instead of using substringBefore
|
||||||
|
private fun functionNameBySignature(signature: String): String =
|
||||||
|
signature.substringBefore("(")
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
fun remove(className: JvmClassName) {
|
||||||
storage.remove(className.internalName)
|
storage.remove(className.internalName)
|
||||||
}
|
}
|
||||||
@@ -663,16 +524,11 @@ sealed class ChangeInfo(val fqName: FqName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CompilationResult(
|
private fun LocalFileKotlinClass.scopeFqName() =
|
||||||
val changes: Sequence<ChangeInfo> = emptySequence()
|
when (classHeader.kind) {
|
||||||
) {
|
KotlinClassHeader.Kind.CLASS -> className.fqNameForClassNameWithoutDollars
|
||||||
companion object {
|
else -> className.packageFqName
|
||||||
val NO_CHANGES: CompilationResult = CompilationResult()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
operator fun plus(other: CompilationResult): CompilationResult =
|
|
||||||
CompilationResult(changes + other.changes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ByteArray.md5(): Long {
|
fun ByteArray.md5(): Long {
|
||||||
val d = MessageDigest.getInstance("MD5").digest(this)!!
|
val d = MessageDigest.getInstance("MD5").digest(this)!!
|
||||||
|
|||||||
@@ -78,20 +78,17 @@ fun makeCompileServices(
|
|||||||
|
|
||||||
fun updateIncrementalCache(
|
fun updateIncrementalCache(
|
||||||
generatedFiles: Iterable<GeneratedFile>,
|
generatedFiles: Iterable<GeneratedFile>,
|
||||||
cache: IncrementalCacheImpl
|
cache: IncrementalCacheImpl,
|
||||||
): CompilationResult {
|
changesCollector: ChangesCollector
|
||||||
var changesInfo = CompilationResult.NO_CHANGES
|
) {
|
||||||
for (generatedFile in generatedFiles) {
|
for (generatedFile in generatedFiles) {
|
||||||
when {
|
when {
|
||||||
generatedFile is GeneratedJvmClass -> changesInfo += cache.saveFileToCache(generatedFile)
|
generatedFile is GeneratedJvmClass -> cache.saveFileToCache(generatedFile, changesCollector)
|
||||||
generatedFile.outputFile.isModuleMappingFile() -> changesInfo += cache.saveModuleMappingToCache(generatedFile.sourceFiles, generatedFile.outputFile)
|
generatedFile.outputFile.isModuleMappingFile() -> cache.saveModuleMappingToCache(generatedFile.sourceFiles, generatedFile.outputFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val newChangesInfo = cache.clearCacheForRemovedClasses()
|
cache.clearCacheForRemovedClasses(changesCollector)
|
||||||
changesInfo += newChangesInfo
|
|
||||||
|
|
||||||
return changesInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LookupStorage.update(
|
fun LookupStorage.update(
|
||||||
@@ -111,14 +108,14 @@ data class DirtyData(
|
|||||||
val dirtyClassesFqNames: Collection<FqName> = emptyList()
|
val dirtyClassesFqNames: Collection<FqName> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun CompilationResult.getDirtyData(
|
fun ChangesCollector.getDirtyData(
|
||||||
caches: Iterable<IncrementalCacheCommon>,
|
caches: Iterable<IncrementalCacheCommon>,
|
||||||
reporter: ICReporter
|
reporter: ICReporter
|
||||||
): DirtyData {
|
): DirtyData {
|
||||||
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
val dirtyClassesFqNames = HashSet<FqName>()
|
val dirtyClassesFqNames = HashSet<FqName>()
|
||||||
|
|
||||||
for (change in changes) {
|
for (change in changes()) {
|
||||||
reporter.report { "Process $change" }
|
reporter.report { "Process $change" }
|
||||||
|
|
||||||
if (change is ChangeInfo.SignatureChanged) {
|
if (change is ChangeInfo.SignatureChanged) {
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.incremental.ProtoCompareGenerated.ProtoBufClassKind
|
import org.jetbrains.kotlin.incremental.ProtoCompareGenerated.ProtoBufClassKind
|
||||||
import org.jetbrains.kotlin.incremental.ProtoCompareGenerated.ProtoBufPackageKind
|
import org.jetbrains.kotlin.incremental.ProtoCompareGenerated.ProtoBufPackageKind
|
||||||
import org.jetbrains.kotlin.incremental.storage.ProtoMapValue
|
import org.jetbrains.kotlin.incremental.storage.ProtoMapValue
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||||
import org.jetbrains.kotlin.serialization.Flags
|
import org.jetbrains.kotlin.serialization.Flags
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
@@ -32,48 +32,23 @@ 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()
|
||||||
@TestOnly
|
|
||||||
val rawProtoDifference: EnumSet<*>? = null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class ProtoData
|
sealed class ProtoData
|
||||||
data class ClassProtoData(val proto: ProtoBuf.Class, val nameResolver: NameResolver) : ProtoData()
|
data class ClassProtoData(val proto: ProtoBuf.Class, val nameResolver: NameResolver) : ProtoData()
|
||||||
data class PackagePartProtoData(val proto: ProtoBuf.Package, val nameResolver: NameResolver) : ProtoData()
|
data class PackagePartProtoData(val proto: ProtoBuf.Package, val nameResolver: NameResolver, val packageFqName: FqName) : ProtoData()
|
||||||
|
|
||||||
fun ProtoMapValue.toProtoData(): ProtoData =
|
fun ProtoMapValue.toProtoData(packageFqName: FqName): ProtoData =
|
||||||
if (isPackageFacade) {
|
if (isPackageFacade) {
|
||||||
val packageData = JvmProtoBufUtil.readPackageDataFrom(bytes, strings)
|
val packageData = JvmProtoBufUtil.readPackageDataFrom(bytes, strings)
|
||||||
PackagePartProtoData(packageData.packageProto, packageData.nameResolver)
|
PackagePartProtoData(packageData.packageProto, packageData.nameResolver, packageFqName)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val classData = JvmProtoBufUtil.readClassDataFrom(bytes, strings)
|
val classData = JvmProtoBufUtil.readClassDataFrom(bytes, strings)
|
||||||
ClassProtoData(classData.classProto, classData.nameResolver)
|
ClassProtoData(classData.classProto, classData.nameResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun difference(oldValue: ProtoMapValue, newValue: ProtoMapValue): Difference =
|
|
||||||
difference(oldValue.toProtoData(), newValue.toProtoData())
|
|
||||||
|
|
||||||
fun difference(oldData: ProtoData, newData: ProtoData): Difference =
|
|
||||||
when (oldData) {
|
|
||||||
is ClassProtoData -> {
|
|
||||||
when (newData) {
|
|
||||||
is ClassProtoData ->
|
|
||||||
DifferenceCalculatorForClass(oldData, newData).difference()
|
|
||||||
is PackagePartProtoData ->
|
|
||||||
Difference(isClassAffected = true, areSubclassesAffected = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PackagePartProtoData -> {
|
|
||||||
when (newData) {
|
|
||||||
is ClassProtoData ->
|
|
||||||
Difference(isClassAffected = true)
|
|
||||||
is PackagePartProtoData ->
|
|
||||||
DifferenceCalculatorForPackageFacade(oldData, newData).difference()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val MessageLite.isPrivate: Boolean
|
internal val MessageLite.isPrivate: Boolean
|
||||||
get() = Visibilities.isPrivate(Deserialization.visibility(
|
get() = Visibilities.isPrivate(Deserialization.visibility(
|
||||||
when (this) {
|
when (this) {
|
||||||
@@ -96,7 +71,7 @@ private fun MessageLite.name(nameResolver: NameResolver): String {
|
|||||||
|
|
||||||
internal fun List<MessageLite>.names(nameResolver: NameResolver): List<String> = map { it.name(nameResolver) }
|
internal fun List<MessageLite>.names(nameResolver: NameResolver): List<String> = map { it.name(nameResolver) }
|
||||||
|
|
||||||
private abstract class DifferenceCalculator {
|
abstract class DifferenceCalculator {
|
||||||
protected abstract val compareObject: ProtoCompareGenerated
|
protected abstract val compareObject: ProtoCompareGenerated
|
||||||
|
|
||||||
abstract fun difference(): Difference
|
abstract fun difference(): Difference
|
||||||
@@ -179,7 +154,7 @@ private abstract class DifferenceCalculator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifferenceCalculatorForClass(
|
class DifferenceCalculatorForClass(
|
||||||
private val oldData: ClassProtoData,
|
private val oldData: ClassProtoData,
|
||||||
private val newData: ClassProtoData
|
private val newData: ClassProtoData
|
||||||
) : DifferenceCalculator() {
|
) : DifferenceCalculator() {
|
||||||
@@ -272,11 +247,11 @@ private class DifferenceCalculatorForClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Difference(isClassAffected, areSubclassesAffected, names, rawProtoDifference = diff)
|
return Difference(isClassAffected, areSubclassesAffected, names)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifferenceCalculatorForPackageFacade(
|
class DifferenceCalculatorForPackageFacade(
|
||||||
private val oldData: PackagePartProtoData,
|
private val oldData: PackagePartProtoData,
|
||||||
private val newData: PackagePartProtoData
|
private val newData: PackagePartProtoData
|
||||||
) : DifferenceCalculator() {
|
) : DifferenceCalculator() {
|
||||||
@@ -317,7 +292,7 @@ private class DifferenceCalculatorForPackageFacade(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Difference(changedMembersNames = names, rawProtoDifference = diff)
|
return Difference(changedMembersNames = names)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-3
@@ -135,7 +135,12 @@ abstract class IncrementalCompilerRunner<
|
|||||||
caches.platformCache.markDirty(dirtySources)
|
caches.platformCache.markDirty(dirtySources)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun compareAndUpdateCache(services: Services, caches: CacheManager, generatedFiles: List<GeneratedFile>): CompilationResult
|
protected abstract fun updateCaches(
|
||||||
|
services: Services,
|
||||||
|
caches: CacheManager,
|
||||||
|
generatedFiles: List<GeneratedFile>,
|
||||||
|
changesCollector: ChangesCollector
|
||||||
|
)
|
||||||
|
|
||||||
protected open fun preBuildHook(args: Args, compilationMode: CompilationMode) {}
|
protected open fun preBuildHook(args: Args, compilationMode: CompilationMode) {}
|
||||||
protected open fun postCompilationHook(exitCode: ExitCode) {}
|
protected open fun postCompilationHook(exitCode: ExitCode) {}
|
||||||
@@ -226,11 +231,12 @@ abstract class IncrementalCompilerRunner<
|
|||||||
allGeneratedFiles.addAll(generatedFiles)
|
allGeneratedFiles.addAll(generatedFiles)
|
||||||
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
||||||
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
||||||
val compilationResult = compareAndUpdateCache(services, caches, generatedFiles)
|
val changesCollector = ChangesCollector()
|
||||||
|
updateCaches(services, caches, generatedFiles, changesCollector)
|
||||||
|
|
||||||
if (compilationMode is CompilationMode.Rebuild) break
|
if (compilationMode is CompilationMode.Rebuild) break
|
||||||
|
|
||||||
val (dirtyLookupSymbols, dirtyClassFqNames) = compilationResult.getDirtyData(listOf(caches.platformCache), reporter)
|
val (dirtyLookupSymbols, dirtyClassFqNames) = changesCollector.getDirtyData(listOf(caches.platformCache), reporter)
|
||||||
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
||||||
|
|
||||||
with (dirtySources) {
|
with (dirtySources) {
|
||||||
|
|||||||
+8
-2
@@ -211,8 +211,14 @@ class IncrementalJvmCompilerRunner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun compareAndUpdateCache(services: Services, caches: IncrementalJvmCachesManager, generatedFiles: List<GeneratedFile>): CompilationResult =
|
override fun updateCaches(
|
||||||
updateIncrementalCache(generatedFiles, caches.platformCache)
|
services: Services,
|
||||||
|
caches: IncrementalJvmCachesManager,
|
||||||
|
generatedFiles: List<GeneratedFile>,
|
||||||
|
changesCollector: ChangesCollector
|
||||||
|
) {
|
||||||
|
updateIncrementalCache(generatedFiles, caches.platformCache, changesCollector)
|
||||||
|
}
|
||||||
|
|
||||||
override fun additionalDirtyFiles(
|
override fun additionalDirtyFiles(
|
||||||
caches: IncrementalJvmCachesManager,
|
caches: IncrementalJvmCachesManager,
|
||||||
|
|||||||
+25
-23
@@ -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
|
||||||
}
|
}
|
||||||
+5
-12
@@ -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
|
||||||
|
|||||||
+24
-15
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user