From b3b885b8f95ec15297608c572a899da29c865a54 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Sat, 29 Dec 2012 17:16:11 +0400 Subject: [PATCH] Added no-resolve optimization for top-level functions and properties. --- .../libraries/JetSourceNavigationHelper.java | 224 +++++++++++++++++- 1 file changed, 218 insertions(+), 6 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java index d4f114860ce..a33ed158daa 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java @@ -17,11 +17,14 @@ package org.jetbrains.jet.plugin.libraries; import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -30,7 +33,9 @@ import com.intellij.psi.impl.cache.CacheManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScopes; import com.intellij.psi.search.UsageSearchContext; +import com.intellij.psi.stubs.StringStubIndexExtension; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; @@ -43,14 +48,14 @@ import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.stubindex.JetTopLevelFunctionsFqnNameIndex; +import org.jetbrains.jet.plugin.stubindex.JetTopLevelPropertiesFqnNameIndex; import org.jetbrains.jet.renderer.DescriptorRenderer; import org.jetbrains.jet.util.slicedmap.ReadOnlySlice; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; public class JetSourceNavigationHelper { private JetSourceNavigationHelper() { @@ -119,6 +124,25 @@ public class JetSourceNavigationHelper { return resultScope; } + @NotNull + private static GlobalSearchScope createLibrarySourcesScope(@NotNull JetNamedDeclaration decompiledDeclaration) { + JetFile containingFile = (JetFile) decompiledDeclaration.getContainingFile(); + VirtualFile libraryFile = containingFile.getVirtualFile(); + Project project = decompiledDeclaration.getProject(); + return libraryFile == null ? GlobalSearchScope.EMPTY_SCOPE : createLibrarySourcesScopeForFile(libraryFile, project); + } + + private static List getContainingFiles(@NotNull Iterable declarations) { + Set result = Sets.newHashSet(); + for (JetNamedDeclaration declaration : declarations) { + PsiFile containingFile = declaration.getContainingFile(); + if (containingFile instanceof JetFile) { + result.add((JetFile) containingFile); + } + } + return Lists.newArrayList(result); + } + @NotNull private static List findAllSourceFilesWhichContainIdentifier(@NotNull JetNamedDeclaration jetDeclaration) { VirtualFile libraryFile = jetDeclaration.getContainingFile().getVirtualFile(); @@ -187,16 +211,90 @@ public class JetSourceNavigationHelper { + declarationContainer.getClass().getSimpleName()); } + private static boolean haveRenamesInImports(@NotNull List files) { + for (JetFile file : files) { + for (JetImportDirective importDirective : file.getImportDirectives()) { + if (importDirective.getAliasName() != null) { + return true; + } + } + } + return false; + } + + private static String getTypeShortName(@NotNull JetTypeReference typeReference) { + JetTypeElement typeElement = typeReference.getTypeElement(); + assert typeElement != null; + return typeElement.accept(new JetVisitor() { + @Override + public String visitDeclaration(JetDeclaration declaration, Void data) { + throw new IllegalStateException("This visitor shouldn't be invoked for " + declaration.getClass()); + } + + @Override + public String visitUserType(JetUserType type, Void data) { + JetSimpleNameExpression referenceExpression = type.getReferenceExpression(); + assert referenceExpression != null; + return referenceExpression.getReferencedName(); + } + + @Override + public String visitFunctionType(JetFunctionType type, Void data) { + KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); + int parameterCount = type.getParameters().size(); + + if (type.getReceiverTypeRef() == null) { + return builtIns.getFunction(parameterCount).getName().getName(); + } + else { + return builtIns.getExtensionFunction(parameterCount).getName().getName(); + } + } + + @Override + public String visitNullableType(JetNullableType nullableType, Void data) { + return nullableType.getInnerType().accept(this, null); + } + }, null); + } + + private static boolean typesHaveSameShortName(@NotNull JetTypeReference a, @NotNull JetTypeReference b) { + return getTypeShortName(a).equals(getTypeShortName(b)); + } + + @Nullable + private static Decl getUnambiguousCandidate(@NotNull Collection candidates) { + return candidates.isEmpty() ? null : candidates.iterator().next(); + } + @Nullable private static JetNamedDeclaration getSourcePropertyOrFunction( - @NotNull Decl decompiledDeclaration, - @NotNull NavigationStrategy navigationStrategy + @NotNull final Decl decompiledDeclaration, + @NotNull final NavigationStrategy navigationStrategy ) { String memberNameAsString = decompiledDeclaration.getName(); assert memberNameAsString != null; Name memberName = Name.identifier(memberNameAsString); + if (decompiledDeclaration.getParent() instanceof JetFile) { + Collection candidates = getInitialCandidates(decompiledDeclaration, navigationStrategy); + + candidates = filterByReceiverPresenceAndParametersCount(decompiledDeclaration, navigationStrategy, candidates); + + if (candidates.size() <= 1) { + return getUnambiguousCandidate(candidates); + } + + if (!haveRenamesInImports(getContainingFiles(candidates))) { + candidates = filterByReceiverAndParameterTypes(decompiledDeclaration, navigationStrategy, candidates); + + if (candidates.size() <= 1) { + return getUnambiguousCandidate(candidates); + } + } + } + Pair contextAndMemberScope = getBindingContextAndMemberScopeForLibrarySources(decompiledDeclaration); if (contextAndMemberScope == null) return null; BindingContext bindingContext = contextAndMemberScope.first; @@ -215,6 +313,65 @@ public class JetSourceNavigationHelper { return null; } + @NotNull + private static Collection getInitialCandidates( + @NotNull Decl decompiledDeclaration, + @NotNull NavigationStrategy navigationStrategy + ) { + FqName memberFqName = JetPsiUtil.getFQName(decompiledDeclaration); + assert memberFqName != null; + + GlobalSearchScope librarySourcesScope = createLibrarySourcesScope(decompiledDeclaration); + if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code + return Collections.emptyList(); + } + StringStubIndexExtension index = navigationStrategy.getIndexForTopLevelMembers(); + return index.get(memberFqName.getFqName(), decompiledDeclaration.getProject(), librarySourcesScope); + } + + @NotNull + private static List filterByReceiverPresenceAndParametersCount( + final @NotNull Decl decompiledDeclaration, + final @NotNull NavigationStrategy navigationStrategy, + @NotNull Collection candidates + ) { + final JetTypeReference decompiledReceiver = navigationStrategy.getReceiverType(decompiledDeclaration); + + return ContainerUtil.filter(candidates, new Condition() { + @Override + public boolean value(Decl candidate) { + boolean sameReceiverPresence = (navigationStrategy.getReceiverType(candidate) != null) == + (decompiledReceiver != null); + boolean sameParameterCount = navigationStrategy.declarationsMatchByParameterCount(candidate, decompiledDeclaration); + return sameReceiverPresence && sameParameterCount; + } + }); + } + + @NotNull + private static List filterByReceiverAndParameterTypes( + final @NotNull Decl decompiledDeclaration, + final @NotNull NavigationStrategy navigationStrategy, + @NotNull Collection candidates + ) { + final JetTypeReference decompiledReceiver = navigationStrategy.getReceiverType(decompiledDeclaration); + + return ContainerUtil.filter(candidates, new Condition() { + @Override + public boolean value(Decl candidate) { + if (decompiledReceiver != null) { + JetTypeReference candidateReceiver = navigationStrategy.getReceiverType(candidate); + assert candidateReceiver != null; + if (!typesHaveSameShortName(decompiledReceiver, candidateReceiver)) { + return false; + } + } + + return navigationStrategy.declarationsMatchByParameterTypes(candidate, decompiledDeclaration); + } + }); + } + private static boolean receiversMatch( @Nullable JetTypeReference receiverTypeRef, @Nullable ReceiverParameterDescriptor receiverParameter @@ -239,14 +396,47 @@ public class JetSourceNavigationHelper { } private interface NavigationStrategy { + boolean declarationsMatchByParameterCount(@NotNull Decl a, @NotNull Decl b); + + boolean declarationsMatchByParameterTypes(@NotNull Decl a, @NotNull Decl b); + boolean declarationAndDescriptorMatch(@NotNull Decl declaration, @NotNull Descr descriptor); @NotNull Collection getCandidateDescriptors(@NotNull JetScope scope, @NotNull Name name); @Nullable JetTypeReference getReceiverType(@NotNull Decl declaration); + + @NotNull + StringStubIndexExtension getIndexForTopLevelMembers(); } private static class FunctionNavigationStrategy implements NavigationStrategy { + @Override + public boolean declarationsMatchByParameterCount(@NotNull JetNamedFunction a, @NotNull JetNamedFunction b) { + return a.getValueParameters().size() == b.getValueParameters().size(); + } + + @Override + public boolean declarationsMatchByParameterTypes(@NotNull JetNamedFunction a, @NotNull JetNamedFunction b) { + List aParameters = a.getValueParameters(); + List bParameters = b.getValueParameters(); + if (aParameters.size() != bParameters.size()) { + return false; + } + for (int i = 0; i < aParameters.size(); i++) { + JetTypeReference aType = aParameters.get(i).getTypeReference(); + JetTypeReference bType = bParameters.get(i).getTypeReference(); + + assert aType != null; + assert bType != null; + + if (!typesHaveSameShortName(aType, bType)) { + return false; + } + } + return true; + } + @Override public boolean declarationAndDescriptorMatch(@NotNull JetNamedFunction declaration, @NotNull FunctionDescriptor descriptor) { List declarationParameters = declaration.getValueParameters(); @@ -289,9 +479,25 @@ public class JetSourceNavigationHelper { public JetTypeReference getReceiverType(@NotNull JetNamedFunction declaration) { return declaration.getReceiverTypeRef(); } + + @NotNull + @Override + public StringStubIndexExtension getIndexForTopLevelMembers() { + return JetTopLevelFunctionsFqnNameIndex.getInstance(); + } } private static class PropertyNavigationStrategy implements NavigationStrategy { + @Override + public boolean declarationsMatchByParameterCount(@NotNull JetProperty a, @NotNull JetProperty b) { + return true; + } + + @Override + public boolean declarationsMatchByParameterTypes(@NotNull JetProperty a, @NotNull JetProperty b) { + return true; + } + @Override public boolean declarationAndDescriptorMatch(@NotNull JetProperty declaration, @NotNull VariableDescriptor descriptor) { return true; @@ -308,5 +514,11 @@ public class JetSourceNavigationHelper { public JetTypeReference getReceiverType(@NotNull JetProperty declaration) { return declaration.getReceiverTypeRef(); } + + @NotNull + @Override + public StringStubIndexExtension getIndexForTopLevelMembers() { + return JetTopLevelPropertiesFqnNameIndex.getInstance(); + } } }