KT-45777: Snapshot Java classes using ASM analysis directly

This is faster than the current approach which creates
`JavaClassDescriptor`s, converts them to protos, and then snapshots
these protos.

- Refactor unit tests to faciliate further changes
- moves test data to a directory that matches the tests' package name
- moves expected snapshots to a separate directory
- adds public and private fields/properties to sample class
- Compute changes between ASM-based Java class snapshots
- Don't collect members of an added Java class as changes
as it's enough to report the name of the added Java class as changed (we
also do that for added Kotlin classes and Kotlin/Java removed classes).
- Add unit tests for impact analysis in advance
- Compute impacted symbols of changed symbols
Also do not collect added classes/class members as they don't impact
recompilation.
-Use ClassId when computing Java class changes
It is more precise than JvmClassName, which can be ambiguous around the
`$` character (e.g., ClassId "com/example/A$B.C" and "com/example/A.B$C"
both have the same JvmClassName "com/example/A$B$C").
- Compute impacted set of changed symbols across Kotlin and Java
- Add unit tests for impact analysis across Kotlin and Java
- Compute supertypes of Kotlin classes during snapshotting
- Handle inner classes when computing list of changed symbols.
For the reported symbols, always check all options:
class member, inner class, top level class, top level member.

Test: IncrementalJavaChangeClasspathSnapshotIT.testAddingInnerClass
This commit is contained in:
Hung Nguyen
2021-10-04 10:37:24 +01:00
committed by nataliya.valtman
parent 271368ac53
commit bd7c2ae6d7
137 changed files with 2139 additions and 1603 deletions
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.incremental
import com.intellij.util.io.DataExternalizer
import org.jetbrains.kotlin.incremental.storage.FqNameExternalizer
import org.jetbrains.kotlin.incremental.storage.LinkedHashSetExternalizer
import org.jetbrains.kotlin.incremental.storage.SetExternalizer
import org.jetbrains.kotlin.incremental.storage.LookupSymbolExternalizer
import org.jetbrains.kotlin.name.FqName
import java.io.*
@@ -24,13 +24,13 @@ sealed class ClasspathChanges : Serializable {
private const val serialVersionUID = 0L
}
lateinit var lookupSymbols: LinkedHashSet<LookupSymbol>
lateinit var lookupSymbols: Set<LookupSymbol> // Preferably ordered but not required
private set
lateinit var fqNames: LinkedHashSet<FqName>
lateinit var fqNames: Set<FqName> // Preferably ordered but not required
private set
constructor(lookupSymbols: LinkedHashSet<LookupSymbol>, fqNames: LinkedHashSet<FqName>) : this() {
constructor(lookupSymbols: Set<LookupSymbol>, fqNames: Set<FqName>) : this() {
this.lookupSymbols = lookupSymbols
this.fqNames = fqNames
}
@@ -61,14 +61,14 @@ sealed class ClasspathChanges : Serializable {
private object ClasspathChangesAvailableExternalizer : DataExternalizer<ClasspathChanges.Available> {
override fun save(output: DataOutput, classpathChanges: ClasspathChanges.Available) {
LinkedHashSetExternalizer(LookupSymbolExternalizer).save(output, classpathChanges.lookupSymbols)
LinkedHashSetExternalizer(FqNameExternalizer).save(output, classpathChanges.fqNames)
SetExternalizer(LookupSymbolExternalizer).save(output, classpathChanges.lookupSymbols)
SetExternalizer(FqNameExternalizer).save(output, classpathChanges.fqNames)
}
override fun read(input: DataInput): ClasspathChanges.Available {
return ClasspathChanges.Available(
lookupSymbols = LinkedHashSetExternalizer(LookupSymbolExternalizer).read(input),
fqNames = LinkedHashSetExternalizer(FqNameExternalizer).read(input)
lookupSymbols = SetExternalizer(LookupSymbolExternalizer).read(input),
fqNames = SetExternalizer(FqNameExternalizer).read(input)
)
}
}
@@ -27,10 +27,10 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.io.DataInput
import java.io.DataInputStream
import java.io.DataOutput
import java.util.*
/**
* Storage versioning:
@@ -114,6 +114,17 @@ object ClassIdExternalizer : DataExternalizer<ClassId> {
}
}
object JvmClassNameExternalizer : DataExternalizer<JvmClassName> {
override fun save(output: DataOutput, jvmClassName: JvmClassName) {
output.writeString(jvmClassName.internalName)
}
override fun read(input: DataInput): JvmClassName {
return JvmClassName.byInternalName(input.readString())
}
}
object ProtoMapValueExternalizer : DataExternalizer<ProtoMapValue> {
override fun save(output: DataOutput, value: ProtoMapValue) {
output.writeBoolean(value.isPackageFacade)
@@ -349,8 +360,8 @@ open class GenericCollectionExternalizer<T, C : Collection<T>>(
class ListExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
GenericCollectionExternalizer<T, List<T>>(elementExternalizer, { size -> ArrayList(size) })
class LinkedHashSetExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
GenericCollectionExternalizer<T, LinkedHashSet<T>>(elementExternalizer, { size -> LinkedHashSet(size) })
class SetExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
GenericCollectionExternalizer<T, Set<T>>(elementExternalizer, { size -> LinkedHashSet(size) })
class LinkedHashMapExternalizer<K, V>(
private val keyExternalizer: DataExternalizer<K>,
@@ -7,13 +7,12 @@ package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.io.File
import javax.tools.ToolProvider
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class JavaClassNameTest {
@@ -73,15 +72,7 @@ class JavaClassNameTest {
}
val classesDir = tmpDir.newFolder()
val compiler = ToolProvider.getSystemJavaCompiler()
compiler.getStandardFileManager(null, null, null).use { fileManager ->
val compilationTask = compiler.getTask(
null, fileManager, null,
listOf("-d", classesDir.path), null,
fileManager.getJavaFileObjectsFromFiles(listOf(sourceFile))
)
assertTrue(compilationTask.call(), "Failed to compile '$className'")
}
KotlinTestUtils.compileJavaFiles(listOf(sourceFile), listOf("-d", classesDir.path))
return classesDir.walk().filter { it.isFile }
.sortedBy { it.path.substringBefore(".class") }