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:
committed by
nataliya.valtman
parent
271368ac53
commit
bd7c2ae6d7
@@ -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") }
|
||||
|
||||
Reference in New Issue
Block a user