Cache resolve session under OUT_OF_BLOCK_MODIFICATION_TRACKER

This commit is contained in:
Nikolay Krasko
2013-08-12 14:05:53 +04:00
parent 1bb036bb80
commit 388f365900
7 changed files with 173 additions and 105 deletions
@@ -71,7 +71,6 @@ public class ResolveSession implements KotlinCodeAnalyzer {
private final InjectorForLazyResolve injector;
private final Function<FqName, Name> classifierAliases;
private final ResolveElementCache resolveElementCache;
public ResolveSession(
@NotNull Project project,
@@ -122,7 +121,6 @@ public class ResolveSession implements KotlinCodeAnalyzer {
rootDescriptor.setRootNamespace(rootPackage);
this.declarationProviderFactory = declarationProviderFactory;
this.resolveElementCache = new ResolveElementCache(this);
}
@NotNull
@@ -352,11 +350,6 @@ public class ResolveSession implements KotlinCodeAnalyzer {
return result;
}
@NotNull
public BindingContext resolveElement(@NotNull JetElement jetElement) {
return resolveElementCache.resolveElement(jetElement);
}
@NotNull
public Name resolveClassifierAlias(@NotNull FqName packageName, @NotNull Name alias) {
// TODO: creating a new FqName object every time...
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2013 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.completion;
import com.intellij.codeInsight.completion.CompletionProgressIndicator;
import com.intellij.codeInsight.completion.CompletionService;
import com.intellij.openapi.progress.ProcessCanceledException;
public class CompletionProgressIndicatorUtil {
private CompletionProgressIndicatorUtil() {
}
public static ProcessCanceledException rethrowWithCancelIndicator(ProcessCanceledException exception) {
CompletionProgressIndicator indicator = (CompletionProgressIndicator) CompletionService.getCompletionService().getCurrentCompletion();
assert indicator != null;
// Force cancel to avoid deadlock in CompletionThreading.delegateWeighing()
if (!indicator.isCanceled()) {
indicator.cancel();
}
return exception;
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
@@ -50,19 +51,24 @@ public class JetCompletionContributor extends CompletionContributor {
JetSimpleNameReference jetReference = getJetReference(parameters);
if (jetReference != null) {
result.restartCompletionWhenNothingMatches();
try {
result.restartCompletionWhenNothingMatches();
CompletionSession session = new CompletionSession(parameters, result, jetReference, position);
session.completeForReference();
if (!session.getJetResult().isSomethingAdded() && session.getParameters().getInvocationCount() < 2) {
// Rerun completion if nothing was found
session = new CompletionSession(parameters.withInvocationCount(2), result, jetReference, position);
CompletionSession session = new CompletionSession(parameters, result, jetReference, position);
session.completeForReference();
}
// Prevent from adding reference variants from standard reference contributor
result.stopHere();
if (!session.getJetResult().isSomethingAdded() && session.getParameters().getInvocationCount() < 2) {
// Rerun completion if nothing was found
session = new CompletionSession(parameters.withInvocationCount(2), result, jetReference, position);
session.completeForReference();
}
// Prevent from adding reference variants from standard reference contributor
result.stopHere();
}
catch (ProcessCanceledException e) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e);
}
}
}
@@ -18,6 +18,7 @@ package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
@@ -66,21 +67,26 @@ public class JetPackagesContributor extends CompletionContributor {
return;
}
int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset();
result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength)));
try {
int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset();
result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength)));
CancelableResolveSession resolveSession = WholeProjectAnalyzerFacade.getLazyResolveResultForFile(
(JetFile) simpleNameReference.getExpression().getContainingFile());
BindingContext bindingContext = resolveSession.resolveToElement(simpleNameReference.getExpression());
CancelableResolveSession resolveSession = WholeProjectAnalyzerFacade.getLazyResolveResultForFile(
(JetFile) simpleNameReference.getExpression().getContainingFile());
BindingContext bindingContext = resolveSession.resolveToElement(simpleNameReference.getExpression());
for (LookupElement variant : DescriptorLookupConverter.collectLookupElements(
resolveSession, bindingContext, TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext))) {
if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(variant);
for (LookupElement variant : DescriptorLookupConverter.collectLookupElements(
resolveSession, bindingContext, TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext))) {
if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(variant);
}
}
}
result.stopHere();
result.stopHere();
}
catch (ProcessCanceledException e) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e);
}
}
}
});
@@ -188,7 +188,7 @@ public final class AnalyzerFacadeWithCache {
ResolveSession resolveSession =
AnalyzerFacadeProvider.getAnalyzerFacadeForFile(file).getLazyResolveSession(fileProject, files);
return Result.create(new CancelableResolveSession(file, resolveSession), PsiModificationTracker.MODIFICATION_COUNT);
return Result.create(new CancelableResolveSession(file, resolveSession), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
}
},
true);
@@ -40,18 +40,20 @@ import java.util.concurrent.atomic.AtomicLong;
public class CancelableResolveSession implements KotlinCodeAnalyzer, ModificationTracker {
private final JetFile file;
private final ResolveSession resolveSession;
private final ResolveElementCache resolveElementCache;
private final AtomicLong canceledTracker = new AtomicLong();
public CancelableResolveSession(JetFile file, ResolveSession resolveSession) {
public CancelableResolveSession(@NotNull JetFile file, @NotNull ResolveSession resolveSession) {
this.file = file;
this.resolveSession = resolveSession;
this.resolveElementCache = new ResolveElementCache(resolveSession, file.getProject());
}
public BindingContext resolveToElement(final JetElement element) {
return computableWithProcessingCancel(new Computable<BindingContext>() {
@Override
public BindingContext compute() {
return resolveSession.resolveElement(element);
return resolveElementCache.resolveElement(element);
}
});
}
@@ -14,21 +14,27 @@
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve.lazy;
package org.jetbrains.jet.plugin.project;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicates;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.di.InjectorForBodyResolve;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
import org.jetbrains.jet.lang.resolve.lazy.LazyDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull;
@@ -44,32 +50,39 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;
class ResolveElementCache {
@SuppressWarnings("unchecked")
private static final BodyResolveContextForLazy EMPTY_CONTEXT = new BodyResolveContextForLazy((com.google.common.base.Function) Functions.constant(null));
public class ResolveElementCache {
private static final BodyResolveContextForLazy EMPTY_CONTEXT = new BodyResolveContextForLazy(Functions.<JetScope>constant(null));
private final CachedValue<MemoizedFunctionToNotNull<JetElement, BindingContext>> additionalResolveCache;
private final ResolveSession resolveSession;
private final MemoizedFunctionToNotNull<JetElement, BindingContext> resolveElementCache;
ResolveElementCache(ResolveSession resolveSession) {
public ResolveElementCache(ResolveSession resolveSession, Project project) {
this.resolveSession = resolveSession;
this.resolveElementCache = resolveSession.getStorageManager().createMemoizedFunction(new Function<JetElement, BindingContext>() {
@Override
public BindingContext fun(JetElement namedFunction) {
return computeResolveElement(namedFunction);
}
}, StorageManager.ReferenceKind.WEAK);
// Recreate internal cache after change of modification count
this.additionalResolveCache =
CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider<MemoizedFunctionToNotNull<JetElement, BindingContext>>() {
@Nullable
@Override
public Result<MemoizedFunctionToNotNull<JetElement, BindingContext>> compute() {
StorageManager manager = ResolveElementCache.this.resolveSession.getStorageManager();
MemoizedFunctionToNotNull<JetElement, BindingContext> elementsCacheFunction =
manager.createMemoizedFunction(new com.intellij.util.Function<JetElement, BindingContext>() {
@Override
public BindingContext fun(JetElement jetElement) {
return elementAdditionalResolve(jetElement);
}
}, StorageManager.ReferenceKind.WEAK);
return Result.create(elementsCacheFunction, PsiModificationTracker.MODIFICATION_COUNT);
}
},
false);
}
@NotNull
BindingContext resolveElement(@NotNull JetElement jetElement) {
return resolveElementCache.fun(jetElement);
}
@NotNull
private BindingContext computeResolveElement(@NotNull JetElement jetElement) {
@SuppressWarnings("unchecked") PsiElement resolveElement = JetPsiUtil.getTopmostParentOfTypes(
public BindingContext resolveElement(@NotNull JetElement jetElement) {
@SuppressWarnings("unchecked") JetElement elementOfAdditionalResolve = (JetElement) JetPsiUtil.getTopmostParentOfTypes(
jetElement,
JetNamedFunction.class,
JetClassInitializer.class,
@@ -81,49 +94,12 @@ class ResolveElementCache {
JetTypeConstraint.class,
JetNamespaceHeader.class);
if (resolveElement != null) {
// All additional resolve should be done to separate trace
BindingTrace trace = resolveSession.getStorageManager().createSafeTrace(
new DelegatingBindingTrace(resolveSession.getBindingContext(), "trace to resolve element", jetElement));
JetFile file = (JetFile) jetElement.getContainingFile();
if (resolveElement instanceof JetNamedFunction) {
functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file);
}
else if (resolveElement instanceof JetClassInitializer) {
initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file);
}
else if (resolveElement instanceof JetProperty) {
propertyAdditionalResolve(resolveSession, (JetProperty) resolveElement, trace, file);
}
else if (resolveElement instanceof JetDelegationSpecifierList) {
delegationSpecifierAdditionalResolve(resolveSession, (JetDelegationSpecifierList) resolveElement, trace, file);
}
else if (resolveElement instanceof JetImportDirective) {
JetImportDirective importDirective = (JetImportDirective) resolveElement;
JetScope scope = resolveSession.getInjector().getScopeProvider().getFileScope((JetFile) importDirective.getContainingFile());
// Get all descriptors to force resolving all imports
scope.getAllDescriptors();
}
else if (resolveElement instanceof JetAnnotationEntry) {
annotationAdditionalResolve(resolveSession, (JetAnnotationEntry) resolveElement);
}
else if (resolveElement instanceof JetTypeParameter) {
typeParameterAdditionalResolve(resolveSession, (JetTypeParameter) resolveElement);
}
else if (resolveElement instanceof JetTypeConstraint) {
typeConstraintAdditionalResolve(resolveSession, jetElement);
}
else if (resolveElement instanceof JetNamespaceHeader) {
namespaceRefAdditionalResolve(resolveSession, (JetNamespaceHeader) resolveElement, trace, jetElement);
}
else {
assert false : "Invalid type of the topmost parent";
if (elementOfAdditionalResolve != null) {
if (elementOfAdditionalResolve instanceof JetNamespaceHeader) {
elementOfAdditionalResolve = jetElement;
}
return trace.getBindingContext();
return additionalResolveCache.getValue().fun(elementOfAdditionalResolve);
}
JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetElement, JetDeclaration.class, false);
@@ -135,11 +111,57 @@ class ResolveElementCache {
return resolveSession.getBindingContext();
}
private static void namespaceRefAdditionalResolve(
ResolveSession resolveSession,
JetNamespaceHeader header, BindingTrace trace, JetElement jetElement
) {
@NotNull
private BindingContext elementAdditionalResolve(@NotNull JetElement resolveElement) {
// All additional resolve should be done to separate trace
BindingTrace trace = resolveSession.getStorageManager().createSafeTrace(
new DelegatingBindingTrace(resolveSession.getBindingContext(), "trace to resolve element", resolveElement));
JetFile file = (JetFile) resolveElement.getContainingFile();
if (resolveElement instanceof JetNamedFunction) {
functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file);
}
else if (resolveElement instanceof JetClassInitializer) {
initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file);
}
else if (resolveElement instanceof JetProperty) {
propertyAdditionalResolve(resolveSession, (JetProperty) resolveElement, trace, file);
}
else if (resolveElement instanceof JetDelegationSpecifierList) {
delegationSpecifierAdditionalResolve(resolveSession, (JetDelegationSpecifierList) resolveElement, trace, file);
}
else if (resolveElement instanceof JetImportDirective) {
JetImportDirective importDirective = (JetImportDirective) resolveElement;
JetScope scope = resolveSession.getInjector().getScopeProvider().getFileScope((JetFile) importDirective.getContainingFile());
// Get all descriptors to force resolving all imports
scope.getAllDescriptors();
}
else if (resolveElement instanceof JetAnnotationEntry) {
annotationAdditionalResolve(resolveSession, (JetAnnotationEntry) resolveElement);
}
else if (resolveElement instanceof JetTypeParameter) {
typeParameterAdditionalResolve(resolveSession, (JetTypeParameter) resolveElement);
}
else if (resolveElement instanceof JetTypeConstraint) {
typeConstraintAdditionalResolve(resolveSession, (JetTypeConstraint) resolveElement);
}
else if (PsiTreeUtil.getParentOfType(resolveElement, JetNamespaceHeader.class) != null) {
namespaceRefAdditionalResolve(resolveSession, trace, resolveElement);
}
else {
assert false : "Invalid type of the topmost parent";
}
return trace.getBindingContext();
}
private static void namespaceRefAdditionalResolve(ResolveSession resolveSession, BindingTrace trace, JetElement jetElement) {
if (jetElement instanceof JetSimpleNameExpression) {
JetNamespaceHeader header = PsiTreeUtil.getParentOfType(jetElement, JetNamespaceHeader.class);
assert header != null;
JetSimpleNameExpression packageNameExpression = (JetSimpleNameExpression) jetElement;
if (trace.getBindingContext().get(BindingContext.RESOLUTION_SCOPE, packageNameExpression) == null) {
JetScope scope = getExpressionMemberScope(resolveSession, packageNameExpression);
@@ -160,8 +182,8 @@ class ResolveElementCache {
}
}
private static void typeConstraintAdditionalResolve(KotlinCodeAnalyzer analyzer, JetElement jetElement) {
JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetElement, JetDeclaration.class);
private static void typeConstraintAdditionalResolve(KotlinCodeAnalyzer analyzer, JetTypeConstraint jetTypeConstraint) {
JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetTypeConstraint, JetDeclaration.class);
DeclarationDescriptor descriptor = analyzer.resolveToDescriptor(declaration);
assert (descriptor instanceof ClassDescriptor);
@@ -212,7 +234,7 @@ class ResolveElementCache {
final JetScope propertyResolutionScope = resolveSession.getInjector().getScopeProvider().getResolutionScopeForDeclaration(
jetProperty);
BodyResolveContextForLazy bodyResolveContext = new BodyResolveContextForLazy(new com.google.common.base.Function<JetDeclaration, JetScope>() {
BodyResolveContextForLazy bodyResolveContext = new BodyResolveContextForLazy(new Function<JetDeclaration, JetScope>() {
@Override
public JetScope apply(JetDeclaration declaration) {
assert declaration.getParent() == jetProperty : "Must be called only for property accessors, but called for " + declaration;
@@ -287,7 +309,7 @@ class ResolveElementCache {
return provider.getResolutionScopeForDeclaration(parentDeclaration);
}
public static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) {
private static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) {
BindingTrace trace = resolveSession.getStorageManager().createSafeTrace(new DelegatingBindingTrace(
resolveSession.getBindingContext(), "trace to resolve a member scope of expression", expression));
@@ -363,9 +385,9 @@ class ResolveElementCache {
private static class BodyResolveContextForLazy implements BodiesResolveContext {
private final com.google.common.base.Function<JetDeclaration, JetScope> declaringScopes;
private final Function<? super JetDeclaration, JetScope> declaringScopes;
private BodyResolveContextForLazy(@NotNull com.google.common.base.Function<JetDeclaration, JetScope> declaringScopes) {
private BodyResolveContextForLazy(@NotNull Function<? super JetDeclaration, JetScope> declaringScopes) {
this.declaringScopes = declaringScopes;
}
@@ -395,8 +417,9 @@ class ResolveElementCache {
}
@Override
public com.google.common.base.Function<JetDeclaration, JetScope> getDeclaringScopes() {
return declaringScopes;
public Function<JetDeclaration, JetScope> getDeclaringScopes() {
//noinspection unchecked
return (Function<JetDeclaration, JetScope>) declaringScopes;
}
@Override