diff --git a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalCacheImpl.kt b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalCacheImpl.kt index 2cabe2d4a52..15200c06ce1 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalCacheImpl.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalCacheImpl.kt @@ -25,6 +25,7 @@ import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.build.GeneratedJvmClass import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.incremental.storage.* +import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames import org.jetbrains.kotlin.load.kotlin.ModuleMapping import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache @@ -37,10 +38,7 @@ 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.JvmProtoBufUtil -import org.jetbrains.org.objectweb.asm.ClassReader -import org.jetbrains.org.objectweb.asm.ClassVisitor -import org.jetbrains.org.objectweb.asm.FieldVisitor -import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.* import java.io.File import java.security.MessageDigest import java.util.* @@ -60,6 +58,7 @@ open class IncrementalCacheImpl( private val MULTIFILE_CLASS_PARTS = "multifile-class-parts" private val SOURCE_TO_CLASSES = "source-to-classes" private val DIRTY_OUTPUT_CLASSES = "dirty-output-classes" + private val INLINE_FUNCTIONS = "inline-functions" private val SUBTYPES = "subtypes" private val SUPERTYPES = "supertypes" private val CLASS_FQ_NAME_TO_SOURCE = "class-fq-name-to-source" @@ -85,6 +84,7 @@ open class IncrementalCacheImpl( private val partToMultifileFacade = registerMap(MultifileClassPartMap(MULTIFILE_CLASS_PARTS.storageFile)) private val sourceToClassesMap = registerMap(SourceToClassesMap(SOURCE_TO_CLASSES.storageFile)) private val dirtyOutputClassesMap = registerMap(DirtyOutputClassesMap(DIRTY_OUTPUT_CLASSES.storageFile)) + private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile)) private val subtypesMap = registerExperimentalMap(SubtypesMap(SUBTYPES.storageFile)) private val supertypesMap = registerExperimentalMap(SupertypesMap(SUPERTYPES.storageFile)) private val classFqNameToSourceMap = registerExperimentalMap(ClassFqNameToSourceMap(CLASS_FQ_NAME_TO_SOURCE.storageFile)) @@ -161,7 +161,7 @@ open class IncrementalCacheImpl( protoMap.process(kotlinClass, isPackage = true) + constantsMap.process(kotlinClass) + - additionalProcessChangedClass(kotlinClass, isPackage = true) + inlineFunctionsMap.process(kotlinClass, isPackage = true) } KotlinClassHeader.Kind.MULTIFILE_CLASS -> { val partNames = kotlinClass.classHeader.data?.toList() @@ -176,7 +176,7 @@ open class IncrementalCacheImpl( // TODO NO_CHANGES? (delegates only) constantsMap.process(kotlinClass) + - additionalProcessChangedClass(kotlinClass, isPackage = true) + inlineFunctionsMap.process(kotlinClass, isPackage = true) } KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> { assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" } @@ -185,7 +185,7 @@ open class IncrementalCacheImpl( protoMap.process(kotlinClass, isPackage = true) + constantsMap.process(kotlinClass) + - additionalProcessChangedClass(kotlinClass, isPackage = true) + inlineFunctionsMap.process(kotlinClass, isPackage = true) } KotlinClassHeader.Kind.CLASS -> { assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" } @@ -193,7 +193,7 @@ open class IncrementalCacheImpl( protoMap.process(kotlinClass, isPackage = false) + constantsMap.process(kotlinClass) + - additionalProcessChangedClass(kotlinClass, isPackage = false) + inlineFunctionsMap.process(kotlinClass, isPackage = false) } else -> CompilationResult.NO_CHANGES } @@ -202,8 +202,6 @@ open class IncrementalCacheImpl( return changesInfo } - protected open fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) = CompilationResult.NO_CHANGES - private fun CompilationResult.logIfSomethingChanged(className: JvmClassName) { if (this == CompilationResult.NO_CHANGES) return @@ -293,19 +291,15 @@ open class IncrementalCacheImpl( multifileFacadeToParts.remove(it) partToMultifileFacade.remove(it) constantsMap.remove(it) + inlineFunctionsMap.remove(it) } - additionalProcessRemovedClasses(dirtyClasses) - removeAllFromClassStorage(dirtyClasses) dirtyOutputClassesMap.clean() return changesInfo } - protected open fun additionalProcessRemovedClasses(dirtyClasses: List) { - } - override fun getObsoletePackageParts(): Collection { val obsoletePackageParts = dirtyOutputClassesMap.getDirtyOutputClasses().filter { packagePartMap.isPackagePart(JvmClassName.byInternalName(it)) } @@ -606,6 +600,91 @@ open class IncrementalCacheImpl( override fun dumpValue(value: Boolean) = "" } + + private inner class InlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringToLongMapExternalizer) { + private fun getInlineFunctionsMap(bytes: ByteArray): Map { + val result = HashMap() + + val inlineFunctions = inlineFunctionsJvmNames(bytes) + if (inlineFunctions.isEmpty()) return emptyMap() + + ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { + override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { + val dummyClassWriter = ClassWriter(Opcodes.ASM5) + + return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) { + override fun visitEnd() { + val jvmName = name + desc + if (jvmName !in inlineFunctions) return + + val dummyBytes = dummyClassWriter.toByteArray()!! + val hash = dummyBytes.md5() + result[jvmName] = hash + } + } + } + + }, 0) + + return result + } + + fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult { + return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.fileContents), isPackage) + } + + private fun put(className: JvmClassName, newMap: Map, isPackage: Boolean): CompilationResult { + val internalName = className.internalName + val oldMap = storage[internalName] ?: emptyMap() + + val added = hashSetOf() + val changed = hashSetOf() + val allFunctions = oldMap.keys + newMap.keys + + for (fn in allFunctions) { + val oldHash = oldMap[fn] + val newHash = newMap[fn] + + when { + oldHash == null -> added.add(fn) + oldHash != newHash -> changed.add(fn) + } + } + + when { + newMap.isNotEmpty() -> storage[internalName] = newMap + else -> storage.remove(internalName) + } + + val changes = + if (IncrementalCompilation.isExperimental()) { + val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars + // TODO get name in better way instead of using substringBefore + (added.asSequence() + changed.asSequence()).map { ChangeInfo.MembersChanged(fqName, listOf(it.substringBefore("("))) } + } + else { + emptySequence() + } + + processChangedInlineFunctions(className, changed) + return CompilationResult(inlineChanged = changed.isNotEmpty(), + inlineAdded = added.isNotEmpty(), + changes = changes) + } + + fun remove(className: JvmClassName) { + storage.remove(className.internalName) + } + + override fun dumpValue(value: Map): String = + value.dumpMap { java.lang.Long.toHexString(it) } + } + + protected open fun processChangedInlineFunctions( + className: JvmClassName, + changedFunctions: Collection + ) { + } } sealed class ChangeInfo(val fqName: FqName) { diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt index 4616450bfb1..2a65d8916df 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/JpsIncrementalCacheImpl.kt @@ -24,27 +24,23 @@ import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.storage.BuildDataManager import org.jetbrains.jps.incremental.storage.StorageOwner import org.jetbrains.kotlin.config.IncrementalCompilation -import org.jetbrains.kotlin.incremental.* +import org.jetbrains.kotlin.incremental.IncrementalCacheImpl +import org.jetbrains.kotlin.incremental.dumpCollection import org.jetbrains.kotlin.incremental.storage.BasicMap import org.jetbrains.kotlin.incremental.storage.BasicStringMap import org.jetbrains.kotlin.incremental.storage.StringCollectionExternalizer -import org.jetbrains.kotlin.incremental.storage.StringToLongMapExternalizer -import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.incremental.storages.PathCollectionExternalizer import org.jetbrains.kotlin.jps.incremental.storages.PathFunctionPair import org.jetbrains.kotlin.jps.incremental.storages.PathFunctionPairKeyDescriptor import org.jetbrains.kotlin.resolve.jvm.JvmClassName -import org.jetbrains.org.objectweb.asm.* import java.io.File -import java.util.* class JpsIncrementalCacheImpl( target: ModuleBuildTarget, paths: BuildDataPaths ) : IncrementalCacheImpl(paths.getTargetDataRoot(target), target.outputDir, target), StorageOwner { - private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile)) private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile)) private val inlinedTo = registerMap(InlineFunctionsFilesMap(INLINED_TO.storageFile)) @@ -58,13 +54,6 @@ class JpsIncrementalCacheImpl( KotlinBuilder.LOG.debug(message) } - override fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) = - inlineFunctionsMap.process(kotlinClass, isPackage) - - override fun additionalProcessRemovedClasses(dirtyClasses: List) { - dirtyClasses.forEach { inlineFunctionsMap.remove(it) } - } - fun getFilesToReinline(): Collection { val result = THashSet(FileUtil.PATH_HASHING_STRATEGY) @@ -84,86 +73,10 @@ class JpsIncrementalCacheImpl( dirtyInlineFunctionsMap.clean() } - private inner class InlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringToLongMapExternalizer) { - private fun getInlineFunctionsMap(bytes: ByteArray): Map { - val result = HashMap() - - val inlineFunctions = inlineFunctionsJvmNames(bytes) - if (inlineFunctions.isEmpty()) return emptyMap() - - ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { - override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { - val dummyClassWriter = ClassWriter(Opcodes.ASM5) - - return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) { - override fun visitEnd() { - val jvmName = name + desc - if (jvmName !in inlineFunctions) return - - val dummyBytes = dummyClassWriter.toByteArray()!! - val hash = dummyBytes.md5() - result[jvmName] = hash - } - } - } - - }, 0) - - return result + override fun processChangedInlineFunctions(className: JvmClassName, changedFunctions: Collection) { + if (changedFunctions.isNotEmpty()) { + dirtyInlineFunctionsMap.put(className, changedFunctions.toList()) } - - fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult { - return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.fileContents), isPackage) - } - - private fun put(className: JvmClassName, newMap: Map, isPackage: Boolean): CompilationResult { - val internalName = className.internalName - val oldMap = storage[internalName] ?: emptyMap() - - val added = hashSetOf() - val changed = hashSetOf() - val allFunctions = oldMap.keys + newMap.keys - - for (fn in allFunctions) { - val oldHash = oldMap[fn] - val newHash = newMap[fn] - - when { - oldHash == null -> added.add(fn) - oldHash != newHash -> changed.add(fn) - } - } - - when { - newMap.isNotEmpty() -> storage[internalName] = newMap - else -> storage.remove(internalName) - } - - if (changed.isNotEmpty()) { - dirtyInlineFunctionsMap.put(className, changed.toList()) - } - - val changes = - if (IncrementalCompilation.isExperimental()) { - val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars - // TODO get name in better way instead of using substringBefore - (added.asSequence() + changed.asSequence()).map { ChangeInfo.MembersChanged(fqName, listOf(it.substringBefore("("))) } - } - else { - emptySequence() - } - - return CompilationResult(inlineChanged = changed.isNotEmpty(), - inlineAdded = added.isNotEmpty(), - changes = changes) - } - - fun remove(className: JvmClassName) { - storage.remove(className.internalName) - } - - override fun dumpValue(value: Map): String = - value.dumpMap { java.lang.Long.toHexString(it) } } private inner class DirtyInlineFunctionsMap(storageFile: File) : BasicStringMap>(storageFile, StringCollectionExternalizer) { @@ -204,7 +117,6 @@ class JpsIncrementalCacheImpl( } companion object { - private val INLINE_FUNCTIONS = "inline-functions" private val DIRTY_INLINE_FUNCTIONS = "dirty-inline-functions" private val INLINED_TO = "inlined-to" }