Recompile all class and member usages when flag and member are changed

Original commit: e7f8d7103f
This commit is contained in:
Alexey Tsvetkov
2015-12-23 15:55:46 +03:00
parent 07a18f2758
commit 7eb8c94ca8
20 changed files with 153 additions and 59 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.jps.incremental
import com.google.protobuf.MessageLite
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
import com.intellij.util.SmartList
import com.intellij.util.io.BooleanDataDescriptor
import com.intellij.util.io.EnumeratorStringDescriptor
import gnu.trove.THashSet
@@ -371,20 +372,19 @@ class IncrementalCacheImpl(
if (oldData == null || !checkChangesIsOpenPart) return CompilationResult(protoChanged = true)
val diff = difference(oldData, data)
if (!IncrementalCompilation.isExperimental()) return CompilationResult(protoChanged = diff != DifferenceKind.NONE)
val difference = difference(oldData, data)
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
val changeList = SmartList<ChangeInfo>()
val changes =
when (diff) {
is DifferenceKind.NONE -> emptySequence<ChangeInfo>()
is DifferenceKind.CLASS_SIGNATURE -> sequenceOf(ChangeInfo.SignatureChanged(fqName))
is DifferenceKind.MEMBERS -> sequenceOf(ChangeInfo.MembersChanged(fqName, diff.names))
}
if (difference.isClassSignatureChanged) {
changeList.add(ChangeInfo.SignatureChanged(fqName))
}
return CompilationResult(protoChanged = diff != DifferenceKind.NONE, changes = changes)
if (difference.changedMembersNames.isNotEmpty()) {
changeList.add(ChangeInfo.MembersChanged(fqName, difference.changedMembersNames))
}
return CompilationResult(protoChanged = changeList.isNotEmpty(), changes = changeList.asSequence())
}
operator fun contains(className: JvmClassName): Boolean =
@@ -29,17 +29,21 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import org.jetbrains.kotlin.utils.HashSetUtil
import java.util.*
sealed class DifferenceKind() {
object NONE: DifferenceKind()
object CLASS_SIGNATURE: DifferenceKind()
class MEMBERS(val names: Collection<String>): DifferenceKind()
}
data class Difference(
val isClassSignatureChanged: Boolean = false,
val changedMembersNames: Set<String> = emptySet()
)
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): DifferenceKind {
if (oldData.isPackageFacade != newData.isPackageFacade) return DifferenceKind.CLASS_SIGNATURE
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): Difference {
if (oldData.isPackageFacade != newData.isPackageFacade) return Difference(isClassSignatureChanged = true)
val differenceObject =
if (oldData.isPackageFacade) DifferenceCalculatorForPackageFacade(oldData, newData) else DifferenceCalculatorForClass(oldData, newData)
if (oldData.isPackageFacade) {
DifferenceCalculatorForPackageFacade(oldData, newData)
}
else {
DifferenceCalculatorForClass(oldData, newData)
}
return differenceObject.difference()
}
@@ -70,9 +74,7 @@ private abstract class DifferenceCalculator() {
protected val compareObject by lazy { ProtoCompareGenerated(oldNameResolver, newNameResolver) }
abstract fun difference(): DifferenceKind
protected fun membersOrNone(names: Collection<String>): DifferenceKind = if (names.isEmpty()) DifferenceKind.NONE else DifferenceKind.MEMBERS(names)
abstract fun difference(): Difference
protected fun calcDifferenceForMembers(oldList: List<MessageLite>, newList: List<MessageLite>): Collection<String> {
val result = hashSetOf<String>()
@@ -172,15 +174,8 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
val diff = compareObject.difference(oldProto, newProto)
override fun difference(): DifferenceKind {
if (diff.isEmpty()) return DifferenceKind.NONE
CLASS_SIGNATURE_ENUMS.forEach { if (it in diff) return DifferenceKind.CLASS_SIGNATURE }
return membersOrNone(getChangedMembersNames())
}
private fun getChangedMembersNames(): Set<String> {
override fun difference(): Difference {
var isClassSignatureChanged = false
val names = hashSetOf<String>()
fun Int.oldToNames() = names.add(oldNameResolver.getString(this))
@@ -211,17 +206,14 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
ProtoBufClassKind.TYPE_TABLE -> {
// TODO
}
ProtoBufClassKind.FLAGS,
ProtoBufClassKind.FQ_NAME,
ProtoBufClassKind.TYPE_PARAMETER_LIST,
ProtoBufClassKind.SUPERTYPE_LIST,
ProtoBufClassKind.CLASS_ANNOTATION_LIST ->
throw IllegalArgumentException("Unexpected kind: $kind")
else ->
throw IllegalArgumentException("Unsupported kind: $kind")
in CLASS_SIGNATURE_ENUMS -> {
isClassSignatureChanged = true
}
else -> throw IllegalArgumentException("Unsupported kind: $kind")
}
}
return names
return Difference(isClassSignatureChanged, names)
}
}
@@ -237,13 +229,7 @@ private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newDa
val diff = compareObject.difference(oldProto, newProto)
override fun difference(): DifferenceKind {
if (diff.isEmpty()) return DifferenceKind.NONE
return membersOrNone(getChangedMembersNames())
}
private fun getChangedMembersNames(): Set<String> {
override fun difference(): Difference {
val names = hashSetOf<String>()
fun calcDifferenceForNonPrivateMembers(members: (ProtoBuf.Package) -> List<MessageLite>): Collection<String> {
@@ -261,11 +247,10 @@ private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newDa
ProtoBufPackageKind.TYPE_TABLE -> {
// TODO
}
else ->
throw IllegalArgumentException("Unsupported kind: $kind")
else -> throw IllegalArgumentException("Unsupported kind: $kind")
}
}
return names
return Difference(isClassSignatureChanged = false, changedMembersNames = names)
}
}
@@ -1081,6 +1081,12 @@ public class ExperimentalIncrementalJpsTestGenerated extends AbstractExperimenta
doTest(fileName);
}
@TestMetadata("flagsAndMemberInSameClassChanged")
public void testFlagsAndMemberInSameClassChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/flagsAndMemberInSameClassChanged/");
doTest(fileName);
}
@TestMetadata("jvmNameChanged")
public void testJvmNameChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/jvmNameChanged/");
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.jps.incremental
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.SmartList
import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
@@ -112,16 +113,21 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
}
}
val changes = when (diff) {
is DifferenceKind.NONE ->
"NONE"
is DifferenceKind.CLASS_SIGNATURE ->
"CLASS_SIGNATURE"
is DifferenceKind.MEMBERS ->
"MEMBERS\n ${diff.names.sorted()}"
val changes = SmartList<String>()
if (diff.isClassSignatureChanged) {
changes.add("CLASS_SIGNATURE")
}
println("changes in ${oldLocalFileKotlinClass.classId}: $changes")
if (diff.changedMembersNames.isNotEmpty()) {
changes.add("MEMBERS\n ${diff.changedMembersNames.sorted()}")
}
if (changes.isEmpty()) {
changes.add("NONE")
}
println("changes in ${oldLocalFileKotlinClass.classId}: ${changes.joinToString()}")
}
private fun File.createSubDirectory(relativePath: String): File {
@@ -37,6 +37,12 @@ public class ProtoComparisonTestGenerated extends AbstractProtoComparisonTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/classSignatureChange"), Pattern.compile("^([^\\.]+)$"), true);
}
@TestMetadata("classFlagsAndMembersChanged")
public void testClassFlagsAndMembersChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classFlagsAndMembersChanged/");
doTest(fileName);
}
@TestMetadata("classFlagsChanged")
public void testClassFlagsChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classFlagsChanged/");
@@ -0,0 +1,5 @@
package test
open class A {
fun f() {}
}
@@ -0,0 +1,3 @@
package test
class A
@@ -0,0 +1,2 @@
changes in test/A: CLASS_SIGNATURE, MEMBERS
[f]
@@ -1 +1,2 @@
changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE
changes in test/ClassWithClassAnnotationListChanged: CLASS_SIGNATURE, MEMBERS
[<init>]
@@ -0,0 +1,3 @@
open class A {
val x: Any = "A.x"
}
@@ -0,0 +1,3 @@
private open class A {
val x: Any? = null
}
@@ -0,0 +1,3 @@
open class A {
val x: Any? = null
}
@@ -0,0 +1,56 @@
Cleaning output files:
out/production/module/A.class
End of files
Compiling files:
src/A.kt
End of files
Cleaning output files:
out/production/module/B.class
out/production/module/GetAKt.class
out/production/module/GetBKt.class
out/production/module/META-INF/module.kotlin_module
out/production/module/UseAKt.class
out/production/module/UseBKt.class
End of files
Compiling files:
src/B.kt
src/getA.kt
src/getB.kt
src/useA.kt
src/useB.kt
End of files
COMPILATION FAILED
Cannot access 'A': it is 'private' in file
Subclass effective visibility 'public' should be the same or less permissive than its superclass effective visibility 'private'
Function effective visibility 'public' should be the same or less permissive than its return type effective visibility 'private'
Cannot access 'A': it is 'private' in file
Cannot access 'A': it is 'private' in file
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.Any?
Cannot access 'x': it is 'invisible_fake' in 'B'
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.Any?
Cleaning output files:
out/production/module/A.class
End of files
Compiling files:
src/A.kt
src/B.kt
src/getA.kt
src/getB.kt
src/useA.kt
src/useB.kt
End of files
COMPILATION FAILED
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.Any?
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.Any?
Compiling files:
src/A.kt
src/B.kt
src/getA.kt
src/getB.kt
src/useA.kt
src/useB.kt
End of files
@@ -0,0 +1,3 @@
fun useA() {
getA().x.hashCode()
}
@@ -0,0 +1,3 @@
fun useA() {
getA().x?.hashCode()
}
@@ -0,0 +1,3 @@
fun useB() {
getB().x.hashCode()
}
@@ -0,0 +1,3 @@
fun useB() {
getB().x?.hashCode()
}