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.google.protobuf.MessageLite
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
@@ -371,20 +372,19 @@ class IncrementalCacheImpl(
if (oldData == null || !checkChangesIsOpenPart) return CompilationResult(protoChanged = true) if (oldData == null || !checkChangesIsOpenPart) return CompilationResult(protoChanged = true)
val diff = difference(oldData, data) val difference = difference(oldData, data)
if (!IncrementalCompilation.isExperimental()) return CompilationResult(protoChanged = diff != DifferenceKind.NONE)
val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
val changeList = SmartList<ChangeInfo>()
val changes = if (difference.isClassSignatureChanged) {
when (diff) { changeList.add(ChangeInfo.SignatureChanged(fqName))
is DifferenceKind.NONE -> emptySequence<ChangeInfo>() }
is DifferenceKind.CLASS_SIGNATURE -> sequenceOf(ChangeInfo.SignatureChanged(fqName))
is DifferenceKind.MEMBERS -> sequenceOf(ChangeInfo.MembersChanged(fqName, diff.names))
}
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 = operator fun contains(className: JvmClassName): Boolean =
@@ -29,17 +29,21 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import org.jetbrains.kotlin.utils.HashSetUtil import org.jetbrains.kotlin.utils.HashSetUtil
import java.util.* import java.util.*
sealed class DifferenceKind() { data class Difference(
object NONE: DifferenceKind() val isClassSignatureChanged: Boolean = false,
object CLASS_SIGNATURE: DifferenceKind() val changedMembersNames: Set<String> = emptySet()
class MEMBERS(val names: Collection<String>): DifferenceKind() )
}
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): DifferenceKind { fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): Difference {
if (oldData.isPackageFacade != newData.isPackageFacade) return DifferenceKind.CLASS_SIGNATURE if (oldData.isPackageFacade != newData.isPackageFacade) return Difference(isClassSignatureChanged = true)
val differenceObject = 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() return differenceObject.difference()
} }
@@ -70,9 +74,7 @@ private abstract class DifferenceCalculator() {
protected val compareObject by lazy { ProtoCompareGenerated(oldNameResolver, newNameResolver) } protected val compareObject by lazy { ProtoCompareGenerated(oldNameResolver, newNameResolver) }
abstract fun difference(): DifferenceKind abstract fun difference(): Difference
protected fun membersOrNone(names: Collection<String>): DifferenceKind = if (names.isEmpty()) DifferenceKind.NONE else DifferenceKind.MEMBERS(names)
protected fun calcDifferenceForMembers(oldList: List<MessageLite>, newList: List<MessageLite>): Collection<String> { protected fun calcDifferenceForMembers(oldList: List<MessageLite>, newList: List<MessageLite>): Collection<String> {
val result = hashSetOf<String>() val result = hashSetOf<String>()
@@ -172,15 +174,8 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
val diff = compareObject.difference(oldProto, newProto) val diff = compareObject.difference(oldProto, newProto)
override fun difference(): DifferenceKind { override fun difference(): Difference {
if (diff.isEmpty()) return DifferenceKind.NONE var isClassSignatureChanged = false
CLASS_SIGNATURE_ENUMS.forEach { if (it in diff) return DifferenceKind.CLASS_SIGNATURE }
return membersOrNone(getChangedMembersNames())
}
private fun getChangedMembersNames(): Set<String> {
val names = hashSetOf<String>() val names = hashSetOf<String>()
fun Int.oldToNames() = names.add(oldNameResolver.getString(this)) fun Int.oldToNames() = names.add(oldNameResolver.getString(this))
@@ -211,17 +206,14 @@ private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: Prot
ProtoBufClassKind.TYPE_TABLE -> { ProtoBufClassKind.TYPE_TABLE -> {
// TODO // TODO
} }
ProtoBufClassKind.FLAGS, in CLASS_SIGNATURE_ENUMS -> {
ProtoBufClassKind.FQ_NAME, isClassSignatureChanged = true
ProtoBufClassKind.TYPE_PARAMETER_LIST, }
ProtoBufClassKind.SUPERTYPE_LIST, else -> throw IllegalArgumentException("Unsupported kind: $kind")
ProtoBufClassKind.CLASS_ANNOTATION_LIST ->
throw IllegalArgumentException("Unexpected kind: $kind")
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) val diff = compareObject.difference(oldProto, newProto)
override fun difference(): DifferenceKind { override fun difference(): Difference {
if (diff.isEmpty()) return DifferenceKind.NONE
return membersOrNone(getChangedMembersNames())
}
private fun getChangedMembersNames(): Set<String> {
val names = hashSetOf<String>() val names = hashSetOf<String>()
fun calcDifferenceForNonPrivateMembers(members: (ProtoBuf.Package) -> List<MessageLite>): Collection<String> { fun calcDifferenceForNonPrivateMembers(members: (ProtoBuf.Package) -> List<MessageLite>): Collection<String> {
@@ -261,11 +247,10 @@ private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newDa
ProtoBufPackageKind.TYPE_TABLE -> { ProtoBufPackageKind.TYPE_TABLE -> {
// TODO // TODO
} }
else -> else -> throw IllegalArgumentException("Unsupported kind: $kind")
throw IllegalArgumentException("Unsupported kind: $kind")
} }
} }
return names return Difference(isClassSignatureChanged = false, changedMembersNames = names)
} }
} }
@@ -1081,6 +1081,12 @@ public class ExperimentalIncrementalJpsTestGenerated extends AbstractExperimenta
doTest(fileName); doTest(fileName);
} }
@TestMetadata("flagsAndMemberInSameClassChanged")
public void testFlagsAndMemberInSameClassChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/flagsAndMemberInSameClassChanged/");
doTest(fileName);
}
@TestMetadata("jvmNameChanged") @TestMetadata("jvmNameChanged")
public void testJvmNameChanged() throws Exception { public void testJvmNameChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/classHierarchyAffected/jvmNameChanged/"); 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.openapi.util.io.FileUtil
import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.SmartList
import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
@@ -112,16 +113,21 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
} }
} }
val changes = when (diff) { val changes = SmartList<String>()
is DifferenceKind.NONE ->
"NONE" if (diff.isClassSignatureChanged) {
is DifferenceKind.CLASS_SIGNATURE -> changes.add("CLASS_SIGNATURE")
"CLASS_SIGNATURE"
is DifferenceKind.MEMBERS ->
"MEMBERS\n ${diff.names.sorted()}"
} }
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 { 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); 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") @TestMetadata("classFlagsChanged")
public void testClassFlagsChanged() throws Exception { public void testClassFlagsChanged() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classFlagsChanged/"); 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()
}