diff --git a/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt b/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt new file mode 100644 index 00000000000..e9587500dd1 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/caches/resolve/KotlinResolveCache.kt @@ -0,0 +1,122 @@ +/* + * 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 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 +import com.intellij.openapi.project.Project +import org.jetbrains.jet.analyzer.AnalyzeExhaust +import com.intellij.psi.util.CachedValuesManager +import com.intellij.openapi.diagnostic.Logger +import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils +import com.intellij.util.containers.SLRUCache +import com.intellij.psi.util.PsiModificationTracker +import com.intellij.openapi.project.DumbService +import org.jetbrains.jet.plugin.util.ApplicationUtils +import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace +import org.jetbrains.jet.di.InjectorForTopDownAnalyzerForJvm +import org.jetbrains.jet.context.SimpleGlobalContext +import org.jetbrains.jet.descriptors.serialization.descriptors.MemberFilter +import org.jetbrains.jet.lang.resolve.TopDownAnalysisParameters +import com.intellij.openapi.progress.ProcessCanceledException +import org.jetbrains.jet.lang.resolve.BindingTraceContext +import com.intellij.psi.util.CachedValueProvider + +private val LOG = Logger.getInstance(javaClass()) + +class KotlinResolveCache( + val project: Project, + val resolveSession: ResolveSession +) { + private val analysisResults = CachedValuesManager.getManager(project).createCachedValue ({ + val results = object : SLRUCache(2, 3) { + override fun createValue(element: JetElement?): AnalyzeExhaust { + element!! + + if (DumbService.isDumb(project)) { + return AnalyzeExhaust.EMPTY + } + + ApplicationUtils.warnTimeConsuming(LOG) + + try { + if (element !is JetFile) { + error("Only files are supported") + } + + // todo: look for pre-existing results for this element or its parents + val trace = DelegatingBindingTrace(resolveSession.getBindingContext(), "Trace for resolution of " + element) + val injector = InjectorForTopDownAnalyzerForJvm( + project, + SimpleGlobalContext(resolveSession.getStorageManager(), resolveSession.getExceptionTracker()), + trace, + resolveSession.getModuleDescriptor(), + MemberFilter.ALWAYS_TRUE + ) + val resultingContext = injector.getLazyTopDownAnalyzer()!!.analyzeDeclarations( + resolveSession, + TopDownAnalysisParameters.createForLazy( + resolveSession.getStorageManager(), + resolveSession.getExceptionTracker(), + analyzeCompletely = { true }, + analyzingBootstrapLibrary = false, + declaredLocally = false + ), + listOf(getContainingNonlocalDeclaration(element)) + ) + return AnalyzeExhaust.success( + trace.getBindingContext(), + resultingContext, + resolveSession.getModuleDescriptor() + ) + } + catch (e: ProcessCanceledException) { + throw e + } + catch (e: Throwable) { + handleError(e) + + // Exception during body resolve analyze can harm internal caches in declarations cache + KotlinCacheManager.getInstance(project).invalidateCache() + + val bindingTraceContext = BindingTraceContext() + return AnalyzeExhaust.error(bindingTraceContext.getBindingContext(), e) + } + } + } + + CachedValueProvider.Result(results, PsiModificationTracker.MODIFICATION_COUNT, resolveSession.getExceptionTracker()) + }, false) + + fun getAnalysisResultsForElement(element: JetElement): AnalyzeExhaust { + return synchronized(analysisResults) { + analysisResults.getValue()!![element] + } + } + + private fun getContainingNonlocalDeclaration(element: JetElement): JetElement? { + return PsiTreeUtil.getParentOfType(element, javaClass(), false); + } + + private fun handleError(e: Throwable) { + DiagnosticUtils.throwIfRunningOnServer(e) + LOG.error(e) + } +} + diff --git a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java index 795d4831b63..be2285ca236 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java @@ -16,15 +16,7 @@ package org.jetbrains.jet.plugin.project; -import com.google.common.base.Predicates; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.libraries.LibraryUtil; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; @@ -34,33 +26,19 @@ import com.intellij.util.containers.SLRUCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzeExhaust; -import org.jetbrains.jet.asJava.LightClassUtil; -import org.jetbrains.jet.context.ContextPackage; -import org.jetbrains.jet.context.GlobalContext; -import org.jetbrains.jet.descriptors.serialization.descriptors.MemberFilter; -import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; -import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.resolve.*; -import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; -import org.jetbrains.jet.plugin.caches.resolve.*; -import org.jetbrains.jet.plugin.util.ApplicationUtils; +import org.jetbrains.jet.plugin.caches.resolve.DeclarationsCacheProvider; +import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManager; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; public final class AnalyzerFacadeWithCache { - private static final Logger LOG = Logger.getInstance("org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache"); - - private final static Key>> ANALYZE_EXHAUST_FULL = Key.create("ANALYZE_EXHAUST_FULL"); - - private static final Object lock = new Object(); - private AnalyzerFacadeWithCache() { } @@ -70,16 +48,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) { - // Need lock, because parallel threads can start evaluation of compute() simultaneously - synchronized (lock) { - Project project = file.getProject(); - return CachedValuesManager.getManager(project).getCachedValue( - project, - ANALYZE_EXHAUST_FULL, - new SLRUCachedAnalyzeExhaustProvider(), - false - ).get(file); - } + return getLazyResolveSessionForFile(file).getResolveCache().getAnalysisResultsForElement(file); } @NotNull @@ -107,13 +76,6 @@ public final class AnalyzerFacadeWithCache { return provider.getLazyResolveSession(); } - @NotNull - private static AnalyzeExhaust emptyExhaustWithDiagnosticOnFile(JetFile file, Throwable e) { - BindingTraceContext bindingTraceContext = new BindingTraceContext(); - bindingTraceContext.report(Errors.EXCEPTION_WHILE_ANALYZING.on(file, e)); - return AnalyzeExhaust.error(bindingTraceContext.getBindingContext(), e); - } - private static final SLRUCache> PER_FILE_SESSION_CACHE = new SLRUCache>(2, 3) { @NotNull @Override @@ -147,86 +109,4 @@ public final class AnalyzerFacadeWithCache { true); } }; - - private static class SLRUCachedAnalyzeExhaustProvider implements CachedValueProvider> { - @Nullable - @Override - public Result> compute() { - final GlobalContext globalContext = ContextPackage.GlobalContext(); - SLRUCache cache = new SLRUCache(3, 8) { - - @NotNull - @Override - public AnalyzeExhaust createValue(JetFile file) { - try { - if (DumbService.isDumb(file.getProject())) { - return AnalyzeExhaust.EMPTY; - } - - ApplicationUtils.warnTimeConsuming(LOG); - - AnalyzeExhaust analyzeExhaustHeaders = analyzeHeadersWithCacheOnFile(file, globalContext); - return analyzeBodies(analyzeExhaustHeaders, file); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (Throwable e) { - handleError(e); - - // Exception during body resolve analyze can harm internal caches in declarations cache - KotlinCacheManager.getInstance(file.getProject()).invalidateCache(); - - return emptyExhaustWithDiagnosticOnFile(file, e); - } - } - }; - return Result.create(cache, PsiModificationTracker.MODIFICATION_COUNT, globalContext.getExceptionTracker()); - } - - private static AnalyzeExhaust analyzeHeadersWithCacheOnFile(@NotNull JetFile fileToCache, @NotNull GlobalContext globalContext) { - VirtualFile virtualFile = fileToCache.getVirtualFile(); - if (LightClassUtil.belongsToKotlinBuiltIns(fileToCache) || - virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, fileToCache.getProject()) != null) { - // Library sources: - // Mark file to skip - fileToCache.putUserData(LibrarySourceHacks.SKIP_TOP_LEVEL_MEMBERS, true); - // Resolve this file, not only project files (as KotlinCacheManager do) - - return AnalyzerFacadeForJVM - .analyzeFilesWithJavaIntegrationInGlobalContext( - fileToCache.getProject(), - Collections.singleton(fileToCache), - new BindingTraceContext(), - Predicates.alwaysFalse(), - true, - AnalyzerFacadeForJVM.createJavaModule(""), - globalContext, - MemberFilter.ALWAYS_TRUE - ); - } - - KotlinDeclarationsCache cache = KotlinCacheManagerUtil.getDeclarationsFromProject(fileToCache); - return ((KotlinDeclarationsCacheImpl) cache).getAnalyzeExhaust(); - } - - private static AnalyzeExhaust analyzeBodies(AnalyzeExhaust analyzeExhaustHeaders, JetFile file) { - BodiesResolveContext context = analyzeExhaustHeaders.getBodiesResolveContext(); - assert context != null : "Headers resolver should prepare and stored information for bodies resolve"; - - // Need to resolve bodies in given file and all in the same package - return AnalyzerFacadeProvider.getAnalyzerFacadeForFile(file).analyzeBodiesInFiles( - file.getProject(), - new JetFilesProvider.SameJetFilePredicate(file), - new DelegatingBindingTrace(analyzeExhaustHeaders.getBindingContext(), - "trace to resolve bodies in file", file.getName()), - context, - analyzeExhaustHeaders.getModuleDescriptor()); - } - - private static void handleError(@NotNull Throwable e) { - DiagnosticUtils.throwIfRunningOnServer(e); - LOG.error(e); - } - } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java b/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java index 97fadf5cfa1..a5560566aa9 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java +++ b/idea/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java @@ -29,11 +29,13 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer; import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; +import org.jetbrains.jet.plugin.caches.resolve.KotlinResolveCache; public class ResolveSessionForBodies implements KotlinCodeAnalyzer, ModificationTracker { 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); @@ -47,6 +49,7 @@ 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 @@ -92,4 +95,9 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification public String toString() { return "ResolveSessionForBodies: " + getModificationCount() + " " + createdForObject + " " + createdForObject.hashCode(); } + + @NotNull + public KotlinResolveCache getResolveCache() { + return resolveCache; + } }