diff --git a/jps/jps-plugin/jps-plugin.iml b/jps/jps-plugin/jps-plugin.iml
index 69920e17ed0..09a0ac0fec2 100644
--- a/jps/jps-plugin/jps-plugin.iml
+++ b/jps/jps-plugin/jps-plugin.iml
@@ -10,6 +10,7 @@
+
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
index edcfde1f205..a0c2c725afa 100644
--- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
+++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
@@ -39,6 +39,9 @@ import org.jetbrains.jps.model.ex.JpsElementChildRoleBase
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
+import org.jetbrains.kotlin.build.GeneratedFile
+import org.jetbrains.kotlin.build.GeneratedJvmClass
+import org.jetbrains.kotlin.build.isModuleMappingFile
import org.jetbrains.kotlin.cli.common.KotlinVersion
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
@@ -54,10 +57,10 @@ import org.jetbrains.kotlin.config.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
+import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.incremental.*
-import org.jetbrains.kotlin.load.kotlin.ModuleMapping
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.modules.TargetId
@@ -67,7 +70,6 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.JsLibraryUtils
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.keysToMap
-import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.ClassReader
import java.io.File
import java.util.*
@@ -249,7 +251,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
return OK
}
- val generatedClasses = generatedFiles.filterIsInstance()
+ val generatedClasses = generatedFiles.filterIsInstance>()
updateJavaMappings(chunk, compilationErrors, context, dirtyFilesHolder, filesToCompile, generatedClasses)
if (!IncrementalCompilation.isEnabled()) {
@@ -301,7 +303,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
rebuildAfterCacheVersionChanged[target] = true
}
- dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider).clean()
+ dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider).clean()
return
}
CacheVersion.Action.REBUILD_CHUNK -> {
@@ -333,7 +335,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
}
CacheVersion.Action.CLEAN_DATA_CONTAINER -> {
LOG.info("Clearing lookup cache")
- dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider).clean()
+ dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider).clean()
cacheVersionsProvider.dataContainerVersion().clean()
}
else -> {
@@ -353,7 +355,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
private fun doCompileModuleChunk(
allCompiledFiles: MutableSet, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder, environment: CompilerEnvironment,
- filesToCompile: MultiMap, incrementalCaches: Map,
+ filesToCompile: MultiMap, incrementalCaches: Map>,
messageCollector: MessageCollectorAdapter, project: JpsProject
): OutputItemsCollectorImpl? {
@@ -398,7 +400,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
context: CompileContext
): CompilerEnvironment {
val compilerServices = Services.Builder()
- .register(IncrementalCompilationComponents::class.java, IncrementalCompilationComponentsImpl(incrementalCaches, lookupTracker))
+ .register(IncrementalCompilationComponents::class.java,
+ IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { TargetId(it.key) }, lookupTracker))
.register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus {
override fun checkCanceled() {
if (context.cancelStatus.isCanceled) throw CompilationCanceledException()
@@ -424,7 +427,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
private fun getGeneratedFiles(
chunk: ModuleChunk,
outputItemCollector: OutputItemsCollectorImpl
- ): List {
+ ): List> {
// If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
val sourceToTarget = HashMap()
if (chunk.targets.size > 1) {
@@ -435,7 +438,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
}
}
- val result = ArrayList()
+ val result = ArrayList>()
val representativeTarget = chunk.representativeTarget()
for (outputItem in outputItemCollector.outputs) {
@@ -450,7 +453,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
result.add(GeneratedJvmClass(target, sourceFiles, outputFile))
}
else {
- result.add(GeneratedFile(target, sourceFiles, outputFile))
+ result.add(GeneratedFile(target, sourceFiles, outputFile))
}
}
return result
@@ -462,7 +465,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder,
filesToCompile: MultiMap,
- generatedClasses: List
+ generatedClasses: List>
) {
val previousMappings = context.projectDescriptor.dataManager.mappings
val delta = previousMappings.createDelta()
@@ -481,7 +484,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, allCompiled, compiledInThisRound)
}
- private fun registerOutputItems(outputConsumer: ModuleLevelBuilder.OutputConsumer, generatedFiles: List) {
+ private fun registerOutputItems(outputConsumer: ModuleLevelBuilder.OutputConsumer, generatedFiles: List>) {
for (generatedFile in generatedFiles) {
outputConsumer.registerOutputFile(generatedFile.target, generatedFile.outputFile, generatedFile.sourceFiles.map { it.path })
}
@@ -489,8 +492,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
private fun updateKotlinIncrementalCache(
compilationErrors: Boolean,
- incrementalCaches: Map,
- generatedFiles: List
+ incrementalCaches: Map,
+ generatedFiles: List>
): CompilationResult {
assert(IncrementalCompilation.isEnabled()) { "updateKotlinIncrementalCache should not be called when incremental compilation disabled" }
@@ -499,7 +502,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
for (generatedFile in generatedFiles) {
val ic = incrementalCaches[generatedFile.target]!!
val newChangesInfo =
- if (generatedFile is GeneratedJvmClass) {
+ if (generatedFile is GeneratedJvmClass) {
ic.saveFileToCache(generatedFile)
}
else if (generatedFile.outputFile.isModuleMappingFile()) {
@@ -533,7 +536,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
if (lookupTracker !is LookupTrackerImpl) throw AssertionError("Lookup tracker is expected to be LookupTrackerImpl, got ${lookupTracker.javaClass}")
- val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider)
+ val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider)
filesToCompile.values().forEach { lookupStorage.removeLookupsFrom(it) }
val removedFiles = chunk.targets.flatMap { KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, it) }
@@ -542,8 +545,6 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
lookupTracker.lookups.entrySet().forEach { lookupStorage.add(it.key, it.value) }
}
- private fun File.isModuleMappingFile() = extension == ModuleMapping.MAPPING_FILE_EXT && parentFile.name == "META-INF"
-
// if null is returned, nothing was done
private fun compileToJs(chunk: ModuleChunk,
commonArguments: CommonCompilerArguments,
@@ -740,12 +741,11 @@ private fun CompilationResult.doProcessChangesUsingLookups(
compiledFiles: Set,
dataManager: BuildDataManager,
fsOperations: FSOperationsHelper,
- caches: Collection
+ caches: Collection>
) {
val dirtyLookupSymbols = HashSet()
- val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider)
- val allCaches = caches.toHashSet()
- allCaches.addAll(caches.flatMap { it.dependentCaches })
+ val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider)
+ val allCaches: Sequence> = caches.asSequence().flatMap { it.dependentsWithThis }
KotlinBuilder.LOG.debug("Start processing changes")
@@ -797,7 +797,7 @@ private fun CompilationResult.doProcessChangesUsingLookups(
*/
private fun withSubtypes(
typeFqName: FqName,
- caches: Collection
+ caches: Sequence>
): Set {
val types = linkedListOf(typeFqName)
val subtypes = hashSetOf()
@@ -805,10 +805,9 @@ private fun withSubtypes(
while (types.isNotEmpty()) {
val unprocessedType = types.pollFirst()
- caches.asSequence()
- .flatMap { it.getSubtypesOf(unprocessedType) }
- .filter { it !in subtypes }
- .forEach { types.addLast(it) }
+ caches.flatMap { it.getSubtypesOf(unprocessedType) }
+ .filter { it !in subtypes }
+ .forEach { types.addLast(it) }
subtypes.add(unprocessedType)
}
@@ -915,22 +914,6 @@ private fun hasKotlinDirtyOrRemovedFiles(
return chunk.targets.any { KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, it).isNotEmpty() }
}
-open class GeneratedFile internal constructor(
- val target: ModuleBuildTarget,
- val sourceFiles: Collection,
- val outputFile: File
-)
-
-class GeneratedJvmClass (
- target: ModuleBuildTarget,
- sourceFiles: Collection,
- outputFile: File
-) : GeneratedFile(target, sourceFiles, outputFile) {
- val outputClass = LocalFileKotlinClass.create(outputFile).sure {
- "Couldn't load KotlinClass from $outputFile; it may happen because class doesn't have valid Kotlin annotations"
- }
-}
-
private inline fun Iterable.forAllPairs(other: Iterable, fn: (T, R)->Unit) {
for (t in this) {
for (r in other) {
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/MarkerFile.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/MarkerFile.kt
index 649bbe39177..7f0e8231008 100644
--- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/MarkerFile.kt
+++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/MarkerFile.kt
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.storage.BuildDataManager
-import org.jetbrains.kotlin.jps.incremental.KOTLIN_CACHE_DIRECTORY_NAME
+import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
import java.io.File
private val HAS_KOTLIN_MARKER_FILE_NAME = "has-kotlin-marker.txt"
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersion.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersion.kt
deleted file mode 100644
index a2abb79eceb..00000000000
--- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersion.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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
-
-import org.jetbrains.annotations.TestOnly
-import org.jetbrains.jps.builders.BuildTarget
-import org.jetbrains.jps.builders.storage.BuildDataPaths
-import org.jetbrains.jps.incremental.ModuleBuildTarget
-import org.jetbrains.kotlin.config.IncrementalCompilation
-import org.jetbrains.kotlin.jps.incremental.CacheVersion.Action
-import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
-import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
-import java.io.File
-
-private val NORMAL_VERSION = 8
-private val EXPERIMENTAL_VERSION = 2
-private val DATA_CONTAINER_VERSION = 1
-
-private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
-private val EXPERIMENTAL_VERSION_FILE_NAME = "experimental-format-version.txt"
-private val DATA_CONTAINER_VERSION_FILE_NAME = "data-container-format-version.txt"
-
-class CacheVersion(
- private val ownVersion: Int,
- private val versionFile: File,
- private val whenVersionChanged: CacheVersion.Action,
- private val whenTurnedOn: CacheVersion.Action,
- private val whenTurnedOff: CacheVersion.Action,
- isEnabled: ()->Boolean
-) {
- private val isEnabled by lazy(isEnabled)
-
- private val actualVersion: Int
- get() = versionFile.readText().toInt()
-
- private val expectedVersion: Int
- get() {
- val metadata = JvmMetadataVersion.INSTANCE
- val bytecode = JvmBytecodeBinaryVersion.INSTANCE
- return ownVersion * 1000000 +
- bytecode.major * 10000 + bytecode.minor * 100 +
- metadata.major * 1000 + metadata.minor
- }
-
- fun checkVersion(): Action =
- when (versionFile.exists() to isEnabled) {
- true to true -> if (actualVersion != expectedVersion) whenVersionChanged else Action.DO_NOTHING
- false to true -> whenTurnedOn
- true to false -> whenTurnedOff
- else -> Action.DO_NOTHING
- }
-
- fun saveIfNeeded() {
- if (!isEnabled) return
-
- if (!versionFile.parentFile.exists()) {
- versionFile.parentFile.mkdirs()
- }
-
- versionFile.writeText(expectedVersion.toString())
- }
-
- fun clean() {
- versionFile.delete()
- }
-
- @get:TestOnly
- val formatVersionFile: File
- get() = versionFile
-
- // Order of entries is important, because actions are sorted in KotlinBuilder::checkVersions
- enum class Action {
- REBUILD_ALL_KOTLIN,
- REBUILD_CHUNK,
- CLEAN_NORMAL_CACHES,
- CLEAN_EXPERIMENTAL_CACHES,
- CLEAN_DATA_CONTAINER,
- DO_NOTHING
- }
-}
-
-class CacheVersionProvider(private val paths: BuildDataPaths) {
- private val BuildTarget<*>.dataRoot: File
- get() = paths.getTargetDataRoot(this)
-
- fun normalVersion(target: ModuleBuildTarget): CacheVersion =
- CacheVersion(ownVersion = NORMAL_VERSION,
- versionFile = File(target.dataRoot, NORMAL_VERSION_FILE_NAME),
- whenVersionChanged = Action.REBUILD_CHUNK,
- whenTurnedOn = Action.REBUILD_CHUNK,
- whenTurnedOff = Action.CLEAN_NORMAL_CACHES,
- isEnabled = { IncrementalCompilation.isEnabled() })
-
- fun experimentalVersion(target: ModuleBuildTarget): CacheVersion =
- CacheVersion(ownVersion = EXPERIMENTAL_VERSION,
- versionFile = File(target.dataRoot, EXPERIMENTAL_VERSION_FILE_NAME),
- whenVersionChanged = Action.REBUILD_CHUNK,
- whenTurnedOn = Action.REBUILD_CHUNK,
- whenTurnedOff = Action.CLEAN_EXPERIMENTAL_CACHES,
- isEnabled = { IncrementalCompilation.isExperimental() })
-
- fun dataContainerVersion(): CacheVersion =
- CacheVersion(ownVersion = DATA_CONTAINER_VERSION,
- versionFile = File(KotlinDataContainerTarget.dataRoot, DATA_CONTAINER_VERSION_FILE_NAME),
- whenVersionChanged = Action.REBUILD_ALL_KOTLIN,
- whenTurnedOn = Action.REBUILD_ALL_KOTLIN,
- whenTurnedOff = Action.CLEAN_DATA_CONTAINER,
- isEnabled = { IncrementalCompilation.isExperimental() })
-
- fun allVersions(targets: Iterable): Iterable {
- val versions = arrayListOf()
- versions.add(dataContainerVersion())
-
- for (target in targets) {
- versions.add(normalVersion(target))
- versions.add(experimentalVersion(target))
- }
-
- return versions
- }
-}
\ No newline at end of file
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersionProvider.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersionProvider.kt
new file mode 100644
index 00000000000..81652efe9aa
--- /dev/null
+++ b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/CacheVersionProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * 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
+
+import org.jetbrains.jps.builders.BuildTarget
+import org.jetbrains.jps.builders.storage.BuildDataPaths
+import org.jetbrains.jps.incremental.ModuleBuildTarget
+import org.jetbrains.kotlin.incremental.*
+import java.io.File
+
+
+class CacheVersionProvider(private val paths: BuildDataPaths) {
+ private val BuildTarget<*>.dataRoot: File
+ get() = paths.getTargetDataRoot(this)
+
+ fun normalVersion(target: ModuleBuildTarget): CacheVersion = normalCacheVersion(target.dataRoot)
+
+ fun experimentalVersion(target: ModuleBuildTarget): CacheVersion = experimentalCacheVersion(target.dataRoot)
+
+ fun dataContainerVersion(): CacheVersion = dataContainerCacheVersion(KotlinDataContainerTarget.dataRoot)
+
+ fun allVersions(targets: Iterable): Iterable = allCachesVersions(KotlinDataContainerTarget.dataRoot, targets.map { it.dataRoot } )
+}
\ No newline at end of file
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt
deleted file mode 100644
index 5b4157eab37..00000000000
--- a/jps/jps-plugin/src/org/jetbrains/kotlin/jps/incremental/IncrementalCacheImpl.kt
+++ /dev/null
@@ -1,652 +0,0 @@
-/*
- * 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
-
-import com.google.protobuf.MessageLite
-import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
-import com.intellij.util.SmartList
-import com.intellij.util.io.BooleanDataDescriptor
-import com.intellij.util.io.EnumeratorStringDescriptor
-import org.jetbrains.annotations.TestOnly
-import org.jetbrains.jps.builders.storage.BuildDataPaths
-import org.jetbrains.jps.incremental.ModuleBuildTarget
-import org.jetbrains.jps.incremental.storage.PathStringDescriptor
-import org.jetbrains.kotlin.config.IncrementalCompilation
-import org.jetbrains.kotlin.jps.build.GeneratedJvmClass
-import org.jetbrains.kotlin.jps.build.KotlinBuilder
-import org.jetbrains.kotlin.jps.incremental.storage.*
-import org.jetbrains.kotlin.load.kotlin.ModuleMapping
-import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
-import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
-import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.resolve.jvm.JvmClassName
-import org.jetbrains.kotlin.serialization.ProtoBuf
-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.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 java.io.File
-import java.security.MessageDigest
-import java.util.*
-
-val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin"
-
-open class IncrementalCacheImpl(
- private val target: ModuleBuildTarget,
- paths: BuildDataPaths
-) : BasicMapsOwner(), IncrementalCache {
- companion object {
- private val PROTO_MAP = "proto"
- private val CONSTANTS_MAP = "constants"
- private val PACKAGE_PARTS = "package-parts"
- private val MULTIFILE_CLASS_FACADES = "multifile-class-facades"
- 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 SUBTYPES = "subtypes"
- private val SUPERTYPES = "supertypes"
-
- private val MODULE_MAPPING_FILE_NAME = "." + ModuleMapping.MAPPING_FILE_EXT
- }
-
- private val baseDir = File(paths.getTargetDataRoot(target), KOTLIN_CACHE_DIRECTORY_NAME)
- private val cacheVersionProvider = CacheVersionProvider(paths)
- private val experimentalMaps = arrayListOf>()
-
- private fun > registerExperimentalMap(map: M): M {
- experimentalMaps.add(map)
- return registerMap(map)
- }
-
- protected val String.storageFile: File
- get() = File(baseDir, this + "." + CACHE_EXTENSION)
-
- private val protoMap = registerMap(ProtoMap(PROTO_MAP.storageFile))
- private val constantsMap = registerMap(ConstantsMap(CONSTANTS_MAP.storageFile))
- private val packagePartMap = registerMap(PackagePartMap(PACKAGE_PARTS.storageFile))
- private val multifileFacadeToParts = registerMap(MultifileClassFacadeMap(MULTIFILE_CLASS_FACADES.storageFile))
- 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 subtypesMap = registerExperimentalMap(SubtypesMap(SUBTYPES.storageFile))
- private val supertypesMap = registerExperimentalMap(SupertypesMap(SUPERTYPES.storageFile))
-
- private val dependents = arrayListOf()
- private val outputDir by lazy(LazyThreadSafetyMode.NONE) { requireNotNull(target.outputDir) { "Target is expected to have output directory: $target" } }
-
- protected val dependentsWithThis: Sequence
- get() = sequenceOf(this).plus(dependents.asSequence())
-
- internal val dependentCaches: Iterable
- get() = dependents
-
- override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) {
- }
-
- fun addDependentCache(cache: IncrementalCacheImpl) {
- dependents.add(cache)
- }
-
- fun markOutputClassesDirty(removedAndCompiledSources: List) {
- for (sourceFile in removedAndCompiledSources) {
- val classes = sourceToClassesMap[sourceFile]
- classes.forEach {
- dirtyOutputClassesMap.markDirty(it.internalName)
- }
-
- sourceToClassesMap.clearOutputsForSource(sourceFile)
- }
- }
-
- fun getSubtypesOf(className: FqName): Sequence =
- subtypesMap[className].asSequence()
-
- override fun getClassFilePath(internalClassName: String): String {
- return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath)
- }
-
- fun saveModuleMappingToCache(sourceFiles: Collection, file: File): CompilationResult {
- val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
- protoMap.process(jvmClassName, file.readBytes(), emptyArray(), isPackage = false, checkChangesIsOpenPart = false)
- dirtyOutputClassesMap.notDirty(MODULE_MAPPING_FILE_NAME)
- sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) }
- return CompilationResult.NO_CHANGES
- }
-
- fun saveFileToCache(generatedClass: GeneratedJvmClass): CompilationResult {
- val sourceFiles: Collection = generatedClass.sourceFiles
- val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
- val className = kotlinClass.className
-
- dirtyOutputClassesMap.notDirty(className.internalName)
- sourceFiles.forEach {
- sourceToClassesMap.add(it, className)
- }
-
- if (kotlinClass.classId.isLocal) {
- return CompilationResult.NO_CHANGES
- }
-
- val header = kotlinClass.classHeader
- val changesInfo = when (header.kind) {
- KotlinClassHeader.Kind.FILE_FACADE -> {
- assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
- packagePartMap.addPackagePart(className)
-
- protoMap.process(kotlinClass, isPackage = true) +
- constantsMap.process(kotlinClass) +
- additionalProcessChangedClass(kotlinClass, isPackage = true)
- }
- KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
- val partNames = kotlinClass.classHeader.data?.toList()
- ?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}")
- multifileFacadeToParts[className] = partNames
- // When a class is replaced with a facade with the same name,
- // the class' proto wouldn't ever be deleted,
- // because we don't write proto for multifile facades.
- // As a workaround we can remove proto values for multifile facades.
- protoMap.remove(className)
-
- // TODO NO_CHANGES? (delegates only)
- constantsMap.process(kotlinClass) +
- additionalProcessChangedClass(kotlinClass, isPackage = true)
- }
- KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
- assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
- packagePartMap.addPackagePart(className)
- partToMultifileFacade.set(className.internalName, header.multifileClassName!!)
-
- protoMap.process(kotlinClass, isPackage = true) +
- constantsMap.process(kotlinClass) +
- additionalProcessChangedClass(kotlinClass, isPackage = true)
- }
- KotlinClassHeader.Kind.CLASS -> {
- addToClassStorage(kotlinClass)
-
- protoMap.process(kotlinClass, isPackage = false) +
- constantsMap.process(kotlinClass) +
- additionalProcessChangedClass(kotlinClass, isPackage = false)
- }
- else -> CompilationResult.NO_CHANGES
- }
-
- changesInfo.logIfSomethingChanged(className)
- 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
-
- KotlinBuilder.LOG.debug("$className is changed: $this")
- }
-
- fun clearCacheForRemovedClasses(): CompilationResult {
-
- fun T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List): Set =
- members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
-
- fun createChangeInfo(className: JvmClassName): ChangeInfo? {
- if (className.internalName == MODULE_MAPPING_FILE_NAME) return null
-
- val mapValue = protoMap.get(className) ?: return null
-
- return when {
- mapValue.isPackageFacade -> {
- val packageData = JvmProtoBufUtil.readPackageDataFrom(mapValue.bytes, mapValue.strings)
-
- val memberNames =
- packageData.packageProto.getNonPrivateNames(
- packageData.nameResolver,
- ProtoBuf.Package::getFunctionList,
- ProtoBuf.Package::getPropertyList
- )
-
- ChangeInfo.Removed(className.packageFqName, memberNames)
- }
- else -> {
- val classData = JvmProtoBufUtil.readClassDataFrom(mapValue.bytes, mapValue.strings)
-
- val memberNames =
- classData.classProto.getNonPrivateNames(
- classData.nameResolver,
- ProtoBuf.Class::getConstructorList,
- ProtoBuf.Class::getFunctionList,
- ProtoBuf.Class::getPropertyList
- ) + classData.classProto.enumEntryList.map { classData.nameResolver.getString(it.name) }
-
- ChangeInfo.Removed(className.fqNameForClassNameWithoutDollars, memberNames)
- }
- }
- }
-
- val dirtyClasses = dirtyOutputClassesMap
- .getDirtyOutputClasses()
- .map(JvmClassName::byInternalName)
- .toList()
-
- val changes =
- if (IncrementalCompilation.isExperimental())
- dirtyClasses.mapNotNull { createChangeInfo(it) }.asSequence()
- else
- emptySequence()
-
- val changesInfo = dirtyClasses.fold(CompilationResult(changes = changes)) { info, className ->
- val newInfo = CompilationResult(protoChanged = className in protoMap,
- constantsChanged = className in constantsMap)
- newInfo.logIfSomethingChanged(className)
- info + newInfo
- }
-
- val facadesWithRemovedParts = hashMapOf>()
- for (dirtyClass in dirtyClasses) {
- val facade = partToMultifileFacade.get(dirtyClass.internalName) ?: continue
- val facadeClassName = JvmClassName.byInternalName(facade)
- val removedParts = facadesWithRemovedParts.getOrPut(facadeClassName) { hashSetOf() }
- removedParts.add(dirtyClass.internalName)
- }
-
- for ((facade, removedParts) in facadesWithRemovedParts.entries) {
- val allParts = multifileFacadeToParts[facade.internalName] ?: continue
- val notRemovedParts = allParts.filter { it !in removedParts }
-
- if (notRemovedParts.isEmpty()) {
- multifileFacadeToParts.remove(facade)
- }
- else {
- multifileFacadeToParts[facade] = notRemovedParts
- }
- }
-
- dirtyClasses.forEach {
- protoMap.remove(it)
- packagePartMap.remove(it)
- multifileFacadeToParts.remove(it)
- partToMultifileFacade.remove(it)
- constantsMap.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)) }
- KotlinBuilder.LOG.debug("Obsolete package parts: ${obsoletePackageParts}")
- return obsoletePackageParts
- }
-
- override fun getPackagePartData(partInternalName: String): JvmPackagePartProto? {
- return protoMap[JvmClassName.byInternalName(partInternalName)]?.let { value ->
- JvmPackagePartProto(value.bytes, value.strings)
- }
- }
-
- override fun getObsoleteMultifileClasses(): Collection {
- val obsoleteMultifileClasses = linkedSetOf()
- for (dirtyClass in dirtyOutputClassesMap.getDirtyOutputClasses()) {
- val dirtyFacade = partToMultifileFacade.get(dirtyClass) ?: continue
- obsoleteMultifileClasses.add(dirtyFacade)
- }
- KotlinBuilder.LOG.debug("Obsolete multifile class facades: $obsoleteMultifileClasses")
- return obsoleteMultifileClasses
- }
-
- override fun getStableMultifileFacadeParts(facadeInternalName: String): Collection? {
- val partNames = multifileFacadeToParts.get(facadeInternalName) ?: return null
- return partNames.filter { !dirtyOutputClassesMap.isDirty(it) }
- }
-
- override fun getMultifileFacade(partInternalName: String): String? {
- return partToMultifileFacade.get(partInternalName)
- }
-
- override fun getModuleMappingData(): ByteArray? {
- return protoMap[JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)]?.bytes
- }
-
- override fun clean() {
- super.clean()
- cacheVersionProvider.normalVersion(target).clean()
- cacheVersionProvider.experimentalVersion(target).clean()
- }
-
- fun cleanExperimental() {
- cacheVersionProvider.experimentalVersion(target).clean()
- experimentalMaps.forEach { it.clean() }
- }
-
- private inner class ProtoMap(storageFile: File) : BasicStringMap(storageFile, ProtoMapValueExternalizer) {
-
- fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean): CompilationResult {
- val header = kotlinClass.classHeader
- val bytes = BitEncoding.decodeBytes(header.data!!)
- return put(kotlinClass.className, bytes, header.strings!!, isPackage, checkChangesIsOpenPart = true)
- }
-
- fun process(className: JvmClassName, data: ByteArray, strings: Array, isPackage: Boolean, checkChangesIsOpenPart: Boolean): CompilationResult {
- return put(className, data, strings, isPackage, checkChangesIsOpenPart)
- }
-
- private fun put(
- className: JvmClassName, bytes: ByteArray, strings: Array, isPackage: Boolean, checkChangesIsOpenPart: Boolean
- ): CompilationResult {
- val key = className.internalName
- val oldData = storage[key]
- val data = ProtoMapValue(isPackage, bytes, strings)
-
- if (oldData == null ||
- !Arrays.equals(bytes, oldData.bytes) ||
- !Arrays.equals(strings, oldData.strings) ||
- isPackage != oldData.isPackageFacade
- ) {
- storage[key] = data
- }
-
- if (oldData == null || !checkChangesIsOpenPart) return CompilationResult(protoChanged = true)
-
- val difference = difference(oldData, data)
- val fqName = if (isPackage) className.packageFqName else className.fqNameForClassNameWithoutDollars
- val changeList = SmartList()
-
- if (difference.isClassAffected) {
- changeList.add(ChangeInfo.SignatureChanged(fqName, difference.areSubclassesAffected))
- }
-
- if (difference.changedMembersNames.isNotEmpty()) {
- changeList.add(ChangeInfo.MembersChanged(fqName, difference.changedMembersNames))
- }
-
- return CompilationResult(protoChanged = changeList.isNotEmpty(), changes = changeList.asSequence())
- }
-
- operator fun contains(className: JvmClassName): Boolean =
- className.internalName in storage
-
- operator fun get(className: JvmClassName): ProtoMapValue? =
- storage[className.internalName]
-
- fun remove(className: JvmClassName) {
- storage.remove(className.internalName)
- }
-
- override fun dumpValue(value: ProtoMapValue): String {
- return (if (value.isPackageFacade) "1" else "0") + java.lang.Long.toHexString(value.bytes.md5())
- }
- }
-
- private inner class ConstantsMap(storageFile: File) : BasicStringMap