Save class hierarchy to incremental caches
Author: Alexey Tsvetkov <Alexey.Tsvetkov@jetbrains.com>
This commit is contained in:
committed by
Alexey Tsvetkov
parent
6ba5dcaa06
commit
c567376e35
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private val NORMAL_VERSION = 7
|
private val NORMAL_VERSION = 7
|
||||||
private val EXPERIMENTAL_VERSION = 1
|
private val EXPERIMENTAL_VERSION = 2
|
||||||
private val DATA_CONTAINER_VERSION = 1
|
private val DATA_CONTAINER_VERSION = 1
|
||||||
|
|
||||||
private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
|
private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.supertypes
|
||||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
@@ -69,12 +71,20 @@ public class IncrementalCacheImpl(
|
|||||||
val DIRTY_OUTPUT_CLASSES = "dirty-output-classes"
|
val DIRTY_OUTPUT_CLASSES = "dirty-output-classes"
|
||||||
val DIRTY_INLINE_FUNCTIONS = "dirty-inline-functions"
|
val DIRTY_INLINE_FUNCTIONS = "dirty-inline-functions"
|
||||||
val INLINED_TO = "inlined-to"
|
val INLINED_TO = "inlined-to"
|
||||||
|
val SUBTYPES = "subtypes"
|
||||||
|
val SUPERTYPES = "supertypes"
|
||||||
|
|
||||||
private val MODULE_MAPPING_FILE_NAME = "." + ModuleMapping.MAPPING_FILE_EXT
|
private val MODULE_MAPPING_FILE_NAME = "." + ModuleMapping.MAPPING_FILE_EXT
|
||||||
}
|
}
|
||||||
|
|
||||||
private val baseDir = File(paths.getTargetDataRoot(target), KOTLIN_CACHE_DIRECTORY_NAME)
|
private val baseDir = File(paths.getTargetDataRoot(target), KOTLIN_CACHE_DIRECTORY_NAME)
|
||||||
private val cacheVersionProvider = CacheVersionProvider(paths)
|
private val cacheVersionProvider = CacheVersionProvider(paths)
|
||||||
|
private val experimentalMaps = arrayListOf<BasicMap<*, *>>()
|
||||||
|
|
||||||
|
private fun <K, V, M : BasicMap<K, V>> registerExperimentalMap(map: M): M {
|
||||||
|
experimentalMaps.add(map)
|
||||||
|
return registerMap(map)
|
||||||
|
}
|
||||||
|
|
||||||
private val String.storageFile: File
|
private val String.storageFile: File
|
||||||
get() = File(baseDir, this + "." + CACHE_EXTENSION)
|
get() = File(baseDir, this + "." + CACHE_EXTENSION)
|
||||||
@@ -89,10 +99,15 @@ public class IncrementalCacheImpl(
|
|||||||
private val dirtyOutputClassesMap = registerMap(DirtyOutputClassesMap(DIRTY_OUTPUT_CLASSES.storageFile))
|
private val dirtyOutputClassesMap = registerMap(DirtyOutputClassesMap(DIRTY_OUTPUT_CLASSES.storageFile))
|
||||||
private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile))
|
private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile))
|
||||||
private val inlinedTo = registerMap(InlineFunctionsFilesMap(INLINED_TO.storageFile))
|
private val inlinedTo = registerMap(InlineFunctionsFilesMap(INLINED_TO.storageFile))
|
||||||
|
private val subtypesMap = registerExperimentalMap(SubtypesMap(SUBTYPES.storageFile))
|
||||||
|
private val supertypesMap = registerExperimentalMap(SupertypesMap(SUPERTYPES.storageFile))
|
||||||
|
|
||||||
private val dependents = arrayListOf<IncrementalCacheImpl>()
|
private val dependents = arrayListOf<IncrementalCacheImpl>()
|
||||||
private val outputDir = requireNotNull(target.outputDir) { "Target is expected to have output directory: $target" }
|
private val outputDir = requireNotNull(target.outputDir) { "Target is expected to have output directory: $target" }
|
||||||
|
|
||||||
|
private val dependentsWithThis: Iterable<IncrementalCacheImpl>
|
||||||
|
get() = dependents + this
|
||||||
|
|
||||||
override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) {
|
override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) {
|
||||||
inlinedTo.add(fromPath, jvmSignature, toPath)
|
inlinedTo.add(fromPath, jvmSignature, toPath)
|
||||||
}
|
}
|
||||||
@@ -118,13 +133,10 @@ public class IncrementalCacheImpl(
|
|||||||
for ((className, functions) in dirtyInlineFunctionsMap.getEntries()) {
|
for ((className, functions) in dirtyInlineFunctionsMap.getEntries()) {
|
||||||
val classFilePath = getClassFilePath(className.internalName)
|
val classFilePath = getClassFilePath(className.internalName)
|
||||||
|
|
||||||
fun addFilesAffectedByChangedInlineFuns(cache: IncrementalCacheImpl) {
|
for (cache in dependentsWithThis) {
|
||||||
val targetFiles = functions.flatMap { cache.inlinedTo[classFilePath, it] }
|
val targetFiles = functions.flatMap { cache.inlinedTo[classFilePath, it] }
|
||||||
result.addAll(targetFiles)
|
result.addAll(targetFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilesAffectedByChangedInlineFuns(this)
|
|
||||||
dependents.forEach(::addFilesAffectedByChangedInlineFuns)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.map { File(it) }
|
return result.map { File(it) }
|
||||||
@@ -149,7 +161,7 @@ public class IncrementalCacheImpl(
|
|||||||
public fun saveFileToCache(generatedClass: GeneratedJvmClass): CompilationResult {
|
public fun saveFileToCache(generatedClass: GeneratedJvmClass): CompilationResult {
|
||||||
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
||||||
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
||||||
val className = JvmClassName.byClassId(kotlinClass.classId)
|
val className = kotlinClass.className
|
||||||
|
|
||||||
dirtyOutputClassesMap.notDirty(className.internalName)
|
dirtyOutputClassesMap.notDirty(className.internalName)
|
||||||
sourceFiles.forEach {
|
sourceFiles.forEach {
|
||||||
@@ -185,6 +197,8 @@ public class IncrementalCacheImpl(
|
|||||||
inlineFunctionsMap.process(kotlinClass, isPackage = true)
|
inlineFunctionsMap.process(kotlinClass, isPackage = true)
|
||||||
}
|
}
|
||||||
header.isCompatibleClassKind() && !header.isLocalClass -> {
|
header.isCompatibleClassKind() && !header.isLocalClass -> {
|
||||||
|
addToClassStorage(kotlinClass)
|
||||||
|
|
||||||
protoMap.process(kotlinClass, isPackage = false) +
|
protoMap.process(kotlinClass, isPackage = false) +
|
||||||
constantsMap.process(kotlinClass) +
|
constantsMap.process(kotlinClass) +
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage = false)
|
inlineFunctionsMap.process(kotlinClass, isPackage = false)
|
||||||
@@ -268,6 +282,9 @@ public class IncrementalCacheImpl(
|
|||||||
constantsMap.remove(it)
|
constantsMap.remove(it)
|
||||||
inlineFunctionsMap.remove(it)
|
inlineFunctionsMap.remove(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeAllFromClassStorage(dirtyClasses)
|
||||||
|
|
||||||
dirtyOutputClassesMap.clean()
|
dirtyOutputClassesMap.clean()
|
||||||
return changesInfo
|
return changesInfo
|
||||||
}
|
}
|
||||||
@@ -316,6 +333,7 @@ public class IncrementalCacheImpl(
|
|||||||
|
|
||||||
public fun cleanExperimental() {
|
public fun cleanExperimental() {
|
||||||
cacheVersionProvider.experimentalVersion(target).clean()
|
cacheVersionProvider.experimentalVersion(target).clean()
|
||||||
|
experimentalMaps.forEach { it.clean() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
||||||
@@ -576,6 +594,46 @@ public class IncrementalCacheImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addToClassStorage(kotlinClass: LocalFileKotlinClass) {
|
||||||
|
if (!IncrementalCompilation.isExperimental()) return
|
||||||
|
|
||||||
|
val classData = JvmProtoBufUtil.readClassDataFrom(kotlinClass.classHeader.annotationData!!, kotlinClass.classHeader.strings!!)
|
||||||
|
val supertypes = classData.classProto.supertypes(TypeTable(classData.classProto.typeTable))
|
||||||
|
val parents = supertypes.map { classData.nameResolver.getClassId(it.className).asSingleFqName() }
|
||||||
|
.filter { it.asString() != "kotlin.Any" }
|
||||||
|
val child = kotlinClass.classId.asSingleFqName()
|
||||||
|
|
||||||
|
parents.forEach { subtypesMap.add(it, child) }
|
||||||
|
supertypesMap[child] = parents
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeAllFromClassStorage(removedClasses: Collection<JvmClassName>) {
|
||||||
|
if (!IncrementalCompilation.isExperimental() || removedClasses.isEmpty()) return
|
||||||
|
|
||||||
|
val removedFqNames = removedClasses.map { it.fqNameForClassNameWithoutDollars }.toSet()
|
||||||
|
|
||||||
|
for (cache in dependentsWithThis) {
|
||||||
|
val parentsFqNames = hashSetOf<FqName>()
|
||||||
|
val childrenFqNames = hashSetOf<FqName>()
|
||||||
|
|
||||||
|
for (removedFqName in removedFqNames) {
|
||||||
|
parentsFqNames.addAll(cache.supertypesMap[removedFqName])
|
||||||
|
childrenFqNames.addAll(cache.subtypesMap[removedFqName])
|
||||||
|
|
||||||
|
cache.supertypesMap.remove(removedFqName)
|
||||||
|
cache.subtypesMap.remove(removedFqName)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (child in childrenFqNames) {
|
||||||
|
cache.supertypesMap.removeValues(child, removedFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (parent in parentsFqNames) {
|
||||||
|
cache.subtypesMap.removeValues(parent, removedFqNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class DirtyOutputClassesMap(storageFile: File) : BasicStringMap<Boolean>(storageFile, BooleanDataDescriptor.INSTANCE) {
|
private inner class DirtyOutputClassesMap(storageFile: File) : BasicStringMap<Boolean>(storageFile, BooleanDataDescriptor.INSTANCE) {
|
||||||
public fun markDirty(className: String) {
|
public fun markDirty(className: String) {
|
||||||
storage[className] = true
|
storage[className] = true
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ internal abstract class BasicMap<K : Comparable<K>, V>(
|
|||||||
}.toString()
|
}.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
protected abstract fun dumpKey(key: K): String
|
protected abstract fun dumpKey(key: K): String
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
protected abstract fun dumpValue(value: V): String
|
protected abstract fun dumpValue(value: V): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.jps.incremental.storage
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.jps.incremental.dumpCollection
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal open class ClassOneToManyMap(
|
||||||
|
storageFile: File
|
||||||
|
) : BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) {
|
||||||
|
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
||||||
|
|
||||||
|
fun add(key: FqName, value: FqName) {
|
||||||
|
storage.append(key.asString()) { out -> out.writeUTF(value.asString()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(key: FqName): Collection<FqName> =
|
||||||
|
storage[key.asString()]?.map(::FqName) ?: setOf()
|
||||||
|
|
||||||
|
operator fun set(key: FqName, values: Collection<FqName>) {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
remove(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storage[key.asString()] = values.map(FqName::asString)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(key: FqName) {
|
||||||
|
storage.remove(key.asString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeValues(key: FqName, removed: Set<FqName>) {
|
||||||
|
val notRemoved = this[key].filter { it !in removed }
|
||||||
|
this[key] = notRemoved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SubtypesMap(storageFile: File) : ClassOneToManyMap(storageFile)
|
||||||
|
internal class SupertypesMap(storageFile: File) : ClassOneToManyMap(storageFile)
|
||||||
+6
@@ -41,6 +41,12 @@ public class ExperimentalIncrementalLazyCachesTestGenerated extends AbstractExpe
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classInheritance")
|
||||||
|
public void testClassInheritance() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/classInheritance/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("constant")
|
@TestMetadata("constant")
|
||||||
public void testConstant() throws Exception {
|
public void testConstant() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/constant/");
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/constant/");
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ public class IncrementalLazyCachesTestGenerated extends AbstractIncrementalLazyC
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classInheritance")
|
||||||
|
public void testClassInheritance() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/classInheritance/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("constant")
|
@TestMetadata("constant")
|
||||||
public void testConstant() throws Exception {
|
public void testConstant() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/constant/");
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/lazyKotlinCaches/constant/");
|
||||||
|
|||||||
Vendored
+2
@@ -16,6 +16,8 @@ Module 'module2' production
|
|||||||
package-parts.tab
|
package-parts.tab
|
||||||
proto.tab
|
proto.tab
|
||||||
source-to-classes.tab
|
source-to-classes.tab
|
||||||
|
subtypes.tab
|
||||||
|
supertypes.tab
|
||||||
Module 'module2' tests
|
Module 'module2' tests
|
||||||
Module 'module3' production
|
Module 'module3' production
|
||||||
experimental-format-version.txt
|
experimental-format-version.txt
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
out/production/module/B.class
|
||||||
|
out/production/module/C.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/main.kt
|
||||||
|
End of files
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
kotlin-data-container
|
||||||
|
Module 'module' production
|
||||||
|
format-version.txt
|
||||||
|
proto.tab
|
||||||
|
source-to-classes.tab
|
||||||
|
Module 'module' tests
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
kotlin-data-container
|
||||||
|
data-container-format-version.txt
|
||||||
|
counters.tab
|
||||||
|
file-to-id.tab
|
||||||
|
id-to-file.tab
|
||||||
|
lookups.tab
|
||||||
|
Module 'module' production
|
||||||
|
experimental-format-version.txt
|
||||||
|
format-version.txt
|
||||||
|
proto.tab
|
||||||
|
source-to-classes.tab
|
||||||
|
subtypes.tab
|
||||||
|
supertypes.tab
|
||||||
|
Module 'module' tests
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
open class A
|
||||||
|
|
||||||
|
open class B : A()
|
||||||
|
|
||||||
|
class C : B()
|
||||||
Reference in New Issue
Block a user