KT-45777: Allow 2 levels of granularity when tracking changes
1. CLASS_LEVEL: allows tracking whether a .class file has changed
without tracking what specific parts of the .class file (e.g.,
fields or methods) have changed.
2. CLASS_MEMBER_LEVEL: allows tracking not only whether a .class file
has changed but also what specific parts of the .class file (e.g.,
fields or methods) have changed.
The idea is that for better performance we will use CLASS_LEVEL for
classpath entries that are usually unchanged, and CLASS_MEMBER_LEVEL
for classpath entries that are frequently changed. We'll work out the
specifics in a following commit after some measurements.
Support running kotlinc on Windows in ClasspathSnapshotTestCommon
Also add tests for different Kotlin class kinds.
Add unit tests for CLASS_LEVEL snapshotting and diffing
Test: Updated ClasspathSnapshotterTest + ClasspathChangesComputerTest
Add ClasspathChangesComputerTest.testMixedClassSnapshotGranularities
This commit is contained in:
committed by
nataliya.valtman
parent
05457c291d
commit
d2193f3873
@@ -35,33 +35,33 @@ fun File.isClassFile(): Boolean =
|
||||
*/
|
||||
fun File.cleanDirectoryContents() {
|
||||
when {
|
||||
isDirectory -> listFiles()!!.forEach { it.forceDeleteRecursively() }
|
||||
isDirectory -> listFiles()!!.forEach { it.deleteRecursivelyOrThrow() }
|
||||
isFile -> error("File.cleanDirectoryContents does not accept a regular file: $path")
|
||||
else -> forceMkdirs()
|
||||
else -> mkdirsOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
/** Deletes this file or directory recursively (if it exists). */
|
||||
fun File.forceDeleteRecursively() {
|
||||
/** Deletes this file or directory recursively (if it exists), throwing an exception if the deletion failed. */
|
||||
fun File.deleteRecursivelyOrThrow() {
|
||||
if (!deleteRecursively()) {
|
||||
throw IOException("Could not delete '$path'")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this directory (if it does not yet exist).
|
||||
*
|
||||
* If this is a regular file, this method will throw an exception.
|
||||
* Creates this directory (if it does not yet exist), throwing an exception if the directiory creation failed or if a regular file already
|
||||
* exists at this path.
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
fun File.forceMkdirs() {
|
||||
fun File.mkdirsOrThrow() {
|
||||
when {
|
||||
this.isDirectory -> { /* Do nothing */ }
|
||||
this.isFile -> error("File.forceMkdirs does not accept a regular file: $path")
|
||||
isDirectory -> Unit
|
||||
isFile -> error("A regular file already exists at this path: $path")
|
||||
else -> {
|
||||
// Note that if the directory already exists, mkdirs() will return `false`, but here we ensure that the directory does not exist
|
||||
// before calling mkdirs(), so it's safe to check the returned result of mkdirs() below.
|
||||
if (!mkdirs()) {
|
||||
// Note that `mkdirs()` returns `false` if the directory already exists (even though we have checked that the directory did not
|
||||
// exist earlier, it might just have been created by some other thread). Therefore, we need to check if the directory exists
|
||||
// again when `mkdirs()` returns `false`.
|
||||
if (!mkdirs() && !isDirectory) {
|
||||
throw IOException("Could not create directory '$path'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ object ByteArrayExternalizer : DataExternalizer<ByteArray> {
|
||||
}
|
||||
}
|
||||
|
||||
open class GenericCollectionExternalizer<T, C : Collection<T>>(
|
||||
abstract class GenericCollectionExternalizer<T, C : Collection<T>>(
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: (size: Int) -> MutableCollection<T>
|
||||
) : DataExternalizer<C> {
|
||||
@@ -364,6 +364,9 @@ open class GenericCollectionExternalizer<T, C : Collection<T>>(
|
||||
repeat(size) {
|
||||
collection.add(elementExternalizer.read(input))
|
||||
}
|
||||
// We want `collection` to be both a mutable collection (so we can add elements to it as done above) and a type that can be safely
|
||||
// converted to type `C` (to be used as the returned value of this method). However, there is no type-safe way to express that, so
|
||||
// we have to use this unsafe cast.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return collection as C
|
||||
}
|
||||
@@ -375,12 +378,13 @@ class ListExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
|
||||
class SetExternalizer<T>(elementExternalizer: DataExternalizer<T>) :
|
||||
GenericCollectionExternalizer<T, Set<T>>(elementExternalizer, { size -> LinkedHashSet(size) })
|
||||
|
||||
class LinkedHashMapExternalizer<K, V>(
|
||||
open class MapExternalizer<K, V, M : Map<K, V>>(
|
||||
private val keyExternalizer: DataExternalizer<K>,
|
||||
private val valueExternalizer: DataExternalizer<V>
|
||||
) : DataExternalizer<LinkedHashMap<K, V>> {
|
||||
private val valueExternalizer: DataExternalizer<V>,
|
||||
private val newMap: (size: Int) -> MutableMap<K, V> = { size -> LinkedHashMap(size) }
|
||||
) : DataExternalizer<M> {
|
||||
|
||||
override fun save(output: DataOutput, map: LinkedHashMap<K, V>) {
|
||||
override fun save(output: DataOutput, map: M) {
|
||||
output.writeInt(map.size)
|
||||
for ((key, value) in map) {
|
||||
keyExternalizer.save(output, key)
|
||||
@@ -388,14 +392,20 @@ class LinkedHashMapExternalizer<K, V>(
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): LinkedHashMap<K, V> {
|
||||
override fun read(input: DataInput): M {
|
||||
val size = input.readInt()
|
||||
val map = LinkedHashMap<K, V>(size)
|
||||
val map = newMap(size)
|
||||
repeat(size) {
|
||||
val key = keyExternalizer.read(input)
|
||||
val value = valueExternalizer.read(input)
|
||||
map[key] = value
|
||||
}
|
||||
return map
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return map as M
|
||||
}
|
||||
}
|
||||
|
||||
class LinkedHashMapExternalizer<K, V>(
|
||||
keyExternalizer: DataExternalizer<K>,
|
||||
valueExternalizer: DataExternalizer<V>
|
||||
) : MapExternalizer<K, V, LinkedHashMap<K, V>>(keyExternalizer, valueExternalizer, { size -> LinkedHashMap(size) })
|
||||
|
||||
Reference in New Issue
Block a user