Use lazy analysis for highlighting and other purposes

This commit is contained in:
Andrey Breslav
2014-04-08 16:37:12 +04:00
parent 4839f7a230
commit 1d7bd354d5
3 changed files with 134 additions and 124 deletions
@@ -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<KotlinResolveCache>())
class KotlinResolveCache(
val project: Project,
val resolveSession: ResolveSession
) {
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue ({
val results = object : SLRUCache<JetElement, AnalyzeExhaust>(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<JetFile>(), false);
}
private fun handleError(e: Throwable) {
DiagnosticUtils.throwIfRunningOnServer(e)
LOG.error(e)
}
}
@@ -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<CachedValue<SLRUCache<JetFile, AnalyzeExhaust>>> 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<JetFile, CachedValue<ResolveSessionForBodies>> PER_FILE_SESSION_CACHE = new SLRUCache<JetFile, CachedValue<ResolveSessionForBodies>>(2, 3) {
@NotNull
@Override
@@ -147,86 +109,4 @@ public final class AnalyzerFacadeWithCache {
true);
}
};
private static class SLRUCachedAnalyzeExhaustProvider implements CachedValueProvider<SLRUCache<JetFile, AnalyzeExhaust>> {
@Nullable
@Override
public Result<SLRUCache<JetFile, AnalyzeExhaust>> compute() {
final GlobalContext globalContext = ContextPackage.GlobalContext();
SLRUCache<JetFile, AnalyzeExhaust> cache = new SLRUCache<JetFile, AnalyzeExhaust>(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.<PsiFile>alwaysFalse(),
true,
AnalyzerFacadeForJVM.createJavaModule("<module>"),
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);
}
}
}
@@ -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;
}
}