From d45347688dc17600b695b21c78d77fe37abf7403 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 22 Apr 2014 15:54:27 +0400 Subject: [PATCH] Create consolidated caching service to provide all lazily resolved data Other caches are to be removed later --- .../lang/resolve/java/JetFilesProvider.java | 1 + idea/src/META-INF/plugin.xml | 3 + .../caches/resolve/KotlinResolveCache.kt | 130 +++++++++++++++++- .../jet/plugin/caches/resolve/chacheUtils.kt | 38 +++++ .../project/AnalyzerFacadeWithCache.java | 3 +- .../project/ResolveSessionForBodies.java | 8 -- .../jet/plugin/util/DebuggerUtils.java | 5 +- 7 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/caches/resolve/chacheUtils.kt diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JetFilesProvider.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JetFilesProvider.java index f66eca7696a..fe0c03de9df 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JetFilesProvider.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JetFilesProvider.java @@ -29,6 +29,7 @@ import org.jetbrains.jet.lang.resolve.name.FqName; import java.util.Collection; public abstract class JetFilesProvider { + @NotNull public static JetFilesProvider getInstance(Project project) { return ServiceManager.getService(project, JetFilesProvider.class); } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index cc28417f9f4..230773b9caf 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -117,6 +117,9 @@ + + diff --git a/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt b/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt index 061e5c40568..18af6e85f25 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt +++ b/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt @@ -16,7 +16,6 @@ package org.jetbrains.jet.plugin.caches.resolve -import org.jetbrains.jet.lang.resolve.lazy.ResolveSession import org.jetbrains.jet.lang.psi.JetElement import org.jetbrains.jet.lang.psi.JetFile import com.intellij.psi.util.PsiTreeUtil @@ -40,19 +39,144 @@ import com.intellij.psi.util.CachedValueProvider import org.jetbrains.jet.asJava.LightClassUtil import com.intellij.openapi.roots.libraries.LibraryUtil import org.jetbrains.jet.lang.resolve.LibrarySourceHacks +import org.jetbrains.jet.lang.resolve.BindingContext +import com.intellij.openapi.components.ServiceManager +import org.jetbrains.jet.lang.resolve.java.JetFilesProvider +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.jet.plugin.project.AnalyzerFacadeProvider +import org.jetbrains.jet.plugin.project.TargetPlatform +import org.jetbrains.jet.plugin.project.TargetPlatform.* +import org.jetbrains.jet.plugin.project.ResolveSessionForBodies +import org.jetbrains.jet.plugin.project.TargetPlatformDetector +import com.google.common.collect.ImmutableMap +import org.jetbrains.jet.lang.resolve.Diagnostics +import org.jetbrains.jet.util.slicedmap.ReadOnlySlice +import org.jetbrains.jet.util.slicedmap.WritableSlice +import org.jetbrains.jet.lang.diagnostics.Diagnostic +import java.util.HashSet private val LOG = Logger.getInstance(javaClass()) -class KotlinResolveCache( +fun JetElement.getLazyResolveSession(): ResolveSessionForBodies { + return KotlinCacheService.getInstance(getProject()).getLazyResolveSession(this) +} + +fun JetElement.getAnalysisResults(): AnalyzeExhaust { + return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this)) +} + +fun JetElement.getBindingContext(): BindingContext { + return getAnalysisResults().getBindingContext() +} + +fun getAnalysisResultsForElements(elements: Collection): AnalyzeExhaust { + if (elements.isEmpty()) return AnalyzeExhaust.EMPTY + val element = elements.first() + return KotlinCacheService.getInstance(element.getProject()).getAnalysisResults(elements) +} + +class KotlinCacheService(val project: Project) { + class object { + fun getInstance(project: Project) = ServiceManager.getService(project, javaClass())!! + } + + private fun globalResolveSessionProvider(platform: TargetPlatform, syntheticFile: JetFile? = null) = { + val allFiles = JetFilesProvider.getInstance(project).allInScope(GlobalSearchScope.allScope(project)) + + val files = if (syntheticFile == null) allFiles else collectFilesForSyntheticFile(allFiles, syntheticFile) + val resolveSession = AnalyzerFacadeProvider.getAnalyzerFacade(platform).getLazyResolveSession(project, files) + CachedValueProvider.Result.create(ResolveSessionForBodies(project, resolveSession), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) + } + + private fun collectFilesForSyntheticFile(allFiles: Collection, syntheticFile: JetFile): Collection { + val files = HashSet(allFiles) + + // Add requested file to the list of files for searching declarations + files.add(syntheticFile) + + val originalFile = syntheticFile.getOriginalFile() + if (syntheticFile != originalFile) { + // Given file can be a non-physical copy of the file in list (completion case). Remove the prototype file. + files.remove(originalFile) + } + + return files + } + + private val globalCachesPerPlatform = mapOf( + JVM to KotlinResolveCache(project, globalResolveSessionProvider(JVM)), + JS to KotlinResolveCache(project, globalResolveSessionProvider(JS)) + ) + + private val syntheticFileCaches = object : SLRUCache>(2, 3) { + override fun createValue(file: JetFile?): SynchronizedCachedValue { + return SynchronizedCachedValue( + project, + { + CachedValueProvider.Result( + KotlinResolveCache( + project, + globalResolveSessionProvider( + TargetPlatformDetector.getPlatform(file!!), + file + ) + ), + PsiModificationTracker.MODIFICATION_COUNT + ) + }, + false) + } + + } + + public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies { + val file = element.getContainingJetFile() + if (!isFileInScope(file)) { + return synchronized(syntheticFileCaches) { + syntheticFileCaches[file].getValue().getLazyResolveSession() + } + } + + return globalCachesPerPlatform[TargetPlatformDetector.getPlatform(file)]!!.getLazyResolveSession() + } + + public fun getAnalysisResults(elements: Collection): AnalyzeExhaust { + if (elements.isEmpty()) return AnalyzeExhaust.EMPTY + + val firstFile = elements.first().getContainingJetFile() + if (elements.size == 1 && !isFileInScope(firstFile)) { + return synchronized(syntheticFileCaches) { + syntheticFileCaches[firstFile].getValue().getAnalysisResultsForElements(elements) + } + } + + val resolveCache = globalCachesPerPlatform[TargetPlatformDetector.getPlatform(firstFile)]!! + return synchronized(resolveCache) { + resolveCache.getAnalysisResultsForElements(elements) + } + } + + private fun isFileInScope(jetFile: JetFile): Boolean { + val project = jetFile.getProject() + return JetFilesProvider.getInstance(project).isFileInScope(jetFile, GlobalSearchScope.allScope(project)) + } +} + +private class KotlinResolveCache( val project: Project, - val resolveSession: ResolveSession + val resolveSessionProvider: () -> CachedValueProvider.Result ) { + private val resolveSessionCache = SynchronizedCachedValue(project, resolveSessionProvider) + + public fun getLazyResolveSession(): ResolveSessionForBodies = resolveSessionCache.getValue() + private data class Task( val elements: Set ) private val analysisResults = CachedValuesManager.getManager(project).createCachedValue ({ + val resolveSession = getLazyResolveSession() val results = object : SLRUCache(2, 3) { override fun createValue(task: Task?): AnalyzeExhaust { if (DumbService.isDumb(project)) { diff --git a/idea/src/org/jetbrains/jet/plugin/caches/resolve/chacheUtils.kt b/idea/src/org/jetbrains/jet/plugin/caches/resolve/chacheUtils.kt new file mode 100644 index 00000000000..44f9b77cc85 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/caches/resolve/chacheUtils.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2014 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.jet.plugin.caches.resolve + +import com.intellij.openapi.project.Project +import com.intellij.psi.util.PsiModificationTracker +import java.util.concurrent.locks.Lock +import java.util.concurrent.locks.ReentrantLock +import com.intellij.psi.util.CachedValuesManager +import com.intellij.util.containers.SLRUCache +import com.intellij.psi.util.CachedValueProvider + +class SynchronizedCachedValue(project: Project, provider: () -> CachedValueProvider.Result, trackValue: Boolean = true) { + private val cachedValue = CachedValuesManager.getManager(project).createCachedValue( + provider, + trackValue + ) + + public fun getValue(): V { + return synchronized(cachedValue) { + cachedValue.getValue() as V + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java index 2c8ef111694..9d84874c021 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java @@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import org.jetbrains.jet.plugin.caches.resolve.DeclarationsCacheProvider; import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManager; +import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage; import java.util.Collection; import java.util.HashSet; @@ -48,7 +49,7 @@ public final class AnalyzerFacadeWithCache { // TODO: Also need to pass several files when user have multi-file environment @NotNull public static AnalyzeExhaust analyzeFileWithCache(@NotNull JetFile file) { - return getLazyResolveSessionForFile(file).getResolveCache().getAnalysisResultsForElement(file); + return ResolvePackage.getAnalysisResults(file); } @NotNull diff --git a/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java b/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java index d957faa4e14..1d64d66f290 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java +++ b/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java @@ -32,7 +32,6 @@ import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider; import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor; import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.plugin.caches.resolve.KotlinResolveCache; import org.jetbrains.jet.storage.ExceptionTracker; import org.jetbrains.jet.storage.StorageManager; @@ -42,7 +41,6 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification private final Object createdForObject; private final ResolveSession resolveSession; private final ResolveElementCache resolveElementCache; - private final KotlinResolveCache resolveCache; public ResolveSessionForBodies(@NotNull JetFile file, @NotNull ResolveSession resolveSession) { this(file, file.getProject(), resolveSession); @@ -56,7 +54,6 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification this.createdForObject = createdForObject; this.resolveSession = resolveSession; this.resolveElementCache = new ResolveElementCache(resolveSession, project); - this.resolveCache = new KotlinResolveCache(project, resolveSession); } @NotNull @@ -103,11 +100,6 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification return "ResolveSessionForBodies: " + getModificationCount() + " " + createdForObject + " " + createdForObject.hashCode(); } - @NotNull - public KotlinResolveCache getResolveCache() { - return resolveCache; - } - @Override @Nullable public LazyPackageDescriptor getPackageFragment(@NotNull FqName fqName) { diff --git a/idea/src/org/jetbrains/jet/plugin/util/DebuggerUtils.java b/idea/src/org/jetbrains/jet/plugin/util/DebuggerUtils.java index 2c653207a12..c2bf5f6d798 100644 --- a/idea/src/org/jetbrains/jet/plugin/util/DebuggerUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/util/DebuggerUtils.java @@ -27,7 +27,7 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; +import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage; import java.util.Collection; @@ -72,8 +72,7 @@ public class DebuggerUtils { // In the rare case that there's more than one file with this name in this package, // we may actually need to analyze the project in order to find a file which produces this class // TODO: this code is not entirely correct, because it takes a session for only one file - AnalyzeExhaust analyzeExhaust = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(anyFile).getResolveCache() - .getAnalysisResultsForElements(files); + AnalyzeExhaust analyzeExhaust = ResolvePackage.getAnalysisResultsForElements(files); return PsiCodegenPredictor.getFileForCodegenNamedClass(analyzeExhaust.getBindingContext(), allPackageFiles, className.getInternalName()); }