Use lazy analysis for highlighting and other purposes
This commit is contained in:
@@ -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;
|
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.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.search.GlobalSearchScope;
|
||||||
import com.intellij.psi.util.CachedValue;
|
import com.intellij.psi.util.CachedValue;
|
||||||
import com.intellij.psi.util.CachedValueProvider;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
|
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.JetElement;
|
||||||
import org.jetbrains.jet.lang.psi.JetFile;
|
import org.jetbrains.jet.lang.psi.JetFile;
|
||||||
import org.jetbrains.jet.lang.resolve.*;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
|
|
||||||
import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
|
import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
|
||||||
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
|
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.*;
|
import org.jetbrains.jet.plugin.caches.resolve.DeclarationsCacheProvider;
|
||||||
import org.jetbrains.jet.plugin.util.ApplicationUtils;
|
import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManager;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
public final class AnalyzerFacadeWithCache {
|
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() {
|
private AnalyzerFacadeWithCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,16 +48,7 @@ public final class AnalyzerFacadeWithCache {
|
|||||||
// TODO: Also need to pass several files when user have multi-file environment
|
// TODO: Also need to pass several files when user have multi-file environment
|
||||||
@NotNull
|
@NotNull
|
||||||
public static AnalyzeExhaust analyzeFileWithCache(@NotNull JetFile file) {
|
public static AnalyzeExhaust analyzeFileWithCache(@NotNull JetFile file) {
|
||||||
// Need lock, because parallel threads can start evaluation of compute() simultaneously
|
return getLazyResolveSessionForFile(file).getResolveCache().getAnalysisResultsForElement(file);
|
||||||
synchronized (lock) {
|
|
||||||
Project project = file.getProject();
|
|
||||||
return CachedValuesManager.getManager(project).getCachedValue(
|
|
||||||
project,
|
|
||||||
ANALYZE_EXHAUST_FULL,
|
|
||||||
new SLRUCachedAnalyzeExhaustProvider(),
|
|
||||||
false
|
|
||||||
).get(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -107,13 +76,6 @@ public final class AnalyzerFacadeWithCache {
|
|||||||
return provider.getLazyResolveSession();
|
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) {
|
private static final SLRUCache<JetFile, CachedValue<ResolveSessionForBodies>> PER_FILE_SESSION_CACHE = new SLRUCache<JetFile, CachedValue<ResolveSessionForBodies>>(2, 3) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
@@ -147,86 +109,4 @@ public final class AnalyzerFacadeWithCache {
|
|||||||
true);
|
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.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
|
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
|
||||||
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
|
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
|
||||||
|
import org.jetbrains.jet.plugin.caches.resolve.KotlinResolveCache;
|
||||||
|
|
||||||
public class ResolveSessionForBodies implements KotlinCodeAnalyzer, ModificationTracker {
|
public class ResolveSessionForBodies implements KotlinCodeAnalyzer, ModificationTracker {
|
||||||
private final Object createdForObject;
|
private final Object createdForObject;
|
||||||
private final ResolveSession resolveSession;
|
private final ResolveSession resolveSession;
|
||||||
private final ResolveElementCache resolveElementCache;
|
private final ResolveElementCache resolveElementCache;
|
||||||
|
private final KotlinResolveCache resolveCache;
|
||||||
|
|
||||||
public ResolveSessionForBodies(@NotNull JetFile file, @NotNull ResolveSession resolveSession) {
|
public ResolveSessionForBodies(@NotNull JetFile file, @NotNull ResolveSession resolveSession) {
|
||||||
this(file, file.getProject(), resolveSession);
|
this(file, file.getProject(), resolveSession);
|
||||||
@@ -47,6 +49,7 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification
|
|||||||
this.createdForObject = createdForObject;
|
this.createdForObject = createdForObject;
|
||||||
this.resolveSession = resolveSession;
|
this.resolveSession = resolveSession;
|
||||||
this.resolveElementCache = new ResolveElementCache(resolveSession, project);
|
this.resolveElementCache = new ResolveElementCache(resolveSession, project);
|
||||||
|
this.resolveCache = new KotlinResolveCache(project, resolveSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -92,4 +95,9 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer, Modification
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "ResolveSessionForBodies: " + getModificationCount() + " " + createdForObject + " " + createdForObject.hashCode();
|
return "ResolveSessionForBodies: " + getModificationCount() + " " + createdForObject + " " + createdForObject.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public KotlinResolveCache getResolveCache() {
|
||||||
|
return resolveCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user