mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
stub hierarchy: extract API
This commit is contained in:
+14
-9
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
|
||||
import com.intellij.psi.stubs.StubIndex;
|
||||
import com.intellij.psi.stubsHierarchy.HierarchyService;
|
||||
import com.intellij.psi.stubsHierarchy.stubs.Unit;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
@@ -35,7 +36,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class HierarchyService {
|
||||
public class HierarchyServiceImpl extends HierarchyService {
|
||||
private static final TObjectHashingStrategy<Unit> UNIT_HASHING_STRATEGY = new TObjectHashingStrategy<Unit>() {
|
||||
@Override
|
||||
public int computeHashCode(Unit object) {
|
||||
@@ -47,30 +48,34 @@ public class HierarchyService {
|
||||
return computeHashCode(o1) == computeHashCode(o2);
|
||||
}
|
||||
};
|
||||
private static final SingleClassHierarchy EMPTY_HIERARCHY = new SingleClassHierarchy(Symbol.ClassSymbol.EMPTY_ARRAY);
|
||||
private final Project myProject;
|
||||
private final ProjectFileIndex myFileIndex;
|
||||
|
||||
private volatile SoftReference<NameEnvironment> myNamesCache = null;
|
||||
private final CachedValue<SingleClassHierarchy> myHierarchy ;
|
||||
|
||||
public static HierarchyService instance(@NotNull Project project) {
|
||||
return ServiceManager.getService(project, HierarchyService.class);
|
||||
}
|
||||
|
||||
public HierarchyService(Project project) {
|
||||
public HierarchyServiceImpl(Project project) {
|
||||
myProject = project;
|
||||
myFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
myHierarchy = CachedValuesManager.getManager(project).createCachedValue(
|
||||
() -> CachedValueProvider.Result.create(buildHierarchy(), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT),
|
||||
() -> CachedValueProvider.Result.create(buildHierarchy(), PsiModificationTracker.MODIFICATION_COUNT),
|
||||
false);
|
||||
}
|
||||
|
||||
public SingleClassHierarchy getSingleClassHierarchy() {
|
||||
@Override
|
||||
@NotNull
|
||||
public SingleClassHierarchy getHierarchy() {
|
||||
if (!IndexTree.STUB_HIERARCHY_ENABLED) {
|
||||
return EMPTY_HIERARCHY;
|
||||
}
|
||||
|
||||
synchronized (myHierarchy) { //the calculation is memory-intensive, don't allow multiple threads to do it
|
||||
return myHierarchy.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearHierarchy() {
|
||||
((CachedValueBase)myHierarchy).clear();
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.stubsHierarchy.ClassHierarchy;
|
||||
import com.intellij.psi.stubsHierarchy.SmartClassAnchor;
|
||||
import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
@@ -28,17 +30,17 @@ import java.util.*;
|
||||
/**
|
||||
* Compact representation of total hierarchy of JVM classes.
|
||||
*/
|
||||
public class SingleClassHierarchy {
|
||||
public class SingleClassHierarchy extends ClassHierarchy {
|
||||
private final BitSet myCoveredFiles;
|
||||
private final List<SmartClassAnchor> myCoveredClasses;
|
||||
private final SmartClassAnchor[] myClassAnchors;
|
||||
private final SmartClassAnchor[] myClassAnchorsByFileIds;
|
||||
private final List<StubClassAnchor> myCoveredClasses;
|
||||
private final StubClassAnchor[] myClassAnchors;
|
||||
private final StubClassAnchor[] myClassAnchorsByFileIds;
|
||||
private int[] mySubtypes;
|
||||
private int[] mySubtypeStarts;
|
||||
|
||||
public SingleClassHierarchy(ClassSymbol[] classSymbols) {
|
||||
myCoveredFiles = calcCoveredFiles(classSymbols);
|
||||
myClassAnchors = ContainerUtil.map2Array(classSymbols, SmartClassAnchor.class, symbol -> symbol.myClassAnchor);
|
||||
myClassAnchors = ContainerUtil.map2Array(classSymbols, StubClassAnchor.class, symbol -> symbol.myClassAnchor);
|
||||
myClassAnchorsByFileIds = mkByFileId(myClassAnchors);
|
||||
connectSubTypes(classSymbols);
|
||||
myCoveredClasses = Collections.unmodifiableList(ContainerUtil.filter(myClassAnchors, this::isCovered));
|
||||
@@ -58,57 +60,57 @@ public class SingleClassHierarchy {
|
||||
return coveredFiles;
|
||||
}
|
||||
|
||||
private boolean isCovered(@NotNull SmartClassAnchor anchor) {
|
||||
private boolean isCovered(@NotNull StubClassAnchor anchor) {
|
||||
return myCoveredFiles.get(anchor.myFileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of pointers to all classes in this project that where all supertype references were resolved
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public List<SmartClassAnchor> getCoveredClasses() {
|
||||
public List<StubClassAnchor> getCoveredClasses() {
|
||||
return myCoveredClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of pointers to all classes in this project, indexed by stub hierarchy support
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public List<SmartClassAnchor> getAllClasses() {
|
||||
public List<StubClassAnchor> getAllClasses() {
|
||||
return Collections.unmodifiableList(Arrays.asList(myClassAnchors));
|
||||
}
|
||||
|
||||
public SmartClassAnchor[] getDirectSubtypes(PsiClass psiClass) {
|
||||
@NotNull
|
||||
@Override
|
||||
public SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull PsiClass psiClass) {
|
||||
int fileId = Math.abs(FileBasedIndex.getFileId(psiClass.getContainingFile().getVirtualFile()));
|
||||
SmartClassAnchor anchor = forPsiClass(fileId, psiClass);
|
||||
return anchor == null ? SmartClassAnchor.EMPTY_ARRAY : getDirectSubtypes(anchor);
|
||||
return anchor == null ? StubClassAnchor.EMPTY_ARRAY : getDirectSubtypeCandidates(anchor);
|
||||
}
|
||||
|
||||
public SmartClassAnchor[] getDirectSubtypes(@NotNull SmartClassAnchor anchor) {
|
||||
int symbolId = anchor.myId;
|
||||
@NotNull
|
||||
@Override
|
||||
public SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull SmartClassAnchor anchor) {
|
||||
int symbolId = ((StubClassAnchor)anchor).myId;
|
||||
int start = subtypeStart(symbolId);
|
||||
int end = subtypeEnd(symbolId);
|
||||
int length = end - start;
|
||||
if (length == 0) {
|
||||
return SmartClassAnchor.EMPTY_ARRAY;
|
||||
return StubClassAnchor.EMPTY_ARRAY;
|
||||
}
|
||||
SmartClassAnchor[] result = new SmartClassAnchor[length];
|
||||
StubClassAnchor[] result = new StubClassAnchor[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = myClassAnchors[mySubtypes[start + i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public SmartClassAnchor[] getAllSubtypes(PsiClass base) {
|
||||
public StubClassAnchor[] getAllSubtypes(PsiClass base) {
|
||||
int fileId = Math.abs(FileBasedIndex.getFileId(base.getContainingFile().getVirtualFile()));
|
||||
|
||||
TIntHashSet resultIds = new TIntHashSet();
|
||||
TIntHashSet processed = new TIntHashSet();
|
||||
TIntStack queue = new TIntStack();
|
||||
|
||||
SmartClassAnchor baseAnchor = forPsiClass(fileId, base);
|
||||
StubClassAnchor baseAnchor = forPsiClass(fileId, base);
|
||||
if (baseAnchor == null) {
|
||||
return SmartClassAnchor.EMPTY_ARRAY;
|
||||
return StubClassAnchor.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
queue.push(baseAnchor.myId);
|
||||
@@ -127,18 +129,18 @@ public class SingleClassHierarchy {
|
||||
}
|
||||
}
|
||||
int[] allIds = resultIds.toArray();
|
||||
SmartClassAnchor[] result = new SmartClassAnchor[allIds.length];
|
||||
StubClassAnchor[] result = new StubClassAnchor[allIds.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = myClassAnchors[allIds[i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static SmartClassAnchor[] mkByFileId(final SmartClassAnchor[] classAnchors) {
|
||||
SmartClassAnchor[] result = new SmartClassAnchor[classAnchors.length];
|
||||
SmartClassAnchor lastProcessedAnchor = null;
|
||||
private static StubClassAnchor[] mkByFileId(final StubClassAnchor[] classAnchors) {
|
||||
StubClassAnchor[] result = new StubClassAnchor[classAnchors.length];
|
||||
StubClassAnchor lastProcessedAnchor = null;
|
||||
int i = 0;
|
||||
for (SmartClassAnchor classAnchor : classAnchors) {
|
||||
for (StubClassAnchor classAnchor : classAnchors) {
|
||||
if (lastProcessedAnchor == null || lastProcessedAnchor.myFileId != classAnchor.myFileId) {
|
||||
result[i++] = classAnchor;
|
||||
}
|
||||
@@ -206,14 +208,14 @@ public class SingleClassHierarchy {
|
||||
return (nameId + 1 >= mySubtypeStarts.length) ? mySubtypes.length : mySubtypeStarts[nameId + 1];
|
||||
}
|
||||
|
||||
private SmartClassAnchor forPsiClass(int fileId, PsiClass psiClass) {
|
||||
SmartClassAnchor anchor = getFirst(fileId, myClassAnchorsByFileIds);
|
||||
private StubClassAnchor forPsiClass(int fileId, PsiClass psiClass) {
|
||||
StubClassAnchor anchor = getFirst(fileId, myClassAnchorsByFileIds);
|
||||
if (anchor == null) {
|
||||
return null;
|
||||
}
|
||||
int id = anchor.myId;
|
||||
while (id < myClassAnchors.length) {
|
||||
SmartClassAnchor candidate = myClassAnchors[id];
|
||||
StubClassAnchor candidate = myClassAnchors[id];
|
||||
if (candidate.myFileId != fileId)
|
||||
return null;
|
||||
if (psiClass.isEquivalentTo(candidate.retrieveClass(psiClass.getProject())))
|
||||
@@ -223,7 +225,7 @@ public class SingleClassHierarchy {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static SmartClassAnchor getFirst(int fileId, SmartClassAnchor[] byFileIds) {
|
||||
private static StubClassAnchor getFirst(int fileId, StubClassAnchor[] byFileIds) {
|
||||
int lo = 0;
|
||||
int hi = byFileIds.length - 1;
|
||||
while (lo <= hi) {
|
||||
|
||||
+15
-9
@@ -28,36 +28,42 @@ import com.intellij.psi.impl.source.PsiFileWithStubSupport;
|
||||
import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubTree;
|
||||
import com.intellij.psi.stubsHierarchy.SmartClassAnchor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SmartClassAnchor {
|
||||
public static final SmartClassAnchor[] EMPTY_ARRAY = new SmartClassAnchor[0];
|
||||
public class StubClassAnchor extends SmartClassAnchor {
|
||||
public static final StubClassAnchor[] EMPTY_ARRAY = new StubClassAnchor[0];
|
||||
|
||||
public final int myId;
|
||||
public final int myFileId;
|
||||
final int myStubId;
|
||||
|
||||
SmartClassAnchor(int symbolId, ClassAnchor classAnchor) {
|
||||
StubClassAnchor(int symbolId, ClassAnchor classAnchor) {
|
||||
myId = symbolId;
|
||||
myFileId = classAnchor.myFileId;
|
||||
myStubId = classAnchor.myStubId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
VirtualFile retrieveFile() {
|
||||
public VirtualFile retrieveFile() {
|
||||
VirtualFile file = PersistentFS.getInstance().findFileById(myFileId);
|
||||
assert file != null;
|
||||
assert file != null : this;
|
||||
return file;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiClass retrieveClass(@NotNull Project project) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(retrieveFile());
|
||||
assert psiFile != null;
|
||||
return (PsiClass)restoreFromStubIndex((PsiFileWithStubSupport)psiFile, myStubId);
|
||||
assert psiFile != null : this;
|
||||
PsiElement element = restoreFromStubIndex((PsiFileWithStubSupport)psiFile, myStubId);
|
||||
if (!(element instanceof PsiClass)) {
|
||||
throw new AssertionError(this + "; " + psiFile);
|
||||
}
|
||||
return (PsiClass)element;
|
||||
}
|
||||
|
||||
private static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, int index) {
|
||||
@@ -86,7 +86,7 @@ public abstract class Symbol {
|
||||
*/
|
||||
public static class ClassSymbol extends Symbol {
|
||||
public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0];
|
||||
public final SmartClassAnchor myClassAnchor;
|
||||
public final StubClassAnchor myClassAnchor;
|
||||
public ClassSymbol[] mySuperClasses;
|
||||
public UnitInfo myUnitInfo;
|
||||
public QualifiedName[] mySuperNames;
|
||||
@@ -94,7 +94,7 @@ public abstract class Symbol {
|
||||
private StubHierarchyConnector myConnector;
|
||||
private boolean myHierarchyIncomplete;
|
||||
|
||||
public ClassSymbol(SmartClassAnchor classAnchor,
|
||||
public ClassSymbol(StubClassAnchor classAnchor,
|
||||
int flags,
|
||||
Symbol owner,
|
||||
QualifiedName fullname,
|
||||
|
||||
@@ -66,8 +66,8 @@ public class Symbols {
|
||||
|
||||
public ClassSymbol enterClass(ClassAnchor classAnchor, int flags, int shortName, Symbol owner, UnitInfo info, QualifiedName[] supers, StubHierarchyConnector connector) {
|
||||
QualifiedName qualifiedName = myNameEnvironment.qualifiedName(owner, shortName);
|
||||
SmartClassAnchor smartClassAnchor = new SmartClassAnchor(myClassSymbols.size(), classAnchor);
|
||||
ClassSymbol c = new ClassSymbol(smartClassAnchor, flags, owner, qualifiedName, shortName, info, supers, connector);
|
||||
StubClassAnchor stubClassAnchor = new StubClassAnchor(myClassSymbols.size(), classAnchor);
|
||||
ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, qualifiedName, shortName, info, supers, connector);
|
||||
myClassSymbols.add(c);
|
||||
putClassByName(c);
|
||||
return c;
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.stubsHierarchy.impl.HierarchyService;
|
||||
import com.intellij.psi.stubsHierarchy.HierarchyService;
|
||||
|
||||
public class BuildStubsHierarchyAction extends InheritanceAction {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubsHierarchy.impl.test.BuildStubsHierarchyAction");
|
||||
@@ -30,11 +30,11 @@ public class BuildStubsHierarchyAction extends InheritanceAction {
|
||||
final Project project = e.getData(CommonDataKeys.PROJECT);
|
||||
if (project == null) return;
|
||||
|
||||
HierarchyService service = HierarchyService.instance(project);
|
||||
HierarchyService service = HierarchyService.getService(project);
|
||||
service.clearHierarchy();
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ReadAction.run(service::getSingleClassHierarchy),
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ReadAction.run(service::getHierarchy),
|
||||
"Building Hierarchy", false, project);
|
||||
LOG.info("Building stub hierarchy took " + (System.currentTimeMillis() - start) + " ms");
|
||||
}
|
||||
|
||||
+11
-23
@@ -25,9 +25,9 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.stubsHierarchy.impl.HierarchyService;
|
||||
import com.intellij.psi.stubsHierarchy.impl.SingleClassHierarchy;
|
||||
import com.intellij.psi.stubsHierarchy.impl.SmartClassAnchor;
|
||||
import com.intellij.psi.stubsHierarchy.ClassHierarchy;
|
||||
import com.intellij.psi.stubsHierarchy.HierarchyService;
|
||||
import com.intellij.psi.stubsHierarchy.SmartClassAnchor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -58,11 +58,11 @@ public class TestStubHierarchyAction extends InheritanceAction {
|
||||
public void run() {
|
||||
LOG.info("TestStubHierarchyAction started");
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
HierarchyService service = HierarchyService.instance(myProject);
|
||||
HierarchyService service = HierarchyService.getService(myProject);
|
||||
service.clearHierarchy();
|
||||
|
||||
indicator.setText("Building hierarchy");
|
||||
SingleClassHierarchy hierarchy = service.getSingleClassHierarchy();
|
||||
ClassHierarchy hierarchy = service.getHierarchy();
|
||||
MultiMap<SmartClassAnchor, SmartClassAnchor> supers = calcSupersMap(hierarchy);
|
||||
|
||||
indicator.setText("Checking");
|
||||
@@ -71,8 +71,8 @@ public class TestStubHierarchyAction extends InheritanceAction {
|
||||
LOG.info("TestStubHierarchyAction finished");
|
||||
}
|
||||
|
||||
private void compareSupers(ProgressIndicator indicator, MultiMap<SmartClassAnchor, SmartClassAnchor> supers, SingleClassHierarchy hierarchy) {
|
||||
List<SmartClassAnchor> anchors = hierarchy.getCoveredClasses();
|
||||
private void compareSupers(ProgressIndicator indicator, MultiMap<SmartClassAnchor, SmartClassAnchor> supers, ClassHierarchy hierarchy) {
|
||||
List<? extends SmartClassAnchor> anchors = hierarchy.getCoveredClasses();
|
||||
for (int i = 0; i < anchors.size(); i++) {
|
||||
indicator.setFraction(i * 1.0 / anchors.size());
|
||||
SmartClassAnchor anchor = anchors.get(i);
|
||||
@@ -81,11 +81,8 @@ public class TestStubHierarchyAction extends InheritanceAction {
|
||||
}
|
||||
|
||||
private void compareSupers(final SmartClassAnchor anchor, final Collection<SmartClassAnchor> superAnchors) {
|
||||
PsiClass subClass = checkRetrieveClass(anchor);
|
||||
List<PsiClass> stubSuperList = ContainerUtil.map(superAnchors, this::checkRetrieveClass);
|
||||
if (subClass == null || stubSuperList.contains(null)) {
|
||||
return;
|
||||
}
|
||||
PsiClass subClass = anchor.retrieveClass(myProject);
|
||||
List<PsiClass> stubSuperList = ContainerUtil.map(superAnchors, (anchor1) -> anchor1.retrieveClass(myProject));
|
||||
|
||||
Set<PsiClass> psiSupers = new HashSet<>(ContainerUtil.filter(getPsiSupers(subClass), psiClass -> !isImplicit(psiClass.getQualifiedName())));
|
||||
Set<PsiClass> stubSupers = new HashSet<>(ContainerUtil.filter(stubSuperList, psiClass -> !isImplicit(psiClass.getQualifiedName())));
|
||||
@@ -112,20 +109,11 @@ public class TestStubHierarchyAction extends InheritanceAction {
|
||||
"groovy.lang.GroovyObject".equals(qname) || "groovy.lang.GroovyObjectSupport".equals(qname);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiClass checkRetrieveClass(SmartClassAnchor anchor) {
|
||||
PsiClass psiClass = anchor.retrieveClass(myProject);
|
||||
if (psiClass == null) {
|
||||
LOG.info("Cannot restore " + anchor);
|
||||
}
|
||||
return psiClass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
MultiMap<SmartClassAnchor, SmartClassAnchor> calcSupersMap(SingleClassHierarchy hierarchy) {
|
||||
MultiMap<SmartClassAnchor, SmartClassAnchor> calcSupersMap(ClassHierarchy hierarchy) {
|
||||
MultiMap<SmartClassAnchor, SmartClassAnchor> supers = MultiMap.create();
|
||||
for (SmartClassAnchor aClass : hierarchy.getAllClasses()) {
|
||||
for (SmartClassAnchor subtype : hierarchy.getDirectSubtypes(aClass)) {
|
||||
for (SmartClassAnchor subtype : hierarchy.getDirectSubtypeCandidates(aClass)) {
|
||||
supers.putValue(subtype, aClass);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user