Optimize top level class searching in LazyJavaPackageScope

Do not try to search something, that is known not to exist
This commit is contained in:
Denis Zharkov
2016-02-23 12:40:24 +03:00
parent beb24af5ef
commit 3652cd9c18
9 changed files with 77 additions and 3 deletions
@@ -20,9 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.IntArrayList import com.intellij.util.containers.IntArrayList
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import java.util.ArrayList import java.util.*
import java.util.EnumSet
import java.util.HashMap
data class JavaRoot(val file: VirtualFile, val type: JavaRoot.RootType, val prefixFqName: FqName? = null) { data class JavaRoot(val file: VirtualFile, val type: JavaRoot.RootType, val prefixFqName: FqName? = null) {
enum class RootType { enum class RootType {
@@ -99,6 +97,24 @@ class JvmDependenciesIndex(_roots: List<JavaRoot>) {
} }
} }
fun collectKnownClassNamesInPackage(
packageFqName: FqName
): Set<String> {
var result = hashSetOf<String>()
traverseDirectoriesInPackage(packageFqName, continueSearch = {
dir, rootType ->
for (child in dir.children) {
if (child.extension != "class" && child.extension != "java") continue
result.add(child.nameWithoutExtension)
}
true
})
return result
}
private data class HandleResult<T : Any>(val result: T?, val continueSearch: Boolean) private data class HandleResult<T : Any>(val result: T?, val continueSearch: Boolean)
private fun <T : Any> search( private fun <T : Any> search(
@@ -120,6 +120,8 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager)
return findClassInPsiFile(classNameWithInnerClasses, file) return findClassInPsiFile(classNameWithInnerClasses, file)
} }
override fun knownClassNamesInPackage(packageFqName: FqName) = index.collectKnownClassNamesInPackage(packageFqName)
companion object { companion object {
private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java) private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java)
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Set;
public class JavaClassFinderImpl implements JavaClassFinder { public class JavaClassFinderImpl implements JavaClassFinder {
private Project project; private Project project;
@@ -108,4 +109,10 @@ public class JavaClassFinderImpl implements JavaClassFinder {
PsiPackage psiPackage = javaFacade.findPackage(fqName.asString(), javaSearchScope); PsiPackage psiPackage = javaFacade.findPackage(fqName.asString(), javaSearchScope);
return psiPackage == null ? null : new JavaPackageImpl(psiPackage, javaSearchScope); return psiPackage == null ? null : new JavaPackageImpl(psiPackage, javaSearchScope);
} }
@Nullable
@Override
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
return javaFacade.knownClassNamesInPackage(packageFqName);
}
} }
@@ -20,7 +20,10 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.impl.file.impl.JavaFileManager
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
interface KotlinCliJavaFileManager : JavaFileManager { interface KotlinCliJavaFileManager : JavaFileManager {
fun findClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? fun findClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass?
fun knownClassNamesInPackage(packageFqName: FqName): Set<String>?
} }
@@ -40,12 +40,15 @@ import kotlin.collections.ArraysKt;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.ClassId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
public class KotlinJavaPsiFacade { public class KotlinJavaPsiFacade {
@@ -112,6 +115,17 @@ public class KotlinJavaPsiFacade {
return null; return null;
} }
@Nullable
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
KotlinPsiElementFinderWrapper[] finders = finders();
if (finders.length == 1) {
return ((KotlinPsiElementFinderImpl) finders[0]).knownClassNamesInPackage(packageFqName);
}
return null;
}
@NotNull @NotNull
private PsiClass[] findClassesInDumbMode(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { private PsiClass[] findClassesInDumbMode(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
String packageName = StringUtil.getPackageName(qualifiedName); String packageName = StringUtil.getPackageName(qualifiedName);
@@ -328,6 +342,15 @@ public class KotlinJavaPsiFacade {
return findClass(classId.asSingleFqName().asString(), scope); return findClass(classId.asSingleFqName().asString(), scope);
} }
@Nullable
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
if (isCliFileManager) {
return ((KotlinCliJavaFileManager) javaFileManager).knownClassNamesInPackage(packageFqName);
}
return null;
}
@Override @Override
public PsiPackage findPackage(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { public PsiPackage findPackage(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
if (isCliFileManager) { if (isCliFileManager) {
@@ -18,15 +18,22 @@ package org.jetbrains.kotlin.load.java;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.load.java.structure.JavaClass; import org.jetbrains.kotlin.load.java.structure.JavaClass;
import org.jetbrains.kotlin.load.java.structure.JavaPackage; import org.jetbrains.kotlin.load.java.structure.JavaPackage;
import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import java.util.Set;
public interface JavaClassFinder { public interface JavaClassFinder {
@Nullable @Nullable
JavaClass findClass(@NotNull ClassId classId); JavaClass findClass(@NotNull ClassId classId);
@Nullable @Nullable
JavaPackage findPackage(@NotNull FqName fqName); JavaPackage findPackage(@NotNull FqName fqName);
@ReadOnly
@Nullable
Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName);
} }
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.storage.NullableLazyValue
import org.jetbrains.kotlin.storage.getValue import org.jetbrains.kotlin.storage.getValue
class LazyJavaPackageScope( class LazyJavaPackageScope(
@@ -38,6 +39,12 @@ class LazyJavaPackageScope(
override val ownerDescriptor: LazyJavaPackageFragment override val ownerDescriptor: LazyJavaPackageFragment
) : LazyJavaStaticScope(c) { ) : LazyJavaStaticScope(c) {
// Null means that it's impossible to determine list of class names in package, i.e. in IDE where special finders exist
// But for compiler though we can determine full list of class names by getting all class-file names in classpath and sources
private val knownClassNamesInPackage: NullableLazyValue<Set<String>> = c.storageManager.createNullableLazyValue {
c.components.finder.knownClassNamesInPackage(ownerDescriptor.fqName)
}
private val partToFacade = c.storageManager.createLazyValue { private val partToFacade = c.storageManager.createLazyValue {
val result = hashMapOf<String, String>() val result = hashMapOf<String, String>()
kotlinClasses@for (kotlinClass in ownerDescriptor.kotlinBinaryClasses) { kotlinClasses@for (kotlinClass in ownerDescriptor.kotlinBinaryClasses) {
@@ -88,6 +95,12 @@ class LazyJavaPackageScope(
if (!SpecialNames.isSafeIdentifier(name)) return null if (!SpecialNames.isSafeIdentifier(name)) return null
recordLookup(name, location) recordLookup(name, location)
val knownClassNamesInPackage = knownClassNamesInPackage()
if (knownClassNamesInPackage != null && name.asString() !in knownClassNamesInPackage) {
return null
}
return classes(name) return classes(name)
} }
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
interface KotlinClassFinder { interface KotlinClassFinder {
fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass? fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass?
@@ -40,6 +40,8 @@ class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFi
// We don't know which packages our class loader has and has not, so we behave as if it contains any package in the world // We don't know which packages our class loader has and has not, so we behave as if it contains any package in the world
return ReflectJavaPackage(fqName) return ReflectJavaPackage(fqName)
} }
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = null
} }
fun ClassLoader.tryLoadClass(fqName: String) = fun ClassLoader.tryLoadClass(fqName: String) =