stub hierarchy: extract API

This commit is contained in:
peter
2016-06-09 18:34:57 +02:00
parent c2021418a0
commit 0ac464856f
11 changed files with 199 additions and 82 deletions
@@ -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) {
@@ -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;
@@ -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");
}
@@ -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);
}
}