PY-21180 Slightly boost resolving imports in projects like Tensorflow by caching findCanonicalImportName()

This commit is contained in:
Mikhail Golubev
2017-03-06 14:03:44 +03:00
parent 2c5949a2bb
commit 851e4b8f79
2 changed files with 49 additions and 51 deletions
@@ -846,20 +846,19 @@ public class PyUtil {
});
}
public static <T, P> T getParameterizedCachedValue(@NotNull PsiElement element, @NotNull P param, @NotNull NotNullFunction<P, T> f) {
final Map<P, T> cache = CachedValuesManager.getCachedValue(element, new CachedValueProvider<Map<P, T>>() {
private static final Object NULL_VALUE = new Object();
public static <T, P> T getParameterizedCachedValue(@NotNull PsiElement element, @Nullable P param, @NotNull NullableFunction<P, T> f) {
final Map<P, Object> cache = CachedValuesManager.getCachedValue(element, new CachedValueProvider<Map<P, Object>>() {
@Nullable
@Override
public Result<Map<P, T>> compute() {
public Result<Map<P, Object>> compute() {
// TODO should be concurrent map instead
return Result.create(Maps.newHashMap(), PsiModificationTracker.MODIFICATION_COUNT);
}
});
T result = cache.get(param);
if (result == null) {
result = f.fun(param);
cache.put(param, result);
}
return result;
final Object wrapped = cache.computeIfAbsent(param, p -> ObjectUtils.notNull(f.fun(param), NULL_VALUE));
//noinspection unchecked
return (T)ObjectUtils.nullizeByCondition(wrapped, NULL_VALUE::equals);
}
/**
@@ -27,10 +27,7 @@ import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -114,49 +111,51 @@ public class QualifiedNameFinder {
*/
@Nullable
public static QualifiedName findCanonicalImportPath(@NotNull PsiElement symbol, @Nullable PsiElement foothold) {
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? (PsiFileSystemItem)symbol : symbol.getContainingFile();
if (srcfile == null) {
return null;
}
VirtualFile virtualFile = srcfile.getVirtualFile();
if (virtualFile == null) {
return null;
}
if (srcfile instanceof PsiFile && symbol instanceof PsiNamedElement && !(symbol instanceof PsiFileSystemItem)) {
PsiElement toplevel = symbol;
if (symbol instanceof PyFunction) {
final PyClass containingClass = ((PyFunction)symbol).getContainingClass();
if (containingClass != null) {
toplevel = containingClass;
}
return PyUtil.getParameterizedCachedValue(symbol, foothold, param -> {
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? (PsiFileSystemItem)symbol : symbol.getContainingFile();
if (srcfile == null) {
return null;
}
PsiDirectory dir = ((PsiFile)srcfile).getContainingDirectory();
while (dir != null) {
PsiFile initPy = dir.findFile(PyNames.INIT_DOT_PY);
if (initPy == null) {
break;
}
if (initPy instanceof PyFile) {
//noinspection ConstantConditions
final List<RatedResolveResult> resolved = ((PyFile)initPy).multiResolveName(((PsiNamedElement)toplevel).getName());
final PsiElement finalTopLevel = toplevel;
if (resolved.stream().anyMatch(r -> r.getElement() == finalTopLevel)) {
virtualFile = dir.getVirtualFile();
VirtualFile virtualFile = srcfile.getVirtualFile();
if (virtualFile == null) {
return null;
}
if (srcfile instanceof PsiFile && symbol instanceof PsiNamedElement && !(symbol instanceof PsiFileSystemItem)) {
PsiElement toplevel = symbol;
if (symbol instanceof PyFunction) {
final PyClass containingClass = ((PyFunction)symbol).getContainingClass();
if (containingClass != null) {
toplevel = containingClass;
}
}
dir = dir.getParentDirectory();
}
}
final QualifiedName qname = findShortestImportableQName(foothold != null ? foothold : symbol, virtualFile);
if (qname != null) {
for (PyCanonicalPathProvider provider : Extensions.getExtensions(PyCanonicalPathProvider.EP_NAME)) {
final QualifiedName restored = provider.getCanonicalPath(qname, foothold);
if (restored != null) {
return restored;
PsiDirectory dir = ((PsiFile)srcfile).getContainingDirectory();
while (dir != null) {
PsiFile initPy = dir.findFile(PyNames.INIT_DOT_PY);
if (initPy == null) {
break;
}
if (initPy instanceof PyFile) {
//noinspection ConstantConditions
final List<RatedResolveResult> resolved = ((PyFile)initPy).multiResolveName(((PsiNamedElement)toplevel).getName());
final PsiElement finalTopLevel = toplevel;
if (resolved.stream().anyMatch(r -> r.getElement() == finalTopLevel)) {
virtualFile = dir.getVirtualFile();
}
}
dir = dir.getParentDirectory();
}
}
}
return qname;
final QualifiedName qname = findShortestImportableQName(param != null ? param : symbol, virtualFile);
if (qname != null) {
for (PyCanonicalPathProvider provider : Extensions.getExtensions(PyCanonicalPathProvider.EP_NAME)) {
final QualifiedName restored = provider.getCanonicalPath(qname, param);
if (restored != null) {
return restored;
}
}
}
return qname;
});
}
@Nullable