mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
java class inheritor search: avoid expensive super type resolve
if there's only one suitable class with the required name in the project anyway
This commit is contained in:
+12
-15
@@ -5,7 +5,6 @@ import com.intellij.compiler.CompilerDirectHierarchyInfo;
|
||||
import com.intellij.compiler.CompilerReferenceService;
|
||||
import com.intellij.concurrency.JobLauncher;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -178,20 +177,20 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
@NotNull SearchScope useScope) {
|
||||
DumbService dumbService = DumbService.getInstance(project);
|
||||
GlobalSearchScope globalUseScope = dumbService.runReadActionInSmartMode(
|
||||
() -> StubHierarchyInheritorSearcher.restrictScope(GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project)));
|
||||
() -> StubHierarchyInheritorSearcher.restrictScope(new JavaSourceFilterScope(GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project))));
|
||||
Collection<PsiReferenceList> candidates =
|
||||
dumbService.runReadActionInSmartMode(() -> JavaSuperClassNameOccurenceIndex.getInstance().get(baseClassName, project, globalUseScope));
|
||||
|
||||
RelaxedDirectInheritorChecker checker = dumbService.runReadActionInSmartMode(() -> new RelaxedDirectInheritorChecker(baseClass));
|
||||
// memory/speed optimisation: it really is a map(string -> PsiClass or List<PsiClass>)
|
||||
final Map<String, Object> classesWithFqn = new HashMap<>();
|
||||
|
||||
processConcurrentlyIfTooMany(candidates,
|
||||
referenceList -> {
|
||||
ProgressManager.checkCanceled();
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
dumbService.runReadActionInSmartMode(() -> {
|
||||
final PsiClass candidate = (PsiClass)referenceList.getParent();
|
||||
boolean isInheritor = candidate.isInheritor(baseClass, false);
|
||||
if (isInheritor) {
|
||||
if (checker.checkInheritance(candidate)) {
|
||||
String fqn = candidate.getQualifiedName();
|
||||
synchronized (classesWithFqn) {
|
||||
Object value = classesWithFqn.get(fqn);
|
||||
@@ -231,16 +230,14 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
Collection<PsiAnonymousClass> anonymousCandidates =
|
||||
dumbService.runReadActionInSmartMode(() -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(baseClassName, project, globalUseScope));
|
||||
|
||||
processConcurrentlyIfTooMany(anonymousCandidates,
|
||||
candidate-> {
|
||||
boolean isInheritor = dumbService.runReadActionInSmartMode(() -> candidate.isInheritor(baseClass, false));
|
||||
if (isInheritor) {
|
||||
synchronized (result) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
processConcurrentlyIfTooMany(anonymousCandidates, candidate-> {
|
||||
if (dumbService.runReadActionInSmartMode(() -> checker.checkInheritance(candidate))) {
|
||||
synchronized (result) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
boolean isEnum = ReadAction.compute(baseClass::isEnum);
|
||||
if (isEnum) {
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.psi.impl.search;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.VolatileNotNullLazyValue;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiModifierList;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil;
|
||||
import com.intellij.psi.search.PsiShortNamesCache;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* To avoid expensive super type resolve, if there's only one suitable class with the required name in the project anyway
|
||||
*/
|
||||
public class RelaxedDirectInheritorChecker {
|
||||
private final String myBaseClassName;
|
||||
private final PsiClass myBaseClass;
|
||||
private final VolatileNotNullLazyValue<PsiClass[]> myClasses;
|
||||
private final VolatileNotNullLazyValue<Boolean> myHasGlobalAmbiguities;
|
||||
private final ProjectFileIndex myFileIndex;
|
||||
|
||||
public RelaxedDirectInheritorChecker(@NotNull PsiClass baseClass) {
|
||||
myBaseClass = baseClass;
|
||||
myBaseClassName = Objects.requireNonNull(baseClass.getName());
|
||||
myClasses = VolatileNotNullLazyValue.createValue(() -> getClassesByName(myBaseClass.getProject(), myBaseClassName));
|
||||
myHasGlobalAmbiguities = VolatileNotNullLazyValue.createValue(() -> hasAmbiguities(JBIterable.of(myClasses.getValue())));
|
||||
myFileIndex = ProjectFileIndex.getInstance(myBaseClass.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiClass[] getClassesByName(Project project, String name) {
|
||||
Map<String, Reference<PsiClass[]>> cache = CachedValuesManager.getManager(project).getCachedValue(project, () -> {
|
||||
Map<String, Reference<PsiClass[]>> map = ContainerUtil.newConcurrentMap();
|
||||
return CachedValueProvider.Result.create(map, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
|
||||
});
|
||||
PsiClass[] result = SoftReference.dereference(cache.get(name));
|
||||
if (result == null) {
|
||||
result = PsiShortNamesCache.getInstance(project).getClassesByName(name, GlobalSearchScope.allScope(project));
|
||||
cache.put(name, new SoftReference<>(result));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static boolean hasAmbiguities(Iterable<PsiClass> classes) {
|
||||
int locals = 0;
|
||||
Set<String> qNames = new HashSet<>();
|
||||
for (PsiClass psiClass : classes) {
|
||||
String qName = psiClass.getQualifiedName();
|
||||
if (qName == null) {
|
||||
locals++;
|
||||
if (locals > 1) return true;
|
||||
} else {
|
||||
qNames.add(qName);
|
||||
if (qNames.size() > 1) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkInheritance(@NotNull PsiClass inheritorCandidate) {
|
||||
if (!inheritorCandidate.isValid() || !myBaseClass.isValid()) return false;
|
||||
if (myFileIndex.isInSourceContent(inheritorCandidate.getContainingFile().getVirtualFile())) {
|
||||
if (!myHasGlobalAmbiguities.getValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GlobalSearchScope scope = inheritorCandidate.getResolveScope();
|
||||
List<PsiClass> accessible = ContainerUtil.findAll(myClasses.getValue(), base ->
|
||||
PsiSearchScopeUtil.isInScope(scope, base) && isAccessibleLight(inheritorCandidate, base));
|
||||
if (!hasAmbiguities(accessible)) {
|
||||
return accessible.contains(myBaseClass);
|
||||
}
|
||||
}
|
||||
|
||||
return inheritorCandidate.isInheritor(myBaseClass, false);
|
||||
}
|
||||
|
||||
private static boolean isAccessibleLight(@NotNull PsiClass inheritorCandidate, @NotNull PsiClass base) {
|
||||
PsiModifierList modifierList = base.getModifierList();
|
||||
if (modifierList != null && PsiUtil.getAccessLevel(modifierList) == PsiUtil.ACCESS_LEVEL_PROTECTED) {
|
||||
return true; // requires hierarchy checks => resolve
|
||||
}
|
||||
|
||||
return JavaResolveUtil.isAccessible(base, base.getContainingClass(), modifierList, inheritorCandidate, null, null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user