Fetch analysis results if it is available
The general idea is to try to fetch a full analysis if it is existed in PerFileAnalysisCache instead of go to ResolveElementCache and do analysis there. Do not perform analysis if it is not in PerFileAnalysisCache. #KT-37467 Fixed
This commit is contained in:
+9
-1
@@ -57,7 +57,15 @@ internal class ModuleResolutionFacadeImpl(
|
|||||||
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
|
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||||
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
|
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
|
||||||
|
|
||||||
if (elements.isEmpty()) return BindingContext.EMPTY
|
when (elements.size) {
|
||||||
|
0 -> return BindingContext.EMPTY
|
||||||
|
1 -> {
|
||||||
|
runWithCancellationCheck {
|
||||||
|
projectFacade.fetchAnalysisResultsForElement(elements.first())?.bindingContext
|
||||||
|
}?.let { return it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java)
|
val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java)
|
||||||
return runWithCancellationCheck {
|
return runWithCancellationCheck {
|
||||||
resolveElementCache.resolveToElements(elements, bodyResolveMode)
|
resolveElementCache.resolveToElements(elements, bodyResolveMode)
|
||||||
|
|||||||
+40
-14
@@ -28,7 +28,9 @@ import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
|
|||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.clearInBlockModifications
|
import org.jetbrains.kotlin.idea.caches.trackers.clearInBlockModifications
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications
|
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications
|
||||||
import org.jetbrains.kotlin.idea.project.*
|
import org.jetbrains.kotlin.idea.project.IdeaModuleStructureOracle
|
||||||
|
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
|
||||||
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.*
|
||||||
@@ -40,6 +42,7 @@ import org.jetbrains.kotlin.types.KotlinType
|
|||||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
internal class PerFileAnalysisCache(val file: KtFile, componentProvider: ComponentProvider) {
|
internal class PerFileAnalysisCache(val file: KtFile, componentProvider: ComponentProvider) {
|
||||||
private val globalContext = componentProvider.get<GlobalContext>()
|
private val globalContext = componentProvider.get<GlobalContext>()
|
||||||
@@ -50,13 +53,30 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
|
|
||||||
private val cache = HashMap<PsiElement, AnalysisResult>()
|
private val cache = HashMap<PsiElement, AnalysisResult>()
|
||||||
private var fileResult: AnalysisResult? = null
|
private var fileResult: AnalysisResult? = null
|
||||||
|
private val lock = ReentrantLock()
|
||||||
|
|
||||||
fun getAnalysisResults(element: KtElement): AnalysisResult {
|
internal fun fetchAnalysisResults(element: KtElement): AnalysisResult? {
|
||||||
|
assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" }
|
||||||
|
|
||||||
|
if (lock.tryLock()) {
|
||||||
|
try {
|
||||||
|
updateFileResultFromCache()
|
||||||
|
|
||||||
|
return fileResult?.takeIf { file.inBlockModifications.isEmpty() }
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getAnalysisResults(element: KtElement): AnalysisResult {
|
||||||
assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" }
|
assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" }
|
||||||
|
|
||||||
val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element)
|
val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element)
|
||||||
|
|
||||||
return synchronized(this) {
|
lock.lock()
|
||||||
|
try {
|
||||||
ProgressIndicatorProvider.checkCanceled()
|
ProgressIndicatorProvider.checkCanceled()
|
||||||
|
|
||||||
// step 1: perform incremental analysis IF it is applicable
|
// step 1: perform incremental analysis IF it is applicable
|
||||||
@@ -67,27 +87,21 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
|
|
||||||
// step 2: return result if it is cached
|
// step 2: return result if it is cached
|
||||||
lookUp(analyzableParent)?.let {
|
lookUp(analyzableParent)?.let {
|
||||||
return@synchronized it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 3: perform analyze of analyzableParent as nothing has been cached yet
|
// step 3: perform analyze of analyzableParent as nothing has been cached yet
|
||||||
val result = analyze(analyzableParent)
|
val result = analyze(analyzableParent)
|
||||||
cache[analyzableParent] = result
|
cache[analyzableParent] = result
|
||||||
|
|
||||||
return@synchronized result
|
return result
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIncrementalAnalysisResult(): AnalysisResult? {
|
private fun getIncrementalAnalysisResult(): AnalysisResult? {
|
||||||
// move fileResult from cache if it is stored there
|
updateFileResultFromCache()
|
||||||
if (fileResult == null && cache.containsKey(file)) {
|
|
||||||
fileResult = cache[file]
|
|
||||||
|
|
||||||
// drop existed results for entire cache:
|
|
||||||
// if incremental analysis is applicable it will produce a single value for file
|
|
||||||
// otherwise those results are potentially stale
|
|
||||||
cache.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
val inBlockModifications = file.inBlockModifications
|
val inBlockModifications = file.inBlockModifications
|
||||||
if (inBlockModifications.isNotEmpty()) {
|
if (inBlockModifications.isNotEmpty()) {
|
||||||
@@ -139,6 +153,18 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
return fileResult
|
return fileResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateFileResultFromCache() {
|
||||||
|
// move fileResult from cache if it is stored there
|
||||||
|
if (fileResult == null && cache.containsKey(file)) {
|
||||||
|
fileResult = cache[file]
|
||||||
|
|
||||||
|
// drop existed results for entire cache:
|
||||||
|
// if incremental analysis is applicable it will produce a single value for file
|
||||||
|
// otherwise those results are potentially stale
|
||||||
|
cache.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun lookUp(analyzableElement: KtElement): AnalysisResult? {
|
private fun lookUp(analyzableElement: KtElement): AnalysisResult? {
|
||||||
// Looking for parent elements that are already analyzed
|
// Looking for parent elements that are already analyzed
|
||||||
// Also removing all elements whose parents are already analyzed, to guarantee consistency
|
// Also removing all elements whose parents are already analyzed, to guarantee consistency
|
||||||
|
|||||||
+10
@@ -136,6 +136,16 @@ internal class ProjectResolutionFacade(
|
|||||||
return AnalysisResult.success(bindingContext, findModuleDescriptor(elements.first().getModuleInfo()))
|
return AnalysisResult.success(bindingContext, findModuleDescriptor(elements.first().getModuleInfo()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun fetchAnalysisResultsForElement(element: KtElement): AnalysisResult? {
|
||||||
|
val slruCache = synchronized(analysisResults) {
|
||||||
|
analysisResults.upToDateOrNull?.get() ?: return null
|
||||||
|
}
|
||||||
|
val perFileCache = synchronized(slruCache) {
|
||||||
|
slruCache[element.containingKtFile]
|
||||||
|
}
|
||||||
|
return perFileCache.fetchAnalysisResults(element)
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$debugString@${Integer.toHexString(hashCode())}"
|
return "$debugString@${Integer.toHexString(hashCode())}"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user