Creating shared module "build", copying lookup storage, tracker and incremental cache related files into it from jps plugin
Original commit: 5ead2edaa0
This commit is contained in:
committed by
Zalim Bashorov
parent
a2d58cca63
commit
693be92b92
@@ -10,6 +10,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="module" module-name="idea-jps-common" />
|
<orderEntry type="module" module-name="idea-jps-common" />
|
||||||
<orderEntry type="module" module-name="cli-common" />
|
<orderEntry type="module" module-name="cli-common" />
|
||||||
|
<orderEntry type="module" module-name="build" />
|
||||||
<orderEntry type="library" name="jps" level="project" />
|
<orderEntry type="library" name="jps" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="jps-test" level="project" />
|
<orderEntry type="library" scope="TEST" name="jps-test" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="idea-full" level="project" />
|
<orderEntry type="library" scope="TEST" name="idea-full" level="project" />
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ import org.jetbrains.jps.model.ex.JpsElementChildRoleBase
|
|||||||
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
|
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
|
||||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||||
import org.jetbrains.jps.model.module.JpsModule
|
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.KotlinVersion
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
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.IncrementalCompilation
|
||||||
import org.jetbrains.kotlin.config.Services
|
import org.jetbrains.kotlin.config.Services
|
||||||
import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
|
import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
|
||||||
|
import org.jetbrains.kotlin.incremental.*
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||||
import org.jetbrains.kotlin.jps.incremental.*
|
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.IncrementalCache
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||||
import org.jetbrains.kotlin.modules.TargetId
|
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.JsLibraryUtils
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
import org.jetbrains.kotlin.utils.sure
|
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -249,7 +251,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
return OK
|
return OK
|
||||||
}
|
}
|
||||||
|
|
||||||
val generatedClasses = generatedFiles.filterIsInstance<GeneratedJvmClass>()
|
val generatedClasses = generatedFiles.filterIsInstance<GeneratedJvmClass<ModuleBuildTarget>>()
|
||||||
updateJavaMappings(chunk, compilationErrors, context, dirtyFilesHolder, filesToCompile, generatedClasses)
|
updateJavaMappings(chunk, compilationErrors, context, dirtyFilesHolder, filesToCompile, generatedClasses)
|
||||||
|
|
||||||
if (!IncrementalCompilation.isEnabled()) {
|
if (!IncrementalCompilation.isEnabled()) {
|
||||||
@@ -301,7 +303,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
rebuildAfterCacheVersionChanged[target] = true
|
rebuildAfterCacheVersionChanged[target] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider).clean()
|
dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider).clean()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
CacheVersion.Action.REBUILD_CHUNK -> {
|
CacheVersion.Action.REBUILD_CHUNK -> {
|
||||||
@@ -333,7 +335,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
}
|
}
|
||||||
CacheVersion.Action.CLEAN_DATA_CONTAINER -> {
|
CacheVersion.Action.CLEAN_DATA_CONTAINER -> {
|
||||||
LOG.info("Clearing lookup cache")
|
LOG.info("Clearing lookup cache")
|
||||||
dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider).clean()
|
dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider).clean()
|
||||||
cacheVersionsProvider.dataContainerVersion().clean()
|
cacheVersionsProvider.dataContainerVersion().clean()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -353,7 +355,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
private fun doCompileModuleChunk(
|
private fun doCompileModuleChunk(
|
||||||
allCompiledFiles: MutableSet<File>, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
|
allCompiledFiles: MutableSet<File>, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
|
||||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: CompilerEnvironment,
|
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: CompilerEnvironment,
|
||||||
filesToCompile: MultiMap<ModuleBuildTarget, File>, incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl>,
|
filesToCompile: MultiMap<ModuleBuildTarget, File>, incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl<*>>,
|
||||||
messageCollector: MessageCollectorAdapter, project: JpsProject
|
messageCollector: MessageCollectorAdapter, project: JpsProject
|
||||||
): OutputItemsCollectorImpl? {
|
): OutputItemsCollectorImpl? {
|
||||||
|
|
||||||
@@ -398,7 +400,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
context: CompileContext
|
context: CompileContext
|
||||||
): CompilerEnvironment {
|
): CompilerEnvironment {
|
||||||
val compilerServices = Services.Builder()
|
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 {
|
.register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus {
|
||||||
override fun checkCanceled() {
|
override fun checkCanceled() {
|
||||||
if (context.cancelStatus.isCanceled) throw CompilationCanceledException()
|
if (context.cancelStatus.isCanceled) throw CompilationCanceledException()
|
||||||
@@ -424,7 +427,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
private fun getGeneratedFiles(
|
private fun getGeneratedFiles(
|
||||||
chunk: ModuleChunk,
|
chunk: ModuleChunk,
|
||||||
outputItemCollector: OutputItemsCollectorImpl
|
outputItemCollector: OutputItemsCollectorImpl
|
||||||
): List<GeneratedFile> {
|
): List<GeneratedFile<ModuleBuildTarget>> {
|
||||||
// If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
|
// If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
|
||||||
val sourceToTarget = HashMap<File, ModuleBuildTarget>()
|
val sourceToTarget = HashMap<File, ModuleBuildTarget>()
|
||||||
if (chunk.targets.size > 1) {
|
if (chunk.targets.size > 1) {
|
||||||
@@ -435,7 +438,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = ArrayList<GeneratedFile>()
|
val result = ArrayList<GeneratedFile<ModuleBuildTarget>>()
|
||||||
|
|
||||||
val representativeTarget = chunk.representativeTarget()
|
val representativeTarget = chunk.representativeTarget()
|
||||||
for (outputItem in outputItemCollector.outputs) {
|
for (outputItem in outputItemCollector.outputs) {
|
||||||
@@ -450,7 +453,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
result.add(GeneratedJvmClass(target, sourceFiles, outputFile))
|
result.add(GeneratedJvmClass(target, sourceFiles, outputFile))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.add(GeneratedFile(target, sourceFiles, outputFile))
|
result.add(GeneratedFile<ModuleBuildTarget>(target, sourceFiles, outputFile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -462,7 +465,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
context: CompileContext,
|
context: CompileContext,
|
||||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||||
filesToCompile: MultiMap<ModuleBuildTarget, File>,
|
filesToCompile: MultiMap<ModuleBuildTarget, File>,
|
||||||
generatedClasses: List<GeneratedJvmClass>
|
generatedClasses: List<GeneratedJvmClass<ModuleBuildTarget>>
|
||||||
) {
|
) {
|
||||||
val previousMappings = context.projectDescriptor.dataManager.mappings
|
val previousMappings = context.projectDescriptor.dataManager.mappings
|
||||||
val delta = previousMappings.createDelta()
|
val delta = previousMappings.createDelta()
|
||||||
@@ -481,7 +484,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, allCompiled, compiledInThisRound)
|
JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, allCompiled, compiledInThisRound)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerOutputItems(outputConsumer: ModuleLevelBuilder.OutputConsumer, generatedFiles: List<GeneratedFile>) {
|
private fun registerOutputItems(outputConsumer: ModuleLevelBuilder.OutputConsumer, generatedFiles: List<GeneratedFile<ModuleBuildTarget>>) {
|
||||||
for (generatedFile in generatedFiles) {
|
for (generatedFile in generatedFiles) {
|
||||||
outputConsumer.registerOutputFile(generatedFile.target, generatedFile.outputFile, generatedFile.sourceFiles.map { it.path })
|
outputConsumer.registerOutputFile(generatedFile.target, generatedFile.outputFile, generatedFile.sourceFiles.map { it.path })
|
||||||
}
|
}
|
||||||
@@ -489,8 +492,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
|||||||
|
|
||||||
private fun updateKotlinIncrementalCache(
|
private fun updateKotlinIncrementalCache(
|
||||||
compilationErrors: Boolean,
|
compilationErrors: Boolean,
|
||||||
incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl>,
|
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCacheImpl>,
|
||||||
generatedFiles: List<GeneratedFile>
|
generatedFiles: List<GeneratedFile<ModuleBuildTarget>>
|
||||||
): CompilationResult {
|
): CompilationResult {
|
||||||
|
|
||||||
assert(IncrementalCompilation.isEnabled()) { "updateKotlinIncrementalCache should not be called when incremental compilation disabled" }
|
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) {
|
for (generatedFile in generatedFiles) {
|
||||||
val ic = incrementalCaches[generatedFile.target]!!
|
val ic = incrementalCaches[generatedFile.target]!!
|
||||||
val newChangesInfo =
|
val newChangesInfo =
|
||||||
if (generatedFile is GeneratedJvmClass) {
|
if (generatedFile is GeneratedJvmClass<ModuleBuildTarget>) {
|
||||||
ic.saveFileToCache(generatedFile)
|
ic.saveFileToCache(generatedFile)
|
||||||
}
|
}
|
||||||
else if (generatedFile.outputFile.isModuleMappingFile()) {
|
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}")
|
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) }
|
filesToCompile.values().forEach { lookupStorage.removeLookupsFrom(it) }
|
||||||
val removedFiles = chunk.targets.flatMap { KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, 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) }
|
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
|
// if null is returned, nothing was done
|
||||||
private fun compileToJs(chunk: ModuleChunk,
|
private fun compileToJs(chunk: ModuleChunk,
|
||||||
commonArguments: CommonCompilerArguments,
|
commonArguments: CommonCompilerArguments,
|
||||||
@@ -740,12 +741,11 @@ private fun CompilationResult.doProcessChangesUsingLookups(
|
|||||||
compiledFiles: Set<File>,
|
compiledFiles: Set<File>,
|
||||||
dataManager: BuildDataManager,
|
dataManager: BuildDataManager,
|
||||||
fsOperations: FSOperationsHelper,
|
fsOperations: FSOperationsHelper,
|
||||||
caches: Collection<IncrementalCacheImpl>
|
caches: Collection<IncrementalCacheImpl<*>>
|
||||||
) {
|
) {
|
||||||
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider)
|
val lookupStorage = dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider)
|
||||||
val allCaches = caches.toHashSet()
|
val allCaches: Sequence<IncrementalCacheImpl<*>> = caches.asSequence().flatMap { it.dependentsWithThis }
|
||||||
allCaches.addAll(caches.flatMap { it.dependentCaches })
|
|
||||||
|
|
||||||
KotlinBuilder.LOG.debug("Start processing changes")
|
KotlinBuilder.LOG.debug("Start processing changes")
|
||||||
|
|
||||||
@@ -797,7 +797,7 @@ private fun CompilationResult.doProcessChangesUsingLookups(
|
|||||||
*/
|
*/
|
||||||
private fun withSubtypes(
|
private fun withSubtypes(
|
||||||
typeFqName: FqName,
|
typeFqName: FqName,
|
||||||
caches: Collection<IncrementalCacheImpl>
|
caches: Sequence<IncrementalCacheImpl<*>>
|
||||||
): Set<FqName> {
|
): Set<FqName> {
|
||||||
val types = linkedListOf(typeFqName)
|
val types = linkedListOf(typeFqName)
|
||||||
val subtypes = hashSetOf<FqName>()
|
val subtypes = hashSetOf<FqName>()
|
||||||
@@ -805,10 +805,9 @@ private fun withSubtypes(
|
|||||||
while (types.isNotEmpty()) {
|
while (types.isNotEmpty()) {
|
||||||
val unprocessedType = types.pollFirst()
|
val unprocessedType = types.pollFirst()
|
||||||
|
|
||||||
caches.asSequence()
|
caches.flatMap { it.getSubtypesOf(unprocessedType) }
|
||||||
.flatMap { it.getSubtypesOf(unprocessedType) }
|
.filter { it !in subtypes }
|
||||||
.filter { it !in subtypes }
|
.forEach { types.addLast(it) }
|
||||||
.forEach { types.addLast(it) }
|
|
||||||
|
|
||||||
subtypes.add(unprocessedType)
|
subtypes.add(unprocessedType)
|
||||||
}
|
}
|
||||||
@@ -915,22 +914,6 @@ private fun hasKotlinDirtyOrRemovedFiles(
|
|||||||
return chunk.targets.any { KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, it).isNotEmpty() }
|
return chunk.targets.any { KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, it).isNotEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
open class GeneratedFile internal constructor(
|
|
||||||
val target: ModuleBuildTarget,
|
|
||||||
val sourceFiles: Collection<File>,
|
|
||||||
val outputFile: File
|
|
||||||
)
|
|
||||||
|
|
||||||
class GeneratedJvmClass (
|
|
||||||
target: ModuleBuildTarget,
|
|
||||||
sourceFiles: Collection<File>,
|
|
||||||
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 <T, R> Iterable<T>.forAllPairs(other: Iterable<R>, fn: (T, R)->Unit) {
|
private inline fun <T, R> Iterable<T>.forAllPairs(other: Iterable<R>, fn: (T, R)->Unit) {
|
||||||
for (t in this) {
|
for (t in this) {
|
||||||
for (r in other) {
|
for (r in other) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.jps.build
|
|||||||
import org.jetbrains.jps.builders.storage.BuildDataPaths
|
import org.jetbrains.jps.builders.storage.BuildDataPaths
|
||||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||||
import org.jetbrains.jps.incremental.storage.BuildDataManager
|
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
|
import java.io.File
|
||||||
|
|
||||||
private val HAS_KOTLIN_MARKER_FILE_NAME = "has-kotlin-marker.txt"
|
private val HAS_KOTLIN_MARKER_FILE_NAME = "has-kotlin-marker.txt"
|
||||||
|
|||||||
@@ -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<ModuleBuildTarget>): Iterable<CacheVersion> {
|
|
||||||
val versions = arrayListOf<CacheVersion>()
|
|
||||||
versions.add(dataContainerVersion())
|
|
||||||
|
|
||||||
for (target in targets) {
|
|
||||||
versions.add(normalVersion(target))
|
|
||||||
versions.add(experimentalVersion(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<ModuleBuildTarget>): Iterable<CacheVersion> = allCachesVersions(KotlinDataContainerTarget.dataRoot, targets.map { it.dataRoot } )
|
||||||
|
}
|
||||||
@@ -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<BasicMap<*, *>>()
|
|
||||||
|
|
||||||
private fun <K, V, M : BasicMap<K, V>> 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<IncrementalCacheImpl>()
|
|
||||||
private val outputDir by lazy(LazyThreadSafetyMode.NONE) { requireNotNull(target.outputDir) { "Target is expected to have output directory: $target" } }
|
|
||||||
|
|
||||||
protected val dependentsWithThis: Sequence<IncrementalCacheImpl>
|
|
||||||
get() = sequenceOf(this).plus(dependents.asSequence())
|
|
||||||
|
|
||||||
internal val dependentCaches: Iterable<IncrementalCacheImpl>
|
|
||||||
get() = dependents
|
|
||||||
|
|
||||||
override fun registerInline(fromPath: String, jvmSignature: String, toPath: String) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addDependentCache(cache: IncrementalCacheImpl) {
|
|
||||||
dependents.add(cache)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markOutputClassesDirty(removedAndCompiledSources: List<File>) {
|
|
||||||
for (sourceFile in removedAndCompiledSources) {
|
|
||||||
val classes = sourceToClassesMap[sourceFile]
|
|
||||||
classes.forEach {
|
|
||||||
dirtyOutputClassesMap.markDirty(it.internalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceToClassesMap.clearOutputsForSource(sourceFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSubtypesOf(className: FqName): Sequence<FqName> =
|
|
||||||
subtypesMap[className].asSequence()
|
|
||||||
|
|
||||||
override fun getClassFilePath(internalClassName: String): String {
|
|
||||||
return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveModuleMappingToCache(sourceFiles: Collection<File>, file: File): CompilationResult {
|
|
||||||
val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
|
|
||||||
protoMap.process(jvmClassName, file.readBytes(), emptyArray<String>(), 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<File> = 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> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
|
|
||||||
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<ChangeInfo>()
|
|
||||||
|
|
||||||
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<JvmClassName, MutableSet<String>>()
|
|
||||||
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<JvmClassName>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getObsoletePackageParts(): Collection<String> {
|
|
||||||
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<String> {
|
|
||||||
val obsoleteMultifileClasses = linkedSetOf<String>()
|
|
||||||
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<String>? {
|
|
||||||
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<ProtoMapValue>(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<String>, isPackage: Boolean, checkChangesIsOpenPart: Boolean): CompilationResult {
|
|
||||||
return put(className, data, strings, isPackage, checkChangesIsOpenPart)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun put(
|
|
||||||
className: JvmClassName, bytes: ByteArray, strings: Array<String>, 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<ChangeInfo>()
|
|
||||||
|
|
||||||
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<Map<String, Any>>(storageFile, ConstantsMapExternalizer) {
|
|
||||||
private fun getConstantsMap(bytes: ByteArray): Map<String, Any>? {
|
|
||||||
val result = HashMap<String, Any>()
|
|
||||||
|
|
||||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
|
|
||||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
|
||||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_PRIVATE
|
|
||||||
if (value != null && access and staticFinal == Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) {
|
|
||||||
result[name] = value
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
|
||||||
|
|
||||||
return if (result.isEmpty()) null else result
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun contains(className: JvmClassName): Boolean =
|
|
||||||
className.internalName in storage
|
|
||||||
|
|
||||||
fun process(kotlinClass: LocalFileKotlinClass): CompilationResult {
|
|
||||||
return put(kotlinClass.className, getConstantsMap(kotlinClass.fileContents))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun put(className: JvmClassName, constantsMap: Map<String, Any>?): CompilationResult {
|
|
||||||
val key = className.internalName
|
|
||||||
|
|
||||||
val oldMap = storage[key]
|
|
||||||
if (oldMap == constantsMap) return CompilationResult.NO_CHANGES
|
|
||||||
|
|
||||||
if (constantsMap != null) {
|
|
||||||
storage[key] = constantsMap
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
storage.remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompilationResult(constantsChanged = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
|
||||||
put(className, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dumpValue(value: Map<String, Any>): String =
|
|
||||||
value.dumpMap(Any::toString)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class PackagePartMap(storageFile: File) : BasicStringMap<Boolean>(storageFile, BooleanDataDescriptor.INSTANCE) {
|
|
||||||
fun addPackagePart(className: JvmClassName) {
|
|
||||||
storage[className.internalName] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
|
||||||
storage.remove(className.internalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPackagePart(className: JvmClassName): Boolean =
|
|
||||||
className.internalName in storage
|
|
||||||
|
|
||||||
override fun dumpValue(value: Boolean) = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class MultifileClassFacadeMap(storageFile: File) : BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) {
|
|
||||||
operator fun set(facadeName: JvmClassName, partNames: Collection<String>) {
|
|
||||||
storage[facadeName.internalName] = partNames
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(facadeName: String): Collection<String>? = storage[facadeName]
|
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
|
||||||
storage.remove(className.internalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class MultifileClassPartMap(storageFile: File) : BasicStringMap<String>(storageFile, EnumeratorStringDescriptor.INSTANCE) {
|
|
||||||
fun set(partName: String, facadeName: String) {
|
|
||||||
storage[partName] = facadeName
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(partName: String): String? {
|
|
||||||
return storage.get(partName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(className: JvmClassName) {
|
|
||||||
storage.remove(className.internalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dumpValue(value: String): String = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class SourceToClassesMap(storageFile: File) : BasicStringMap<Collection<String>>(storageFile, PathStringDescriptor.INSTANCE, StringCollectionExternalizer) {
|
|
||||||
fun clearOutputsForSource(sourceFile: File) {
|
|
||||||
remove(sourceFile.absolutePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(sourceFile: File, className: JvmClassName) {
|
|
||||||
storage.append(sourceFile.absolutePath, className.internalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(sourceFile: File): Collection<JvmClassName> =
|
|
||||||
storage[sourceFile.absolutePath].orEmpty().map { JvmClassName.byInternalName(it) }
|
|
||||||
|
|
||||||
override fun dumpValue(value: Collection<String>) = value.dumpCollection()
|
|
||||||
|
|
||||||
override fun clean() {
|
|
||||||
storage.keys.forEach { remove(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun remove(path: String) {
|
|
||||||
storage.remove(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addToClassStorage(kotlinClass: LocalFileKotlinClass) {
|
|
||||||
if (!IncrementalCompilation.isExperimental()) return
|
|
||||||
|
|
||||||
val classData = JvmProtoBufUtil.readClassDataFrom(kotlinClass.classHeader.data!!, 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" }
|
|
||||||
.toSet()
|
|
||||||
val child = kotlinClass.classId.asSingleFqName()
|
|
||||||
|
|
||||||
parents.forEach { subtypesMap.add(it, child) }
|
|
||||||
|
|
||||||
val removedSupertypes = supertypesMap[child].filter { it !in parents }
|
|
||||||
removedSupertypes.forEach { subtypesMap.removeValues(it, setOf(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) {
|
|
||||||
fun markDirty(className: String) {
|
|
||||||
storage[className] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notDirty(className: String) {
|
|
||||||
storage.remove(className)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDirtyOutputClasses(): Collection<String> =
|
|
||||||
storage.keys
|
|
||||||
|
|
||||||
fun isDirty(className: String): Boolean =
|
|
||||||
storage.contains(className)
|
|
||||||
|
|
||||||
override fun dumpValue(value: Boolean) = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class ChangeInfo(val fqName: FqName) {
|
|
||||||
open class MembersChanged(fqName: FqName, val names: Collection<String>) : ChangeInfo(fqName) {
|
|
||||||
override fun toStringProperties(): String = super.toStringProperties() + ", names = $names"
|
|
||||||
}
|
|
||||||
|
|
||||||
class Removed(fqName: FqName, names: Collection<String>) : MembersChanged(fqName, names)
|
|
||||||
|
|
||||||
class SignatureChanged(fqName: FqName, val areSubclassesAffected: Boolean) : ChangeInfo(fqName)
|
|
||||||
|
|
||||||
|
|
||||||
protected open fun toStringProperties(): String = "fqName = $fqName"
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return this.javaClass.simpleName + "(${toStringProperties()})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CompilationResult(
|
|
||||||
val protoChanged: Boolean = false,
|
|
||||||
val constantsChanged: Boolean = false,
|
|
||||||
val inlineChanged: Boolean = false,
|
|
||||||
val inlineAdded: Boolean = false,
|
|
||||||
val changes: Sequence<ChangeInfo> = emptySequence()
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
val NO_CHANGES: CompilationResult = CompilationResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun plus(other: CompilationResult): CompilationResult =
|
|
||||||
CompilationResult(protoChanged || other.protoChanged,
|
|
||||||
constantsChanged || other.constantsChanged,
|
|
||||||
inlineChanged || other.inlineChanged,
|
|
||||||
inlineAdded || other.inlineAdded,
|
|
||||||
changes + other.changes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ByteArray.md5(): Long {
|
|
||||||
val d = MessageDigest.getInstance("MD5").digest(this)!!
|
|
||||||
return ((d[0].toLong() and 0xFFL)
|
|
||||||
or ((d[1].toLong() and 0xFFL) shl 8)
|
|
||||||
or ((d[2].toLong() and 0xFFL) shl 16)
|
|
||||||
or ((d[3].toLong() and 0xFFL) shl 24)
|
|
||||||
or ((d[4].toLong() and 0xFFL) shl 32)
|
|
||||||
or ((d[5].toLong() and 0xFFL) shl 40)
|
|
||||||
or ((d[6].toLong() and 0xFFL) shl 48)
|
|
||||||
or ((d[7].toLong() and 0xFFL) shl 56)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly
|
|
||||||
fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V)->String): String =
|
|
||||||
buildString {
|
|
||||||
append("{")
|
|
||||||
for (key in keys.sorted()) {
|
|
||||||
if (length != 1) {
|
|
||||||
append(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
val value = get(key)?.let(dumpValue) ?: "null"
|
|
||||||
append("$key -> $value")
|
|
||||||
}
|
|
||||||
append("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
|
|
||||||
"[${sorted().joinToString(", ", transform = Any::toString)}]"
|
|
||||||
-35
@@ -1,35 +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.jps.incremental.ModuleBuildTarget
|
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
|
||||||
import org.jetbrains.kotlin.modules.TargetId
|
|
||||||
|
|
||||||
class IncrementalCompilationComponentsImpl(
|
|
||||||
caches: Map<ModuleBuildTarget, IncrementalCache>,
|
|
||||||
private val lookupTracker: LookupTracker
|
|
||||||
): IncrementalCompilationComponents {
|
|
||||||
private val caches = caches.mapKeys { TargetId(it.key) }
|
|
||||||
|
|
||||||
override fun getIncrementalCache(target: TargetId): IncrementalCache =
|
|
||||||
caches[target]!!
|
|
||||||
|
|
||||||
override fun getLookupTracker(): LookupTracker = lookupTracker
|
|
||||||
}
|
|
||||||
@@ -24,8 +24,10 @@ import org.jetbrains.jps.incremental.ModuleBuildTarget
|
|||||||
import org.jetbrains.jps.incremental.storage.BuildDataManager
|
import org.jetbrains.jps.incremental.storage.BuildDataManager
|
||||||
import org.jetbrains.jps.incremental.storage.StorageOwner
|
import org.jetbrains.jps.incremental.storage.StorageOwner
|
||||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
|
import org.jetbrains.kotlin.incremental.*
|
||||||
|
import org.jetbrains.kotlin.incremental.storage.*
|
||||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
||||||
import org.jetbrains.kotlin.jps.incremental.storage.*
|
import org.jetbrains.kotlin.jps.build.KotlinBuilder
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -34,7 +36,7 @@ import java.util.*
|
|||||||
class JpsIncrementalCacheImpl(
|
class JpsIncrementalCacheImpl(
|
||||||
target: ModuleBuildTarget,
|
target: ModuleBuildTarget,
|
||||||
paths: BuildDataPaths
|
paths: BuildDataPaths
|
||||||
) : IncrementalCacheImpl(target, paths), StorageOwner {
|
) : IncrementalCacheImpl<ModuleBuildTarget>(paths.getTargetDataRoot(target), target.outputDir, target), StorageOwner {
|
||||||
|
|
||||||
private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile))
|
private val inlineFunctionsMap = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile))
|
||||||
private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile))
|
private val dirtyInlineFunctionsMap = registerMap(DirtyInlineFunctionsMap(DIRTY_INLINE_FUNCTIONS.storageFile))
|
||||||
@@ -46,6 +48,10 @@ class JpsIncrementalCacheImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun debugLog(message: String) {
|
||||||
|
KotlinBuilder.LOG.debug(message)
|
||||||
|
}
|
||||||
|
|
||||||
override fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) =
|
override fun additionalProcessChangedClass(kotlinClass: LocalFileKotlinClass, isPackage: Boolean) =
|
||||||
inlineFunctionsMap.process(kotlinClass, isPackage)
|
inlineFunctionsMap.process(kotlinClass, isPackage)
|
||||||
|
|
||||||
@@ -173,7 +179,7 @@ class JpsIncrementalCacheImpl(
|
|||||||
* * inlineFunction - jvmSignature of some inline function in source file
|
* * inlineFunction - jvmSignature of some inline function in source file
|
||||||
* * target files - collection of files inlineFunction has been inlined to
|
* * target files - collection of files inlineFunction has been inlined to
|
||||||
*/
|
*/
|
||||||
private inner class InlineFunctionsFilesMap(storageFile: File) : BasicMap<PathFunctionPair, Collection<String>>(storageFile, PathFunctionPairKeyDescriptor, PathCollectionExternalizer) {
|
private inner class InlineFunctionsFilesMap(storageFile: File) : BasicMap<PathFunctionPair, Collection<String>>(storageFile, PathFunctionPairKeyDescriptor, PathStringCollectionExternalizer) {
|
||||||
fun add(sourcePath: String, jvmSignature: String, targetPath: String) {
|
fun add(sourcePath: String, jvmSignature: String, targetPath: String) {
|
||||||
val key = PathFunctionPair(sourcePath, jvmSignature)
|
val key = PathFunctionPair(sourcePath, jvmSignature)
|
||||||
storage.append(key, targetPath)
|
storage.append(key, targetPath)
|
||||||
|
|||||||
+8
-18
@@ -14,25 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.jps.incremental.storage
|
package org.jetbrains.kotlin.jps.incremental
|
||||||
|
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.jps.builders.storage.StorageProvider
|
||||||
|
import org.jetbrains.jps.incremental.storage.StorageOwner
|
||||||
|
import org.jetbrains.kotlin.incremental.LookupStorage
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class FileToIdMap(file: File) : BasicMap<File, Int>(file, FileKeyDescriptor, IntExternalizer) {
|
object JpsLookupStorageProvider : StorageProvider<JpsLookupStorage>() {
|
||||||
override fun dumpKey(key: File): String = key.toString()
|
override fun createStorage(targetDataDir: File): JpsLookupStorage = JpsLookupStorage(targetDataDir)
|
||||||
|
|
||||||
override fun dumpValue(value: Int): String = value.toString()
|
|
||||||
|
|
||||||
operator fun get(file: File): Int? = storage[file]
|
|
||||||
|
|
||||||
operator fun set(file: File, id: Int) {
|
|
||||||
storage[file] = id
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(file: File) {
|
|
||||||
storage.remove(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toMap(): Map<File, Int> = storage.keys.keysToMap { storage[it]!! }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JpsLookupStorage(targetDataDir: File) : StorageOwner, LookupStorage(targetDataDir)
|
||||||
@@ -1,52 +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.kotlin.load.kotlin.FileBasedKotlinClass
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class LocalFileKotlinClass private constructor(
|
|
||||||
private val file: File,
|
|
||||||
private val fileContents: ByteArray,
|
|
||||||
className: ClassId,
|
|
||||||
classHeader: KotlinClassHeader,
|
|
||||||
innerClasses: FileBasedKotlinClass.InnerClassesInfo
|
|
||||||
) : FileBasedKotlinClass(className, classHeader, innerClasses) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun create(file: File): LocalFileKotlinClass? {
|
|
||||||
val fileContents = file.readBytes()
|
|
||||||
return FileBasedKotlinClass.create(fileContents) {
|
|
||||||
className, classHeader, innerClasses ->
|
|
||||||
LocalFileKotlinClass(file, fileContents, className, classHeader, innerClasses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
|
||||||
|
|
||||||
override fun getLocation(): String = file.absolutePath
|
|
||||||
|
|
||||||
public override fun getFileContents(): ByteArray = fileContents
|
|
||||||
|
|
||||||
override fun hashCode(): Int = file.hashCode()
|
|
||||||
override fun equals(other: Any?): Boolean = other is LocalFileKotlinClass && file == other.file
|
|
||||||
override fun toString(): String = "$javaClass: $file"
|
|
||||||
}
|
|
||||||
@@ -1,194 +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.intellij.util.containers.MultiMap
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
|
||||||
import org.jetbrains.jps.builders.storage.StorageProvider
|
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
|
||||||
import org.jetbrains.kotlin.incremental.components.Position
|
|
||||||
import org.jetbrains.kotlin.incremental.components.ScopeKind
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.storage.*
|
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object LookupStorageProvider : StorageProvider<LookupStorage>() {
|
|
||||||
override fun createStorage(targetDataDir: File): LookupStorage = LookupStorage(targetDataDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LookupStorage(private val targetDataDir: File) : BasicMapsOwner() {
|
|
||||||
companion object {
|
|
||||||
private val DELETED_TO_SIZE_TRESHOLD = 0.5
|
|
||||||
private val MINIMUM_GARBAGE_COLLECTIBLE_SIZE = 10000
|
|
||||||
}
|
|
||||||
|
|
||||||
private val String.storageFile: File
|
|
||||||
get() = File(targetDataDir, this + "." + CACHE_EXTENSION)
|
|
||||||
|
|
||||||
private val countersFile = "counters".storageFile
|
|
||||||
private val idToFile = registerMap(IdToFileMap("id-to-file".storageFile))
|
|
||||||
private val fileToId = registerMap(FileToIdMap("file-to-id".storageFile))
|
|
||||||
private val lookupMap = registerMap(LookupMap("lookups".storageFile))
|
|
||||||
private var size: Int = 0
|
|
||||||
private var deletedCount: Int = 0
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (countersFile.exists()) {
|
|
||||||
val lines = countersFile.readLines()
|
|
||||||
size = lines[0].toInt()
|
|
||||||
deletedCount = lines[1].toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(lookupSymbol: LookupSymbol): Collection<String> {
|
|
||||||
val key = LookupSymbolKey(lookupSymbol.name, lookupSymbol.scope)
|
|
||||||
val fileIds = lookupMap[key] ?: return emptySet()
|
|
||||||
|
|
||||||
return fileIds.mapNotNull {
|
|
||||||
// null means it's outdated
|
|
||||||
idToFile[it]?.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(lookupSymbol: LookupSymbol, containingPaths: Collection<String>) {
|
|
||||||
val key = LookupSymbolKey(lookupSymbol.name, lookupSymbol.scope)
|
|
||||||
val fileIds = containingPaths.map { addFileIfNeeded(File(it)) }.toHashSet()
|
|
||||||
fileIds.addAll(lookupMap[key] ?: emptySet())
|
|
||||||
lookupMap[key] = fileIds
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeLookupsFrom(file: File) {
|
|
||||||
val id = fileToId[file] ?: return
|
|
||||||
idToFile.remove(id)
|
|
||||||
fileToId.remove(file)
|
|
||||||
deletedCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clean() {
|
|
||||||
if (countersFile.exists()) {
|
|
||||||
countersFile.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
size = 0
|
|
||||||
deletedCount = 0
|
|
||||||
|
|
||||||
super.clean()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush(memoryCachesOnly: Boolean) {
|
|
||||||
try {
|
|
||||||
removeGarbageIfNeeded()
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
if (!countersFile.exists()) {
|
|
||||||
countersFile.parentFile.mkdirs()
|
|
||||||
countersFile.createNewFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
countersFile.writeText("$size\n$deletedCount")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
super.flush(memoryCachesOnly)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addFileIfNeeded(file: File): Int {
|
|
||||||
val existing = fileToId[file]
|
|
||||||
if (existing != null) return existing
|
|
||||||
|
|
||||||
val id = size++
|
|
||||||
fileToId[file] = id
|
|
||||||
idToFile[id] = file
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeGarbageIfNeeded(force: Boolean = false) {
|
|
||||||
if (!force && size <= MINIMUM_GARBAGE_COLLECTIBLE_SIZE && deletedCount.toDouble() / size <= DELETED_TO_SIZE_TRESHOLD) return
|
|
||||||
|
|
||||||
for (hash in lookupMap.keys) {
|
|
||||||
lookupMap[hash] = lookupMap[hash]!!.filter { it in idToFile }.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
val oldFileToId = fileToId.toMap()
|
|
||||||
val oldIdToNewId = HashMap<Int, Int>(oldFileToId.size)
|
|
||||||
idToFile.clean()
|
|
||||||
fileToId.clean()
|
|
||||||
size = 0
|
|
||||||
deletedCount = 0
|
|
||||||
|
|
||||||
for ((file, oldId) in oldFileToId.entries) {
|
|
||||||
val newId = addFileIfNeeded(file)
|
|
||||||
oldIdToNewId[oldId] = newId
|
|
||||||
}
|
|
||||||
|
|
||||||
for (lookup in lookupMap.keys) {
|
|
||||||
val fileIds = lookupMap[lookup]!!.mapNotNull { oldIdToNewId[it] }.toSet()
|
|
||||||
|
|
||||||
if (fileIds.isEmpty()) {
|
|
||||||
lookupMap.remove(lookup)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lookupMap[lookup] = fileIds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly fun forceGC() {
|
|
||||||
removeGarbageIfNeeded(force = true)
|
|
||||||
flush(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly fun dump(lookupSymbols: Set<LookupSymbol>): String {
|
|
||||||
flush(false)
|
|
||||||
|
|
||||||
val sb = StringBuilder()
|
|
||||||
val p = Printer(sb)
|
|
||||||
val lookupsStrings = lookupSymbols.groupBy { LookupSymbolKey(it.name, it.scope) }
|
|
||||||
|
|
||||||
for (lookup in lookupMap.keys.sorted()) {
|
|
||||||
val fileIds = lookupMap[lookup]!!
|
|
||||||
|
|
||||||
val key = if (lookup in lookupsStrings) {
|
|
||||||
lookupsStrings[lookup]!!.map { "${it.scope}#${it.name}" }.sorted().joinToString(", ")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lookup.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
val value = fileIds.map { idToFile[it]?.absolutePath ?: it.toString() }.sorted().joinToString(", ")
|
|
||||||
p.println("$key -> $value")
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LookupTrackerImpl(private val delegate: LookupTracker) : LookupTracker {
|
|
||||||
val lookups = MultiMap<LookupSymbol, String>()
|
|
||||||
|
|
||||||
override val requiresPosition: Boolean
|
|
||||||
get() = delegate.requiresPosition
|
|
||||||
|
|
||||||
override fun record(filePath: String, position: Position, scopeFqName: String, scopeKind: ScopeKind, name: String) {
|
|
||||||
lookups.putValue(LookupSymbol(name, scopeFqName), filePath)
|
|
||||||
delegate.record(filePath, position, scopeFqName, scopeKind, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class LookupSymbol(val name: String, val scope: String)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,277 +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 org.jetbrains.kotlin.descriptors.Visibilities
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.ProtoCompareGenerated.ProtoBufClassKind
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.ProtoCompareGenerated.ProtoBufPackageKind
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue
|
|
||||||
import org.jetbrains.kotlin.serialization.Flags
|
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.Deserialization
|
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
|
||||||
import org.jetbrains.kotlin.utils.HashSetUtil
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class Difference(
|
|
||||||
val isClassAffected: Boolean = false,
|
|
||||||
val areSubclassesAffected: Boolean = false,
|
|
||||||
val changedMembersNames: Set<String> = emptySet()
|
|
||||||
)
|
|
||||||
|
|
||||||
fun difference(oldData: ProtoMapValue, newData: ProtoMapValue): Difference {
|
|
||||||
if (!oldData.isPackageFacade && newData.isPackageFacade) return Difference(isClassAffected = true, areSubclassesAffected = true)
|
|
||||||
|
|
||||||
if (oldData.isPackageFacade && !newData.isPackageFacade) return Difference(isClassAffected = true)
|
|
||||||
|
|
||||||
val differenceObject =
|
|
||||||
if (oldData.isPackageFacade) {
|
|
||||||
DifferenceCalculatorForPackageFacade(oldData, newData)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DifferenceCalculatorForClass(oldData, newData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return differenceObject.difference()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val MessageLite.isPrivate: Boolean
|
|
||||||
get() = Visibilities.isPrivate(Deserialization.visibility(
|
|
||||||
when (this) {
|
|
||||||
is ProtoBuf.Constructor -> Flags.VISIBILITY.get(flags)
|
|
||||||
is ProtoBuf.Function -> Flags.VISIBILITY.get(flags)
|
|
||||||
is ProtoBuf.Property -> Flags.VISIBILITY.get(flags)
|
|
||||||
else -> error("Unknown message: $this")
|
|
||||||
}))
|
|
||||||
|
|
||||||
private fun MessageLite.name(nameResolver: NameResolver): String {
|
|
||||||
return when (this) {
|
|
||||||
is ProtoBuf.Constructor -> "<init>"
|
|
||||||
is ProtoBuf.Function -> nameResolver.getString(name)
|
|
||||||
is ProtoBuf.Property -> nameResolver.getString(name)
|
|
||||||
else -> error("Unknown message: $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun List<MessageLite>.names(nameResolver: NameResolver): List<String> = map { it.name(nameResolver) }
|
|
||||||
|
|
||||||
private abstract class DifferenceCalculator() {
|
|
||||||
protected abstract val oldNameResolver: NameResolver
|
|
||||||
protected abstract val newNameResolver: NameResolver
|
|
||||||
|
|
||||||
protected val compareObject by lazy { ProtoCompareGenerated(oldNameResolver, newNameResolver) }
|
|
||||||
|
|
||||||
abstract fun difference(): Difference
|
|
||||||
|
|
||||||
protected fun calcDifferenceForMembers(oldList: List<MessageLite>, newList: List<MessageLite>): Collection<String> {
|
|
||||||
val result = hashSetOf<String>()
|
|
||||||
|
|
||||||
val oldMap =
|
|
||||||
oldList.groupBy { it.getHashCode({ compareObject.oldGetIndexOfString(it) }, { compareObject.oldGetIndexOfClassId(it) }) }
|
|
||||||
val newMap =
|
|
||||||
newList.groupBy { it.getHashCode({ compareObject.newGetIndexOfString(it) }, { compareObject.newGetIndexOfClassId(it) }) }
|
|
||||||
|
|
||||||
val hashes = oldMap.keys + newMap.keys
|
|
||||||
for (hash in hashes) {
|
|
||||||
val oldMembers = oldMap[hash]
|
|
||||||
val newMembers = newMap[hash]
|
|
||||||
|
|
||||||
val differentMembers = when {
|
|
||||||
newMembers == null -> oldMembers!!.names(compareObject.oldNameResolver)
|
|
||||||
oldMembers == null -> newMembers.names(compareObject.newNameResolver)
|
|
||||||
else -> calcDifferenceForEqualHashes(oldMembers, newMembers)
|
|
||||||
}
|
|
||||||
result.addAll(differentMembers)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calcDifferenceForEqualHashes(
|
|
||||||
oldList: List<MessageLite>,
|
|
||||||
newList: List<MessageLite>
|
|
||||||
): Collection<String> {
|
|
||||||
val result = hashSetOf<String>()
|
|
||||||
val newSet = HashSet(newList)
|
|
||||||
|
|
||||||
oldList.forEach { oldMember ->
|
|
||||||
val newMember = newSet.firstOrNull { compareObject.checkEquals(oldMember, it) }
|
|
||||||
if (newMember != null) {
|
|
||||||
newSet.remove(newMember)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result.add(oldMember.name(compareObject.oldNameResolver))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newSet.forEach { newMember ->
|
|
||||||
result.add(newMember.name(compareObject.newNameResolver))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun calcDifferenceForNames(
|
|
||||||
oldList: List<Int>,
|
|
||||||
newList: List<Int>
|
|
||||||
): Collection<String> {
|
|
||||||
val oldNames = oldList.map { compareObject.oldNameResolver.getString(it) }.toSet()
|
|
||||||
val newNames = newList.map { compareObject.newNameResolver.getString(it) }.toSet()
|
|
||||||
return HashSetUtil.symmetricDifference(oldNames, newNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MessageLite.getHashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
|
||||||
return when (this) {
|
|
||||||
is ProtoBuf.Constructor -> hashCode(stringIndexes, fqNameIndexes)
|
|
||||||
is ProtoBuf.Function -> hashCode(stringIndexes, fqNameIndexes)
|
|
||||||
is ProtoBuf.Property -> hashCode(stringIndexes, fqNameIndexes)
|
|
||||||
else -> error("Unknown message: $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ProtoCompareGenerated.checkEquals(old: MessageLite, new: MessageLite): Boolean {
|
|
||||||
return when {
|
|
||||||
old is ProtoBuf.Constructor && new is ProtoBuf.Constructor -> checkEquals(old, new)
|
|
||||||
old is ProtoBuf.Function && new is ProtoBuf.Function -> checkEquals(old, new)
|
|
||||||
old is ProtoBuf.Property && new is ProtoBuf.Property -> checkEquals(old, new)
|
|
||||||
else -> error("Unknown message: $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DifferenceCalculatorForClass(oldData: ProtoMapValue, newData: ProtoMapValue) : DifferenceCalculator() {
|
|
||||||
companion object {
|
|
||||||
private val CLASS_SIGNATURE_ENUMS = EnumSet.of(
|
|
||||||
ProtoBufClassKind.FLAGS,
|
|
||||||
ProtoBufClassKind.FQ_NAME,
|
|
||||||
ProtoBufClassKind.TYPE_PARAMETER_LIST,
|
|
||||||
ProtoBufClassKind.SUPERTYPE_LIST
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val oldClassData = JvmProtoBufUtil.readClassDataFrom(oldData.bytes, oldData.strings)
|
|
||||||
val newClassData = JvmProtoBufUtil.readClassDataFrom(newData.bytes, newData.strings)
|
|
||||||
|
|
||||||
val oldProto = oldClassData.classProto
|
|
||||||
val newProto = newClassData.classProto
|
|
||||||
|
|
||||||
override val oldNameResolver = oldClassData.nameResolver
|
|
||||||
override val newNameResolver = newClassData.nameResolver
|
|
||||||
|
|
||||||
val diff = compareObject.difference(oldProto, newProto)
|
|
||||||
|
|
||||||
override fun difference(): Difference {
|
|
||||||
var isClassAffected = false
|
|
||||||
var areSubclassesAffected = false
|
|
||||||
val names = hashSetOf<String>()
|
|
||||||
val classIsSealed = newProto.isSealed && oldProto.isSealed
|
|
||||||
|
|
||||||
fun Int.oldToNames() = names.add(oldNameResolver.getString(this))
|
|
||||||
fun Int.newToNames() = names.add(newNameResolver.getString(this))
|
|
||||||
|
|
||||||
fun calcDifferenceForNonPrivateMembers(members: (ProtoBuf.Class) -> List<MessageLite>): Collection<String> {
|
|
||||||
val oldMembers = members(oldProto).filterNot { it.isPrivate }
|
|
||||||
val newMembers = members(newProto).filterNot { it.isPrivate }
|
|
||||||
return calcDifferenceForMembers(oldMembers, newMembers)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (kind in diff) {
|
|
||||||
when (kind!!) {
|
|
||||||
ProtoBufClassKind.COMPANION_OBJECT_NAME -> {
|
|
||||||
if (oldProto.hasCompanionObjectName()) oldProto.companionObjectName.oldToNames()
|
|
||||||
if (newProto.hasCompanionObjectName()) newProto.companionObjectName.newToNames()
|
|
||||||
}
|
|
||||||
ProtoBufClassKind.NESTED_CLASS_NAME_LIST -> {
|
|
||||||
if (classIsSealed) {
|
|
||||||
// when class is sealed, adding an implementation can break exhaustive when expressions
|
|
||||||
// the workaround is to recompile all class usages
|
|
||||||
isClassAffected = true
|
|
||||||
}
|
|
||||||
|
|
||||||
names.addAll(calcDifferenceForNames(oldProto.nestedClassNameList, newProto.nestedClassNameList))
|
|
||||||
}
|
|
||||||
ProtoBufClassKind.CONSTRUCTOR_LIST -> {
|
|
||||||
val differentNonPrivateConstructors = calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getConstructorList)
|
|
||||||
|
|
||||||
if (differentNonPrivateConstructors.isNotEmpty()) {
|
|
||||||
isClassAffected = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProtoBufClassKind.FUNCTION_LIST ->
|
|
||||||
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getFunctionList))
|
|
||||||
ProtoBufClassKind.PROPERTY_LIST ->
|
|
||||||
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Class::getPropertyList))
|
|
||||||
ProtoBufClassKind.ENUM_ENTRY_LIST -> {
|
|
||||||
isClassAffected = true
|
|
||||||
}
|
|
||||||
ProtoBufClassKind.TYPE_TABLE -> {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
in CLASS_SIGNATURE_ENUMS -> {
|
|
||||||
isClassAffected = true
|
|
||||||
areSubclassesAffected = true
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Unsupported kind: $kind")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Difference(isClassAffected, areSubclassesAffected, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DifferenceCalculatorForPackageFacade(oldData: ProtoMapValue, newData: ProtoMapValue) : DifferenceCalculator() {
|
|
||||||
val oldPackageData = JvmProtoBufUtil.readPackageDataFrom(oldData.bytes, oldData.strings)
|
|
||||||
val newPackageData = JvmProtoBufUtil.readPackageDataFrom(newData.bytes, newData.strings)
|
|
||||||
|
|
||||||
val oldProto = oldPackageData.packageProto
|
|
||||||
val newProto = newPackageData.packageProto
|
|
||||||
|
|
||||||
override val oldNameResolver = oldPackageData.nameResolver
|
|
||||||
override val newNameResolver = newPackageData.nameResolver
|
|
||||||
|
|
||||||
val diff = compareObject.difference(oldProto, newProto)
|
|
||||||
|
|
||||||
override fun difference(): Difference {
|
|
||||||
val names = hashSetOf<String>()
|
|
||||||
|
|
||||||
fun calcDifferenceForNonPrivateMembers(members: (ProtoBuf.Package) -> List<MessageLite>): Collection<String> {
|
|
||||||
val oldMembers = members(oldProto).filterNot { it.isPrivate }
|
|
||||||
val newMembers = members(newProto).filterNot { it.isPrivate }
|
|
||||||
return calcDifferenceForMembers(oldMembers, newMembers)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (kind in diff) {
|
|
||||||
when (kind!!) {
|
|
||||||
ProtoBufPackageKind.FUNCTION_LIST ->
|
|
||||||
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Package::getFunctionList))
|
|
||||||
ProtoBufPackageKind.PROPERTY_LIST ->
|
|
||||||
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Package::getPropertyList))
|
|
||||||
ProtoBufPackageKind.TYPE_TABLE -> {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Unsupported kind: $kind")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Difference(changedMembersNames = names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val ProtoBuf.Class.isSealed: Boolean
|
|
||||||
get() = ProtoBuf.Modality.SEALED == Flags.MODALITY.get(flags)
|
|
||||||
@@ -1,81 +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.storage
|
|
||||||
|
|
||||||
import com.intellij.util.io.DataExternalizer
|
|
||||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
|
||||||
import com.intellij.util.io.KeyDescriptor
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
internal abstract class BasicMap<K : Comparable<K>, V>(
|
|
||||||
storageFile: File,
|
|
||||||
keyDescriptor: KeyDescriptor<K>,
|
|
||||||
valueExternalizer: DataExternalizer<V>
|
|
||||||
) {
|
|
||||||
protected val storage = LazyStorage(storageFile, keyDescriptor, valueExternalizer)
|
|
||||||
|
|
||||||
open fun clean() {
|
|
||||||
storage.clean()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun flush(memoryCachesOnly: Boolean) {
|
|
||||||
storage.flush(memoryCachesOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun close() {
|
|
||||||
storage.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly
|
|
||||||
fun dump(): String {
|
|
||||||
return with(StringBuilder()) {
|
|
||||||
with(Printer(this)) {
|
|
||||||
println(this@BasicMap.javaClass.simpleName)
|
|
||||||
pushIndent()
|
|
||||||
|
|
||||||
for (key in storage.keys.sorted()) {
|
|
||||||
println("${dumpKey(key)} -> ${dumpValue(storage[key]!!)}")
|
|
||||||
}
|
|
||||||
|
|
||||||
popIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly
|
|
||||||
protected abstract fun dumpKey(key: K): String
|
|
||||||
|
|
||||||
@TestOnly
|
|
||||||
protected abstract fun dumpValue(value: V): String
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract class BasicStringMap<V>(
|
|
||||||
storageFile: File,
|
|
||||||
keyDescriptor: KeyDescriptor<String>,
|
|
||||||
valueExternalizer: DataExternalizer<V>
|
|
||||||
) : BasicMap<String, V>(storageFile, keyDescriptor, valueExternalizer) {
|
|
||||||
constructor(
|
|
||||||
storageFile: File,
|
|
||||||
valueExternalizer: DataExternalizer<V>
|
|
||||||
) : this(storageFile, EnumeratorStringDescriptor.INSTANCE, valueExternalizer)
|
|
||||||
|
|
||||||
override fun dumpKey(key: String): String = key
|
|
||||||
}
|
|
||||||
@@ -1,47 +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.storage
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
|
||||||
import org.jetbrains.jps.incremental.storage.StorageOwner
|
|
||||||
|
|
||||||
open class BasicMapsOwner : StorageOwner {
|
|
||||||
private val maps = arrayListOf<BasicMap<*, *>>()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val CACHE_EXTENSION = "tab"
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun <K, V, M : BasicMap<K, V>> registerMap(map: M): M {
|
|
||||||
maps.add(map)
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clean() {
|
|
||||||
maps.forEach { it.clean() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
maps.forEach { it.close() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush(memoryCachesOnly: Boolean) {
|
|
||||||
maps.forEach { it.flush(memoryCachesOnly) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOnly fun dump(): String = maps.map { it.dump() }.joinToString("\n\n")
|
|
||||||
}
|
|
||||||
@@ -1,55 +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.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(), 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)
|
|
||||||
@@ -1,38 +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.storage
|
|
||||||
|
|
||||||
import com.intellij.util.io.ExternalIntegerKeyDescriptor
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
internal class IdToFileMap(file: File) : BasicMap<Int, File>(file, ExternalIntegerKeyDescriptor(), FileKeyDescriptor) {
|
|
||||||
override fun dumpKey(key: Int): String = key.toString()
|
|
||||||
|
|
||||||
override fun dumpValue(value: File): String = value.toString()
|
|
||||||
|
|
||||||
operator fun get(id: Int): File? = storage[id]
|
|
||||||
|
|
||||||
operator fun contains(id: Int): Boolean = id in storage
|
|
||||||
|
|
||||||
operator fun set(id: Int, file: File) {
|
|
||||||
storage[id] = file
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(id: Int) {
|
|
||||||
storage.remove(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +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.storage
|
|
||||||
|
|
||||||
import com.intellij.util.io.DataExternalizer
|
|
||||||
import com.intellij.util.io.IOUtil
|
|
||||||
import com.intellij.util.io.KeyDescriptor
|
|
||||||
import com.intellij.util.io.PersistentHashMap
|
|
||||||
import java.io.DataOutput
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It's lazy in a sense that PersistentHashMap is created only on write
|
|
||||||
*/
|
|
||||||
internal class LazyStorage<K, V>(
|
|
||||||
private val storageFile: File,
|
|
||||||
private val keyDescriptor: KeyDescriptor<K>,
|
|
||||||
private val valueExternalizer: DataExternalizer<V>
|
|
||||||
) {
|
|
||||||
@Volatile
|
|
||||||
private var storage: PersistentHashMap<K, V>? = null
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
|
||||||
if (storage != null) return storage
|
|
||||||
|
|
||||||
if (storageFile.exists()) {
|
|
||||||
storage = createMap()
|
|
||||||
return storage
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun getStorageOrCreateNew(): PersistentHashMap<K, V> {
|
|
||||||
if (storage == null) {
|
|
||||||
storage = createMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val keys: Collection<K>
|
|
||||||
get() = getStorageIfExists()?.allKeysWithExistingMapping ?: listOf()
|
|
||||||
|
|
||||||
operator fun contains(key: K): Boolean =
|
|
||||||
getStorageIfExists()?.containsMapping(key) ?: false
|
|
||||||
|
|
||||||
operator fun get(key: K): V? =
|
|
||||||
getStorageIfExists()?.get(key)
|
|
||||||
|
|
||||||
operator fun set(key: K, value: V) {
|
|
||||||
getStorageOrCreateNew().put(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(key: K) {
|
|
||||||
getStorageIfExists()?.remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun append(key: K, value: String) {
|
|
||||||
append(key) { out -> IOUtil.writeUTF(out, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun append(key: K, value: Int) {
|
|
||||||
append(key) { out -> out.writeInt(value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun clean() {
|
|
||||||
try {
|
|
||||||
storage?.close()
|
|
||||||
}
|
|
||||||
catch (ignored: IOException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PersistentHashMap.deleteFilesStartingWith(storageFile)
|
|
||||||
storage = null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun flush(memoryCachesOnly: Boolean) {
|
|
||||||
val existingStorage = storage ?: return
|
|
||||||
|
|
||||||
if (memoryCachesOnly) {
|
|
||||||
if (existingStorage.isDirty) {
|
|
||||||
existingStorage.dropMemoryCaches()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
existingStorage.force()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun close() {
|
|
||||||
storage?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createMap(): PersistentHashMap<K, V> =
|
|
||||||
PersistentHashMap(storageFile, keyDescriptor, valueExternalizer)
|
|
||||||
|
|
||||||
private fun append(key: K, append: (DataOutput)->Unit) {
|
|
||||||
getStorageOrCreateNew().appendData(key, append)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +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.storage
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
internal class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
|
||||||
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
|
||||||
|
|
||||||
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
|
||||||
|
|
||||||
fun add(name: String, scope: String, fileId: Int) {
|
|
||||||
storage.append(LookupSymbolKey(name, scope), fileId)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(key: LookupSymbolKey): Collection<Int>? = storage[key]
|
|
||||||
|
|
||||||
operator fun set(key: LookupSymbolKey, fileIds: Set<Int>) {
|
|
||||||
storage[key] = fileIds
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(key: LookupSymbolKey) {
|
|
||||||
storage.remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
val keys: Collection<LookupSymbolKey>
|
|
||||||
get() = storage.keys
|
|
||||||
}
|
|
||||||
@@ -1,233 +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.storage
|
|
||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import com.intellij.util.io.DataExternalizer
|
|
||||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
|
||||||
import com.intellij.util.io.IOUtil
|
|
||||||
import com.intellij.util.io.KeyDescriptor
|
|
||||||
import gnu.trove.THashSet
|
|
||||||
import org.jetbrains.jps.incremental.storage.PathStringDescriptor
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.io.DataInputStream
|
|
||||||
import java.io.DataOutput
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object LookupSymbolKeyDescriptor : KeyDescriptor<LookupSymbolKey> {
|
|
||||||
override fun read(input: DataInput): LookupSymbolKey {
|
|
||||||
val first = input.readInt()
|
|
||||||
val second = input.readInt()
|
|
||||||
|
|
||||||
return LookupSymbolKey(first, second)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: LookupSymbolKey) {
|
|
||||||
output.writeInt(value.nameHash)
|
|
||||||
output.writeInt(value.scopeHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHashCode(value: LookupSymbolKey): Int = value.hashCode()
|
|
||||||
|
|
||||||
override fun isEqual(val1: LookupSymbolKey, val2: LookupSymbolKey): Boolean = val1 == val2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object PathFunctionPairKeyDescriptor : KeyDescriptor<PathFunctionPair> {
|
|
||||||
override fun read(input: DataInput): PathFunctionPair {
|
|
||||||
val path = IOUtil.readUTF(input)
|
|
||||||
val function = IOUtil.readUTF(input)
|
|
||||||
return PathFunctionPair(path, function)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: PathFunctionPair) {
|
|
||||||
IOUtil.writeUTF(output, value.path)
|
|
||||||
IOUtil.writeUTF(output, value.function)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHashCode(value: PathFunctionPair): Int = value.hashCode()
|
|
||||||
|
|
||||||
override fun isEqual(val1: PathFunctionPair, val2: PathFunctionPair): Boolean = val1 == val2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object ProtoMapValueExternalizer : DataExternalizer<ProtoMapValue> {
|
|
||||||
override fun save(output: DataOutput, value: ProtoMapValue) {
|
|
||||||
output.writeBoolean(value.isPackageFacade)
|
|
||||||
output.writeInt(value.bytes.size)
|
|
||||||
output.write(value.bytes)
|
|
||||||
output.writeInt(value.strings.size)
|
|
||||||
|
|
||||||
for (string in value.strings) {
|
|
||||||
output.writeUTF(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(input: DataInput): ProtoMapValue {
|
|
||||||
val isPackageFacade = input.readBoolean()
|
|
||||||
val bytesLength = input.readInt()
|
|
||||||
val bytes = ByteArray(bytesLength)
|
|
||||||
input.readFully(bytes, 0, bytesLength)
|
|
||||||
val stringsLength = input.readInt()
|
|
||||||
val strings = Array<String>(stringsLength) { input.readUTF() }
|
|
||||||
return ProtoMapValue(isPackageFacade, bytes, strings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
abstract class StringMapExternalizer<T> : DataExternalizer<Map<String, T>> {
|
|
||||||
override fun save(output: DataOutput, map: Map<String, T>?) {
|
|
||||||
output.writeInt(map!!.size)
|
|
||||||
|
|
||||||
for ((key, value) in map.entries) {
|
|
||||||
IOUtil.writeString(key, output)
|
|
||||||
writeValue(output, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(input: DataInput): Map<String, T>? {
|
|
||||||
val size = input.readInt()
|
|
||||||
val map = HashMap<String, T>(size)
|
|
||||||
|
|
||||||
repeat(size) {
|
|
||||||
val name = IOUtil.readString(input)!!
|
|
||||||
map[name] = readValue(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun writeValue(output: DataOutput, value: T)
|
|
||||||
protected abstract fun readValue(input: DataInput): T
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object StringToLongMapExternalizer : StringMapExternalizer<Long>() {
|
|
||||||
override fun readValue(input: DataInput): Long = input.readLong()
|
|
||||||
|
|
||||||
override fun writeValue(output: DataOutput, value: Long) {
|
|
||||||
output.writeLong(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ConstantsMapExternalizer : DataExternalizer<Map<String, Any>> {
|
|
||||||
override fun save(output: DataOutput, map: Map<String, Any>?) {
|
|
||||||
output.writeInt(map!!.size)
|
|
||||||
for (name in map.keys.sorted()) {
|
|
||||||
IOUtil.writeString(name, output)
|
|
||||||
val value = map[name]!!
|
|
||||||
when (value) {
|
|
||||||
is Int -> {
|
|
||||||
output.writeByte(Kind.INT.ordinal)
|
|
||||||
output.writeInt(value)
|
|
||||||
}
|
|
||||||
is Float -> {
|
|
||||||
output.writeByte(Kind.FLOAT.ordinal)
|
|
||||||
output.writeFloat(value)
|
|
||||||
}
|
|
||||||
is Long -> {
|
|
||||||
output.writeByte(Kind.LONG.ordinal)
|
|
||||||
output.writeLong(value)
|
|
||||||
}
|
|
||||||
is Double -> {
|
|
||||||
output.writeByte(Kind.DOUBLE.ordinal)
|
|
||||||
output.writeDouble(value)
|
|
||||||
}
|
|
||||||
is String -> {
|
|
||||||
output.writeByte(Kind.STRING.ordinal)
|
|
||||||
IOUtil.writeString(value, output)
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException("Unexpected constant class: ${value.javaClass}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(input: DataInput): Map<String, Any>? {
|
|
||||||
val size = input.readInt()
|
|
||||||
val map = HashMap<String, Any>(size)
|
|
||||||
|
|
||||||
repeat(size) {
|
|
||||||
val name = IOUtil.readString(input)!!
|
|
||||||
val kind = Kind.values()[input.readByte().toInt()]
|
|
||||||
|
|
||||||
val value: Any = when (kind) {
|
|
||||||
Kind.INT -> input.readInt()
|
|
||||||
Kind.FLOAT -> input.readFloat()
|
|
||||||
Kind.LONG -> input.readLong()
|
|
||||||
Kind.DOUBLE -> input.readDouble()
|
|
||||||
Kind.STRING -> IOUtil.readString(input)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
map[name] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class Kind {
|
|
||||||
INT, FLOAT, LONG, DOUBLE, STRING
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object IntExternalizer : DataExternalizer<Int> {
|
|
||||||
override fun read(input: DataInput): Int = input.readInt()
|
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: Int) {
|
|
||||||
output.writeInt(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object FileKeyDescriptor : KeyDescriptor<File> {
|
|
||||||
override fun read(input: DataInput): File = File(input.readUTF())
|
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: File) {
|
|
||||||
output.writeUTF(value.canonicalPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHashCode(value: File?): Int =
|
|
||||||
FileUtil.FILE_HASHING_STRATEGY.computeHashCode(value)
|
|
||||||
|
|
||||||
override fun isEqual(val1: File?, val2: File?): Boolean =
|
|
||||||
FileUtil.FILE_HASHING_STRATEGY.equals(val1, val2)
|
|
||||||
}
|
|
||||||
|
|
||||||
open class CollectionExternalizer<T>(
|
|
||||||
private val elementExternalizer: DataExternalizer<T>,
|
|
||||||
private val newCollection: ()->MutableCollection<T>
|
|
||||||
) : DataExternalizer<Collection<T>> {
|
|
||||||
override fun read(input: DataInput): Collection<T> {
|
|
||||||
val result = newCollection()
|
|
||||||
val stream = input as DataInputStream
|
|
||||||
|
|
||||||
while (stream.available() > 0) {
|
|
||||||
result.add(elementExternalizer.read(stream))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: Collection<T>) {
|
|
||||||
value.forEach { elementExternalizer.save(output, it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object StringCollectionExternalizer : CollectionExternalizer<String>(EnumeratorStringDescriptor(), { HashSet() })
|
|
||||||
|
|
||||||
object PathCollectionExternalizer : CollectionExternalizer<String>(PathStringDescriptor(), { THashSet(FileUtil.PATH_HASHING_STRATEGY) })
|
|
||||||
|
|
||||||
object IntCollectionExternalizer : CollectionExternalizer<Int>(IntExternalizer, { HashSet() })
|
|
||||||
@@ -1,56 +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.storage
|
|
||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
|
|
||||||
data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int) : Comparable<LookupSymbolKey> {
|
|
||||||
constructor(name: String, scope: String) : this(name.hashCode(), scope.hashCode())
|
|
||||||
|
|
||||||
override fun compareTo(other: LookupSymbolKey): Int {
|
|
||||||
val nameCmp = nameHash.compareTo(other.nameHash)
|
|
||||||
|
|
||||||
if (nameCmp != 0) return nameCmp
|
|
||||||
|
|
||||||
return scopeHash.compareTo(other.scopeHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PathFunctionPair(
|
|
||||||
val path: String,
|
|
||||||
val function: String
|
|
||||||
): Comparable<PathFunctionPair> {
|
|
||||||
override fun compareTo(other: PathFunctionPair): Int {
|
|
||||||
val pathComp = FileUtil.comparePaths(path, other.path)
|
|
||||||
|
|
||||||
if (pathComp != 0) return pathComp
|
|
||||||
|
|
||||||
return function.compareTo(other.function)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
when (other) {
|
|
||||||
is PathFunctionPair ->
|
|
||||||
FileUtil.pathsEqual(path, other.path) && function == other.function
|
|
||||||
else ->
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = 31 * FileUtil.pathHashCode(path) + function.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ProtoMapValue(val isPackageFacade: Boolean, val bytes: ByteArray, val strings: Array<String>)
|
|
||||||
@@ -44,11 +44,11 @@ import org.jetbrains.jps.model.java.JpsJavaDependencyScope
|
|||||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||||
import org.jetbrains.jps.util.JpsPathUtil
|
import org.jetbrains.jps.util.JpsPathUtil
|
||||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
|
import org.jetbrains.kotlin.incremental.LookupSymbol
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.jps.build.classFilesComparison.assertEqualDirectories
|
import org.jetbrains.kotlin.jps.build.classFilesComparison.assertEqualDirectories
|
||||||
|
import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageProvider
|
||||||
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
|
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
|
||||||
import org.jetbrains.kotlin.jps.incremental.LookupStorageProvider
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.LookupSymbol
|
|
||||||
import org.jetbrains.kotlin.jps.incremental.getKotlinCache
|
import org.jetbrains.kotlin.jps.incremental.getKotlinCache
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
@@ -384,7 +384,7 @@ abstract class AbstractIncrementalJpsTest(
|
|||||||
p.println("Begin of Lookup Maps")
|
p.println("Begin of Lookup Maps")
|
||||||
p.println()
|
p.println()
|
||||||
|
|
||||||
val lookupStorage = project.dataManager.getStorage(KotlinDataContainerTarget, LookupStorageProvider)
|
val lookupStorage = project.dataManager.getStorage(KotlinDataContainerTarget, JpsLookupStorageProvider)
|
||||||
lookupStorage.forceGC()
|
lookupStorage.forceGC()
|
||||||
p.print(lookupStorage.dump(lookupsDuringTest))
|
p.print(lookupStorage.dump(lookupsDuringTest))
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -20,11 +20,11 @@ import com.intellij.testFramework.UsefulTestCase
|
|||||||
import org.jetbrains.jps.builders.BuildTarget
|
import org.jetbrains.jps.builders.BuildTarget
|
||||||
import org.jetbrains.jps.builders.storage.BuildDataPaths
|
import org.jetbrains.jps.builders.storage.BuildDataPaths
|
||||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
import org.jetbrains.kotlin.jps.incremental.CacheVersion
|
import org.jetbrains.kotlin.incremental.CacheVersion
|
||||||
|
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
|
||||||
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider
|
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider
|
||||||
import org.jetbrains.kotlin.jps.incremental.KOTLIN_CACHE_DIRECTORY_NAME
|
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
|
||||||
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
|
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
|
||||||
import org.jetbrains.kotlin.jps.incremental.storage.BasicMapsOwner
|
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -119,4 +119,4 @@ abstract class AbstractIncrementalLazyCachesTest : AbstractIncrementalJpsTest()
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ import com.google.common.hash.Hashing
|
|||||||
import com.google.common.io.Files
|
import com.google.common.io.Files
|
||||||
import com.google.protobuf.ExtensionRegistry
|
import com.google.protobuf.ExtensionRegistry
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.jps.incremental.LocalFileKotlinClass
|
import org.jetbrains.kotlin.incremental.LocalFileKotlinClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.serialization.DebugProtoBuf
|
import org.jetbrains.kotlin.serialization.DebugProtoBuf
|
||||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||||
|
|||||||
+4
-2
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.jps.incremental
|
|||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.testFramework.UsefulTestCase
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue
|
import org.jetbrains.kotlin.incremental.LocalFileKotlinClass
|
||||||
|
import org.jetbrains.kotlin.incremental.difference
|
||||||
|
import org.jetbrains.kotlin.incremental.storage.ProtoMapValue
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||||
@@ -124,4 +126,4 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
|
|||||||
FileUtil.createDirectory(directory)
|
FileUtil.createDirectory(directory)
|
||||||
return directory
|
return directory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user