don't use PSI in stub hierarchy: it's expensive (especially if it's Kotlin)

This commit is contained in:
peter
2016-06-08 19:25:50 +02:00
parent ed59620c5d
commit 6e013e9be0
4 changed files with 5 additions and 273 deletions
@@ -17,16 +17,12 @@ package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClassOwner;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.stubsHierarchy.stubs.Unit;
import com.intellij.reference.SoftReference;
import org.jetbrains.annotations.NotNull;
public class HierarchyService {
public static boolean PROCESS_PSI = true;
public static boolean IGNORE_LOCAL_CLASSES = true;
public static final boolean IGNORE_LOCAL_CLASSES = true;
private NameEnvironment myNameEnvironment;
private SoftReference<NameEnvironment> myNamesCache = new SoftReference<NameEnvironment>(null);
@@ -34,7 +30,6 @@ public class HierarchyService {
private SingleClassHierarchy mySingleClassHierarchy;
private Symbols mySymbols;
private StubEnter myStubEnter;
private PsiEnter myPsiEnter;
public static HierarchyService instance(Project project) {
return ServiceManager.getService(project, HierarchyService.class);
@@ -51,20 +46,13 @@ public class HierarchyService {
}
}
public void processPsiClassOwner(@NotNull PsiClassOwner psiClassOwner) {
myPsiEnter.enter(psiClassOwner);
}
public void connect1() {
myStubEnter.connect1();
}
public void complete2() {
myStubEnter.connect2();
myPsiEnter.connect();
myStubEnter = null;
myPsiEnter = null;
}
public void connectSubtypes() {
@@ -84,7 +72,6 @@ public class HierarchyService {
}
mySymbols = new Symbols(myNameEnvironment);
myStubEnter = new StubEnter(myNameEnvironment, mySymbols);
myPsiEnter = new PsiEnter(myNameEnvironment, mySymbols);
mySingleClassHierarchy = null;
}
@@ -1,114 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassOwner;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.util.indexing.FileBasedIndex;
import java.util.ArrayList;
import java.util.Arrays;
import static com.intellij.psi.stubsHierarchy.impl.Symbol.*;
public class PsiEnter {
private final Symbols mySymbols;
private final NameEnvironment myNameEnvironment;
private final PsiHierachyConnector myPsiHierachyConnector;
private ArrayList<ClassSymbol> myQueue = new ArrayList<ClassSymbol>();
public PsiEnter(NameEnvironment nameEnvironment, Symbols symbols) {
this.myNameEnvironment = nameEnvironment;
this.mySymbols = symbols;
myPsiHierachyConnector = new PsiHierachyConnector(nameEnvironment, symbols);
}
void enter(final PsiClassOwner psiClassOwner) {
ApplicationManager.getApplication().runReadAction(() -> enterTopLevels(psiClassOwner));
}
private void enterTopLevels(PsiClassOwner owner) {
String pkgName = owner.getPackageName();
PackageSymbol pkg = StringUtil.isEmpty(pkgName) ?
mySymbols.myRootPackage : mySymbols.enterPackage(myNameEnvironment.fromString(pkgName, true));
for (PsiClass psiClass : owner.getClasses()) {
enter(psiClass, pkg);
}
}
private ClassSymbol enter(PsiClass psiClass, Symbol owner) {
if (HierarchyService.IGNORE_LOCAL_CLASSES && psiClass.getName() == null) {
return null;
}
int psiFlags = translateFlags(psiClass);
int fileId = FileBasedIndex.getFileId(psiClass.getContainingFile().getVirtualFile());
ClassAnchor.DirectClassAnchor classAnchor = new ClassAnchor.DirectClassAnchor(fileId, psiClass);
int shortName = myNameEnvironment.simpleName(psiClass.getName(), true);
ClassSymbol classSymbol = mySymbols.enterClass(classAnchor, psiFlags, shortName, owner, null, null, myPsiHierachyConnector);
if (myQueue != null) {
myQueue.add(classSymbol);
}
ClassSymbol[] members = enter(psiClass.getInnerClasses(), classSymbol);
classSymbol.setMembers(members);
return classSymbol;
}
private ClassSymbol[] enter(PsiClass[] psiClasses, Symbol owner) {
ClassSymbol[] members = new ClassSymbol[psiClasses.length];
int i = 0;
for (PsiClass psiClass : psiClasses) {
ClassSymbol member = enter(psiClass, owner);
if (member != null && member.myShortName != 0) {
members[i++] = member;
}
}
members = members.length == 0 ? ClassSymbol.EMPTY_ARRAY : Arrays.copyOf(members, i);
Arrays.sort(members, CLASS_SYMBOL_BY_NAME_COMPARATOR);
return members;
}
public static int translateFlags(PsiClass psiClass) {
int flags = 0;
if (psiClass.isInterface()) {
flags |= IndexTree.INTERFACE;
}
if (psiClass.isEnum()) {
flags |= IndexTree.ENUM;
}
if (psiClass.isAnnotationType()) {
flags |= IndexTree.ANNOTATION;
}
if (psiClass.hasModifierProperty(PsiModifier.STATIC)) {
flags |= IndexTree.STATIC;
}
return flags;
}
public void connect() {
for (ClassSymbol classSymbol : myQueue) {
classSymbol.connect();
}
myQueue = null;
}
}
@@ -1,83 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.psi.PsiClass;
import java.util.ArrayList;
public class PsiHierachyConnector implements HierarchyConnector {
private final NameEnvironment myNameEnvironment;
private final Symbols mySymbols;
protected PsiHierachyConnector(NameEnvironment nameEnvironment, Symbols symbols) {
this.myNameEnvironment = nameEnvironment;
this.mySymbols = symbols;
}
@Override
public void connect(final Symbol sym) {
ApplicationManager.getApplication().runReadAction(() -> connectInternal((Symbol.ClassSymbol)sym));
}
private void connectInternal(Symbol.ClassSymbol sym) {
if (sym.myOwner instanceof Symbol.ClassSymbol) {
((Symbol.ClassSymbol)sym.myOwner).connect();
}
sym.mySuperClasses = getSuperTypes(sym.myClassAnchor);
}
private Symbol.ClassSymbol[] asSymbol(PsiClass psiClass) {
if (psiClass == null) {
return Symbol.ClassSymbol.EMPTY_ARRAY;
}
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName != null) {
QualifiedName n = myNameEnvironment.fromString(qualifiedName, false);
if (n != null) {
return mySymbols.loadClass(n);
}
}
return Symbol.ClassSymbol.EMPTY_ARRAY;
}
Symbol.ClassSymbol[] getSuperTypes(SmartClassAnchor anchor) {
PsiClass psiClass = ((SmartClassAnchor.DirectSmartClassAnchor)anchor).myPsiClass;
PsiClass[] psiInterfaces = psiClass.getInterfaces();
ArrayList<Symbol.ClassSymbol> superList = new ArrayList<Symbol.ClassSymbol>();
for (PsiClass psiInterface : psiInterfaces) {
for (Symbol.ClassSymbol cs : asSymbol(psiInterface)) {
if (cs.myQualifiedName != myNameEnvironment.java_lang_Object) {
superList.add(cs);
}
}
}
PsiClass psiSuperClass = psiClass.getSuperClass();
for (Symbol.ClassSymbol cs : asSymbol(psiSuperClass)) {
if (cs.myQualifiedName != myNameEnvironment.java_lang_Object) {
superList.add(cs);
}
}
if (superList.isEmpty()) {
return Symbol.ClassSymbol.EMPTY_ARRAY;
}
return superList.isEmpty() ? Symbol.ClassSymbol.EMPTY_ARRAY : superList.toArray(new Symbol.ClassSymbol[superList.size()]);
}
}
@@ -20,27 +20,17 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.psi.PsiClassOwner;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
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.util.Processor;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import java.util.BitSet;
import java.util.Set;
public class SingleClassHierarchyBuilder {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubsHierarchy.HierarchyBuilder");
private static final boolean TEST_MEMORY_USAGE = false;
@@ -53,7 +43,6 @@ public class SingleClassHierarchyBuilder {
private static class UnitProcessor implements Processor<IndexTree.Unit> {
private final Project myProject;
private final HierarchyService myHierarchyService;
protected final BitSet myProcessedSet = new BitSet();
private final ProjectFileIndex myProjectIndex;
boolean isInSourceMode;
@@ -72,7 +61,6 @@ public class SingleClassHierarchyBuilder {
boolean process = isInSourceMode ? myProjectIndex.isInSourceContent(file) : myProjectIndex.isInLibraryClasses(file);
if (process) {
myHierarchyService.processUnit(unit);
myProcessedSet.set(unit.myFileId);
}
return true;
}
@@ -97,7 +85,6 @@ public class SingleClassHierarchyBuilder {
final double classes = 0.3;
final double completeClasses = 0.1;
final double sources = 0.3;
final double collectPsiFilesFraction = 0.2;
final UnitProcessor processor = new UnitProcessor(myProject, service);
@@ -126,64 +113,19 @@ public class SingleClassHierarchyBuilder {
});
indicator.setFraction(classes + completeClasses + sources);
// 4. reading PSI
LOG.info("read PSI start");
indicator.setText("Collecting PSI Files");
final Set<VirtualFile> srcSet = new HashSet<VirtualFile>();
collectFiles(srcSet, processor.myProcessedSet);
double total = srcSet.size();
LOG.info("Processing PSI");
indicator.setText("Processing PSI Files");
int loadedCound = 0;
for (final VirtualFile vFile : srcSet) {
String presentableUrl = vFile.getPresentableUrl();
if (HierarchyService.PROCESS_PSI) {
PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
return PsiManager.getInstance(myProject).findFile(vFile);
}
});
if (psiFile instanceof PsiClassOwner) {
service.processPsiClassOwner((PsiClassOwner)psiFile);
LOG.info("PSI: " + presentableUrl);
}
}
loadedCound++;
indicator.setFraction(classes + completeClasses + sources + collectPsiFilesFraction *(loadedCound / total));
}
testMemory("2");
// 5. completing sources + PSI
indicator.setText("Completing sources + PSI");
// 4. completing sources
indicator.setText("Completing sources");
service.complete2();
LOG.info("Complete end");
testMemory("3");
testMemory("2");
indicator.setFraction(0.9);
// 6. connect subtypes
// 5. connect subtypes
indicator.setText("Connecting subtypes");
service.connectSubtypes();
LOG.info("Subtypes connected");
indicator.setFraction(1);
}
private void collectFiles(final Set<VirtualFile> srcSet, final BitSet processed) {
final ProjectFileIndex projectIndex = ProjectFileIndex.SERVICE.getInstance(myProject);
projectIndex.iterateContent(new ContentIterator() {
@Override
public boolean processFile(VirtualFile fileOrDir) {
int fileId = ((VirtualFileWithId)fileOrDir).getId();
if (!processed.get(fileId) && projectIndex.isInSourceContent(fileOrDir)) {
srcSet.add(fileOrDir);
}
return true;
}
});
}
}
private static void testMemory(final String msg) {