stub hierarchy: distinguish "covered" files where the supertype hierarchy is reliable

This commit is contained in:
peter
2016-06-09 18:34:56 +02:00
parent 19e64e26a1
commit 32dad25fb8
4 changed files with 83 additions and 68 deletions
@@ -17,25 +17,65 @@ package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.psi.PsiClass;
import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntStack;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.*;
/**
* Compact representation of total hierarchy of JVM classes.
*/
public class SingleClassHierarchy {
public final SmartClassAnchor[] myClassAnchors;
private final BitSet myCoveredFiles;
private final List<SmartClassAnchor> myCoveredClasses;
private final SmartClassAnchor[] myClassAnchors;
private final SmartClassAnchor[] myClassAnchorsByFileIds;
private int[] mySubtypes;
private int[] mySubtypeStarts;
public SingleClassHierarchy(ClassSymbol[] classSymbols, int symbolCount) {
this.myClassAnchors = mkAnchors(classSymbols, symbolCount);
this.myClassAnchorsByFileIds = mkByFileId(this.myClassAnchors);
public SingleClassHierarchy(ClassSymbol[] classSymbols) {
myCoveredFiles = calcCoveredFiles(classSymbols);
myClassAnchors = ContainerUtil.map2Array(classSymbols, SmartClassAnchor.class, symbol -> symbol.myClassAnchor);
myClassAnchorsByFileIds = mkByFileId(myClassAnchors);
connectSubTypes(classSymbols);
myCoveredClasses = Collections.unmodifiableList(ContainerUtil.filter(myClassAnchors, this::isCovered));
}
@NotNull
private static BitSet calcCoveredFiles(ClassSymbol[] classSymbols) {
BitSet problematicFiles = new BitSet();
BitSet coveredFiles = new BitSet();
for (ClassSymbol symbol : classSymbols) {
coveredFiles.set(symbol.myClassAnchor.myFileId);
if (symbol.isHierarchyIncomplete()) {
problematicFiles.set(symbol.myClassAnchor.myFileId);
}
}
coveredFiles.andNot(problematicFiles);
return coveredFiles;
}
private boolean isCovered(@NotNull SmartClassAnchor anchor) {
return myCoveredFiles.get(anchor.myFileId);
}
/**
* @return the list of pointers to all classes in this project that where all supertype references were resolved
*/
@NotNull
public List<SmartClassAnchor> getCoveredClasses() {
return myCoveredClasses;
}
/**
* @return the list of pointers to all classes in this project, indexed by stub hierarchy support
*/
@NotNull
public List<SmartClassAnchor> getAllClasses() {
return Collections.unmodifiableList(Arrays.asList(myClassAnchors));
}
public SmartClassAnchor[] getDirectSubtypes(PsiClass psiClass) {
@@ -94,14 +134,6 @@ public class SingleClassHierarchy {
return result;
}
private static SmartClassAnchor[] mkAnchors(ClassSymbol[] classSymbols, int symbolCount) {
SmartClassAnchor[] anchors = new SmartClassAnchor[symbolCount];
for (int i = 0; i < symbolCount; i++) {
anchors[i] = classSymbols[i].myClassAnchor;
}
return anchors;
}
private static SmartClassAnchor[] mkByFileId(final SmartClassAnchor[] classAnchors) {
SmartClassAnchor[] result = new SmartClassAnchor[classAnchors.length];
SmartClassAnchor lastProcessedAnchor = null;
@@ -115,24 +147,16 @@ public class SingleClassHierarchy {
// compacting
result = Arrays.copyOf(result, i);
Arrays.sort(result, (o1, o2) -> {
int i1 = o1.myFileId;
int i2 = o2.myFileId;
if (i1 < i2) {
return -1;
} else if (i1 > i2) {
return +1;
} else {
return 0;
}
});
Arrays.sort(result, Comparator.comparing(a -> a.myFileId));
return result;
}
void connectSubTypes(ClassSymbol[] classSymbols, int n) {
int[] sizes = calculateSizes(classSymbols, n);
private void connectSubTypes(ClassSymbol[] classSymbols) {
excludeUncoveredFiles(classSymbols);
int[] starts = new int[n];
int[] sizes = calculateSizes(classSymbols);
int[] starts = new int[classSymbols.length];
int count = 0;
for (int i = 0; i < sizes.length; i++) {
@@ -142,7 +166,7 @@ public class SingleClassHierarchy {
int[] subtypes = new int[count];
int[] filled = new int[sizes.length];
for (int subTypeId = 0; subTypeId < n; subTypeId++) {
for (int subTypeId = 0; subTypeId < classSymbols.length; subTypeId++) {
ClassSymbol subType = classSymbols[subTypeId];
for (ClassSymbol superType : subType.rawSuperClasses()) {
int superTypeId = superType.myClassAnchor.myId;
@@ -155,10 +179,17 @@ public class SingleClassHierarchy {
this.mySubtypeStarts = starts;
}
private static int[] calculateSizes(ClassSymbol[] classSymbols, int n) {
int[] sizes = new int[n];
for (int i = 1; i < n; i++) {
ClassSymbol subType = classSymbols[i];
private void excludeUncoveredFiles(ClassSymbol[] classSymbols) {
for (ClassSymbol symbol : classSymbols) {
if (!isCovered(symbol.myClassAnchor)) {
symbol.markHierarchyIncomplete();
}
}
}
private static int[] calculateSizes(ClassSymbol[] classSymbols) {
int[] sizes = new int[classSymbols.length];
for (ClassSymbol subType : classSymbols) {
for (ClassSymbol superType : subType.rawSuperClasses()) {
int superTypeId = superType.myClassAnchor.myId;
sizes[superTypeId] += 1;
@@ -150,6 +150,9 @@ public abstract class Symbol {
}
void markHierarchyIncomplete() {
mySuperClasses = EMPTY_ARRAY;
mySuperNames = null;
myUnitInfo = null;
myHierarchyIncomplete = true;
}
@@ -7,14 +7,16 @@ import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class Symbols {
public final PackageSymbol myRootPackage;
protected final NameEnvironment myNameEnvironment;
// id -> ClassSymbol
protected ClassSymbol[] myClassSymbols = new ClassSymbol[0x8000];
private List<ClassSymbol> myClassSymbols = new ArrayList<>(0x8000);
// fullName -> PackageSymbol
public final TIntObjectHashMap<PackageSymbol> myPackages = new TIntObjectHashMap<PackageSymbol>();
private final TIntObjectHashMap<PackageSymbol> myPackages = new TIntObjectHashMap<>();
// nameId -> ClassSymbols (used by global resolve)
private Object[] myClassSymbolsByNameId = new Object[0x8000];
@@ -24,9 +26,6 @@ public class Symbols {
myPackages.put(nameEnvironment.empty.myId, myRootPackage);
}
// last used id
protected int id;
public PackageSymbol enterPackage(QualifiedName qualifiedName) {
PackageSymbol p = myPackages.get(qualifiedName.myId);
if (p == null) {
@@ -67,22 +66,13 @@ 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(id++, classAnchor);
SmartClassAnchor smartClassAnchor = new SmartClassAnchor(myClassSymbols.size(), classAnchor);
ClassSymbol c = new ClassSymbol(smartClassAnchor, flags, owner, qualifiedName, shortName, info, supers, connector);
putClass(c);
myClassSymbols.add(c);
putClassByName(c);
return c;
}
private void putClass(ClassSymbol classSymbol) {
putClassById(classSymbol);
putClassByName(classSymbol);
}
private void putClassById(ClassSymbol classSymbol) {
ensureByIdCapacity(classSymbol.myClassAnchor.myId);
myClassSymbols[classSymbol.myClassAnchor.myId] = classSymbol;
}
private void putClassByName(ClassSymbol classSymbol) {
QualifiedName name = classSymbol.myQualifiedName;
// anonymous class
@@ -116,24 +106,14 @@ public class Symbols {
}
}
private void ensureByIdCapacity(int maxIndex) {
if (maxIndex >= myClassSymbols.length) {
int newLength = calculateNewLength(myClassSymbols.length, maxIndex);
ClassSymbol[] result = new ClassSymbol[newLength];
System.arraycopy(myClassSymbols, 0, result, 0, myClassSymbols.length);
myClassSymbols = result;
}
}
private static int calculateNewLength(int currentLength, int maxIndex) {
while (currentLength < maxIndex + 1) currentLength *= 2;
return currentLength;
}
public SingleClassHierarchy createHierarchy() {
SingleClassHierarchy table = new SingleClassHierarchy(myClassSymbols, id);
table.connectSubTypes(myClassSymbols, id);
SingleClassHierarchy createHierarchy() {
ClassSymbol[] array = myClassSymbols.toArray(ClassSymbol.EMPTY_ARRAY);
myClassSymbols = null;
return table;
return new SingleClassHierarchy(array);
}
}
@@ -72,10 +72,11 @@ public class TestStubHierarchyAction extends InheritanceAction {
}
private void compareSupers(ProgressIndicator indicator, MultiMap<SmartClassAnchor, SmartClassAnchor> supers, SingleClassHierarchy hierarchy) {
SmartClassAnchor[] anchors = hierarchy.myClassAnchors;
for (int i = 0; i < anchors.length; i++) {
indicator.setFraction(i * 1.0 / anchors.length);
compareSupers(anchors[i], supers.get(anchors[i]));
List<SmartClassAnchor> anchors = hierarchy.getCoveredClasses();
for (int i = 0; i < anchors.size(); i++) {
indicator.setFraction(i * 1.0 / anchors.size());
SmartClassAnchor anchor = anchors.get(i);
compareSupers(anchor, supers.get(anchor));
}
}
@@ -123,7 +124,7 @@ public class TestStubHierarchyAction extends InheritanceAction {
@NotNull
MultiMap<SmartClassAnchor, SmartClassAnchor> calcSupersMap(SingleClassHierarchy hierarchy) {
MultiMap<SmartClassAnchor, SmartClassAnchor> supers = MultiMap.create();
for (SmartClassAnchor aClass : hierarchy.myClassAnchors) {
for (SmartClassAnchor aClass : hierarchy.getAllClasses()) {
for (SmartClassAnchor subtype : hierarchy.getDirectSubtypes(aClass)) {
supers.putValue(subtype, aClass);
}