From bc7bbf4866dbab59134f350f691e3a6d33126d4c Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 12 Oct 2016 02:01:14 +0300 Subject: [PATCH] search java direct inheritors using javac indices (initial): do not process lots of non-direct inheritors for library classes (IDEA-CR-14364) --- .../compiler/CompilerReferenceReader.java | 15 ++--- .../CompilerReferenceServiceImpl.java | 55 ++++++++--------- .../intellij/compiler/CompilerElement.java | 22 +++++++ .../compiler/CompilerSearchAdapter.java | 2 +- .../JavaBaseCompilerSearchAdapter.java | 60 ++++--------------- ...tionalExpressionCompilerSearchAdapter.java | 5 +- .../testHierarchyOfLibClass/Foo.java | 14 +++++ .../compiler/CompilerReferencesTest.java | 18 ++++-- 8 files changed, 95 insertions(+), 96 deletions(-) create mode 100644 java/java-tests/testData/compiler/bytecodeReferences/testHierarchyOfLibClass/Foo.java diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java index 255c42dc56e8..47edd0c7b13f 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceReader.java @@ -88,17 +88,11 @@ class CompilerReferenceReader { @NotNull GlobalSearchScope searchScope, @NotNull GlobalSearchScope dirtyScope, @NotNull Project project, - FileType fileType) { + @NotNull FileType fileType) { if (classSearchElementInfo == null) return null; + LOG.assertTrue(classSearchElementInfo.searchElements.length == 1); - CompilerBackwardReferenceIndex.LightDefinition[] candidates = - Stream.of(classSearchElementInfo.searchElements) - .map(this::asLightUsage) - .map(myIndex.getBackwardHierarchyMap()::get) - .filter(Objects::nonNull) - .flatMap(Collection::stream) - .toArray(CompilerBackwardReferenceIndex.LightDefinition[]::new); - + Collection candidates = myIndex.getBackwardHierarchyMap().get(asLightUsage(classSearchElementInfo.searchElements[0])); if (candidates == null) return Couple.of(Collections.emptyMap(), Collections.emptyMap()); Set> suitableClasses = new THashSet<>(); @@ -111,7 +105,8 @@ class CompilerReferenceReader { final GlobalSearchScope effectiveSearchScope = GlobalSearchScope.notScope(dirtyScope).intersectWith(searchScope); - Map> candidatesPerFile = Stream.of(candidates) + Map> candidatesPerFile = candidates + .stream() .filter(def -> suitableClasses.contains(def.getUsage().getClass())) .map(definition -> { final VirtualFile file = findFile(definition.getFileId()); diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java index 6a7b673328cb..76147e498eaf 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java @@ -25,17 +25,15 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.roots.impl.LibraryScopeCache; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.*; import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileBasedIndex; @@ -204,9 +202,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple @NotNull GlobalSearchScope searchScope, @NotNull ClassResolvingCompilerSearchAdapter inheritorSearchAdapter, @NotNull FileType searchFileType) { - if (!isServiceEnabled() || - InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile()) || - !myProjectFileIndex.isInSourceContent(aClass.getContainingFile().getVirtualFile())) return null; + if (!isServiceEnabled() || InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile())) return null; Couple> directInheritorsAndCandidates = CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(calculateDirectInheritors(aClass, @@ -217,7 +213,11 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple this)); if (directInheritorsAndCandidates == null) return null; - return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, myDirtyModulesHolder.getDirtyScope(), searchScope); + GlobalSearchScope dirtyScope = myDirtyModulesHolder.getDirtyScope(); + if (ElementPlace.LIB == ElementPlace.get(aClass.getContainingFile().getVirtualFile(), myProjectFileIndex)) { + dirtyScope = dirtyScope.union(LibraryScopeCache.getInstance(myProject).getLibrariesOnlyScope()); + } + return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, dirtyScope, searchScope); } private boolean isServiceEnabled() { @@ -227,8 +227,8 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple private Couple> calculateDirectInheritors(@NotNull PsiNamedElement aClass, @NotNull ClassResolvingCompilerSearchAdapter searchAdapter, @NotNull GlobalSearchScope useScope, - FileType searchFileType) { - final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, searchAdapter); + @NotNull FileType searchFileType) { + final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, searchAdapter, false); synchronized (myLock) { if (myReader == null) return null; return myReader.getDirectInheritors(aClass, @@ -252,7 +252,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple @Nullable private TIntHashSet getReferentFileIds(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) { - final CompilerElementInfo compilerElementInfo = asCompilerElements(element, adapter); + final CompilerElementInfo compilerElementInfo = asCompilerElements(element, adapter, true); if (compilerElementInfo == null) return null; synchronized (myLock) { @@ -268,27 +268,24 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple } @Nullable - private CompilerElementInfo asCompilerElements(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) { - final PsiFile file = element.getContainingFile(); - if (file == null) return null; - final VirtualFile vFile = file.getVirtualFile(); - if (vFile == null) return null; - - ElementPlace place = ElementPlace.get(vFile, myProjectFileIndex); - if (place == null) { + private CompilerElementInfo asCompilerElements(@NotNull PsiElement psiElement, @NotNull CompilerSearchAdapter adapter, boolean buildHierarchyForLibraryElements) { + VirtualFile file = PsiUtilCore.getVirtualFile(psiElement); + ElementPlace place = ElementPlace.get(file, myProjectFileIndex); + if (place == null || (place == ElementPlace.SRC && myDirtyModulesHolder.contains(file))) { return null; } - if (myDirtyModulesHolder.contains(vFile)) { - return null; - } - if (place == ElementPlace.SRC) { - final CompilerElement compilerElement = adapter.asCompilerElement(element); - return compilerElement == null ? null : new CompilerElementInfo(place, compilerElement); + final CompilerElement compilerElement = adapter.asCompilerElement(psiElement); + if (compilerElement == null) return null; + if (place == ElementPlace.LIB && buildHierarchyForLibraryElements) { + final CompilerElement[] elements = adapter.getHierarchyRestrictedToLibrariesScope(compilerElement, psiElement); + final CompilerElement[] fullHierarchy = new CompilerElement[elements.length + 1]; + fullHierarchy[0] = compilerElement; + System.arraycopy(elements, 0, fullHierarchy, 1, elements.length); + return new CompilerElementInfo(place, fullHierarchy); } else { - final CompilerElement[] elements = adapter.libraryElementAsCompilerElements(element); - return elements.length == 0 ? null : new CompilerElementInfo(place, elements); + return new CompilerElementInfo(place, compilerElement); } } @@ -329,8 +326,8 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple SRC, LIB; private static ElementPlace get(VirtualFile file, ProjectFileIndex index) { - return index.isInSourceContent(file) ? SRC : - ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null); + if (file == null) return null; + return index.isInSourceContent(file) ? SRC : ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null); } } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerElement.java b/java/java-indexing-impl/src/com/intellij/compiler/CompilerElement.java index 361b6739211e..b9a70c61e8ed 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerElement.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/CompilerElement.java @@ -18,6 +18,8 @@ package com.intellij.compiler; public abstract class CompilerElement { public static final CompilerElement[] EMPTY_ARRAY = new CompilerElement[0]; + public abstract CompilerElement override(String overriderName); + public static class CompilerMethod extends CompilerElement { private final String myJavacClassName; private final String myJavacMethodName; @@ -40,6 +42,11 @@ public abstract class CompilerElement { public int getJavacParameterCount() { return myJavacParameterCount; } + + @Override + public CompilerElement override(String overriderName) { + return new CompilerMethod(overriderName, myJavacMethodName, myJavacParameterCount); + } } public static class CompilerClass extends CompilerElement { @@ -52,6 +59,11 @@ public abstract class CompilerElement { public String getJavacName() { return myJavacName; } + + @Override + public CompilerElement override(String overriderName) { + return new CompilerClass(myJavacName); + } } public static class CompilerField extends CompilerElement { @@ -70,6 +82,11 @@ public abstract class CompilerElement { public String getJavacName() { return myJavacName; } + + @Override + public CompilerElement override(String overriderName) { + return new CompilerField(overriderName, myJavacName); + } } public static class CompilerFunExpr extends CompilerElement { @@ -82,5 +99,10 @@ public abstract class CompilerElement { public String getJavacClassName() { return myJavacClassName; } + + @Override + public CompilerElement override(String overriderName) { + throw new UnsupportedOperationException(); + } } } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/CompilerSearchAdapter.java index e25ff5179c7e..12919b0d476e 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerSearchAdapter.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/CompilerSearchAdapter.java @@ -27,5 +27,5 @@ public interface CompilerSearchAdapter { CompilerElement asCompilerElement(@NotNull PsiElement psi); @NotNull - CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement psi); + CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi); } diff --git a/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java index eb3080153c7f..1067021c5673 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/JavaBaseCompilerSearchAdapter.java @@ -26,18 +26,15 @@ import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.PsiFileWithStubSupport; import com.intellij.psi.search.searches.ClassInheritorsSearch; -import com.intellij.psi.stubs.StubBase; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubTree; import com.intellij.psi.util.ClassUtil; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.function.Function; public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSearchAdapter { public static final JavaBaseCompilerSearchAdapter INSTANCE = new JavaBaseCompilerSearchAdapter(); @@ -82,43 +79,18 @@ public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSear @NotNull @Override - public CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement element) { - if (mayBeVisibleOutsideOwnerFile(element)) { - if (element instanceof PsiField || element instanceof PsiMethod) { - final String name = ((PsiMember)element).getName(); - - final Function builder; - if (element instanceof PsiField) { - builder = (ownerJvmName) -> new CompilerElement.CompilerField(ownerJvmName, name); - } - else { - final int parametersCount = ((PsiMethod)element).getParameterList().getParametersCount(); - builder = (ownerJvmName) -> new CompilerElement.CompilerMethod(ownerJvmName, name, parametersCount); - } - - final List result = new ArrayList<>(); - inLibrariesHierarchy(((PsiMember)element).getContainingClass(), aClass -> { - final String jvmClassName = ClassUtil.getJVMClassName(aClass); - if (jvmClassName != null) { - result.add(builder.apply(jvmClassName)); - } - return true; - }); - return result.toArray(new CompilerElement[result.size()]); - } - else if (element instanceof PsiClass) { - final List result = new ArrayList<>(); - inLibrariesHierarchy((PsiClass)element, aClass -> { - final String jvmClassName = ClassUtil.getJVMClassName(aClass); - if (jvmClassName != null) { - result.add(new CompilerElement.CompilerClass(jvmClassName)); - } - return true; - }); - return result.toArray(new CompilerElement[result.size()]); - } - } - return CompilerElement.EMPTY_ARRAY; + public CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi) { + final PsiClass baseClass = ObjectUtils.notNull(baseLibraryPsi instanceof PsiClass ? (PsiClass)baseLibraryPsi : ((PsiMember)baseLibraryPsi).getContainingClass()); + final List overridden = new ArrayList<>(); + Processor processor = c -> { + if (c.hasModifierProperty(PsiModifier.PRIVATE)) return true; + String qName = c.getQualifiedName(); + if (qName == null) return true; + overridden.add(baseLibraryElement.override(qName)); + return true; + }; + ClassInheritorsSearch.search(baseClass, LibraryScopeCache.getInstance(baseClass.getProject()).getLibrariesOnlyScope(), true).forEach(processor); + return overridden.toArray(new CompilerElement[overridden.size()]); } @NotNull @@ -137,14 +109,6 @@ public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSear return true; } - private static void inLibrariesHierarchy(PsiClass aClass, Processor processor) { - if (aClass != null) { - processor.process(aClass); - ClassInheritorsSearch.search(aClass, LibraryScopeCache.getInstance(aClass.getProject()).getLibrariesOnlyScope(), true) - .forEach(processor); - } - } - private static List retrieveMatchedClasses(VirtualFile file, Project project, Collection matchers) { final List result = new ArrayList<>(matchers.size()); PsiFileWithStubSupport psiFile = ObjectUtils.notNull((PsiFileWithStubSupport)PsiManager.getInstance(project).findFile(file)); diff --git a/java/java-indexing-impl/src/com/intellij/compiler/JavaFunctionalExpressionCompilerSearchAdapter.java b/java/java-indexing-impl/src/com/intellij/compiler/JavaFunctionalExpressionCompilerSearchAdapter.java index 07ae67a735c0..2b333626b22f 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/JavaFunctionalExpressionCompilerSearchAdapter.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/JavaFunctionalExpressionCompilerSearchAdapter.java @@ -39,8 +39,7 @@ public class JavaFunctionalExpressionCompilerSearchAdapter implements CompilerSe @NotNull @Override - public CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement psi) { - final CompilerElement element = asCompilerElement(psi); - return element == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[] {element}; + public CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi) { + return CompilerElement.EMPTY_ARRAY; } } diff --git a/java/java-tests/testData/compiler/bytecodeReferences/testHierarchyOfLibClass/Foo.java b/java/java-tests/testData/compiler/bytecodeReferences/testHierarchyOfLibClass/Foo.java new file mode 100644 index 000000000000..129c983452cf --- /dev/null +++ b/java/java-tests/testData/compiler/bytecodeReferences/testHierarchyOfLibClass/Foo.java @@ -0,0 +1,14 @@ +import java.util.List; +import java.util.AbstractList; + +class Foo { + + interface ListImpl extends List { + + } + + abstract static class AbstractListImpl extends AbstractList { + + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java index 64bb0a11ed36..58386645f0d5 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTest.java @@ -20,10 +20,7 @@ import com.intellij.codeInsight.completion.AbstractCompilerAwareTest; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.PsiAnonymousClass; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMember; +import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.SkipSlowTestLocally; @@ -101,17 +98,28 @@ public class CompilerReferencesTest extends AbstractCompilerAwareTest { assertEmpty(candidates); } + public void testHierarchyOfLibClass() { + myFixture.configureByFiles(getName() + "/Foo.java"); + rebuildProject(); + CompilerReferenceService.CompilerDirectInheritorInfo directInheritorInfo = getHierarchyFor(myFixture.getJavaFacade().findClass(CommonClassNames.JAVA_UTIL_LIST)); + PsiClass inheritor = assertOneElement(directInheritorInfo.getDirectInheritors().collect(Collectors.toList())); + assertEquals("Foo.ListImpl", inheritor.getQualifiedName()); + } + private CompilerReferenceService.CompilerDirectInheritorInfo getHierarchyUnderForElementCaret() { final PsiElement atCaret = myFixture.getElementAtCaret(); assertNotNull(atCaret); final PsiClass classAtCaret = PsiTreeUtil.getParentOfType(atCaret, PsiClass.class, false); assertNotNull(classAtCaret); + return getHierarchyFor(classAtCaret); + } + + private CompilerReferenceService.CompilerDirectInheritorInfo getHierarchyFor(PsiClass classAtCaret) { return CompilerReferenceService.getInstance(myFixture.getProject()).getDirectInheritors(classAtCaret, assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class), assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class), JavaBaseCompilerSearchAdapter.INSTANCE, StdFileTypes.JAVA); - } private Set getReferentFilesForElementUnderCaret(CompilerSearchAdapter adapter) {