Improve package caching in KotlinJavaPsiFacade

^KT-33394 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-09-10 07:38:46 +00:00
parent 05d3e48564
commit 5036673187
22 changed files with 348 additions and 121 deletions
@@ -404,7 +404,7 @@ class JavaSymbolProvider(
private fun hasTopLevelClassOf(classId: ClassId): Boolean {
val knownNames = knownClassNamesInPackage.getOrPut(classId.packageFqName) {
facade.knownClassNamesInPackage(classId.packageFqName)
facade.knownClassNamesInPackage(classId.packageFqName, searchScope)
} ?: return true
return classId.relativeClassName.topLevelName() in knownNames
}
@@ -23,6 +23,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
import org.jetbrains.kotlin.resolve.jvm.TopPackageNamesProvider
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import javax.annotation.PostConstruct
import javax.inject.Inject
@@ -32,10 +33,14 @@ abstract class AbstractJavaClassFinder : JavaClassFinder {
protected lateinit var project: Project
protected lateinit var javaSearchScope: GlobalSearchScope
@Inject
fun setScope(scope: GlobalSearchScope) {
javaSearchScope = FilterOutKotlinSourceFilesScope(scope)
javaSearchScope =
if (scope == GlobalSearchScope.EMPTY_SCOPE) {
GlobalSearchScope.EMPTY_SCOPE
} else {
FilterOutKotlinSourceFilesScope(scope)
}
}
@Inject
@@ -48,9 +53,15 @@ abstract class AbstractJavaClassFinder : JavaClassFinder {
CodeAnalyzerInitializer.getInstance(project).initialize(trace, codeAnalyzer.moduleDescriptor, codeAnalyzer)
}
inner class FilterOutKotlinSourceFilesScope(baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
inner class FilterOutKotlinSourceFilesScope(baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope),
TopPackageNamesProvider {
override fun contains(file: VirtualFile) = myBaseScope.contains(file) && (file.isDirectory || file.fileType !== KotlinFileType.INSTANCE)
override val topPackageNames: Set<String>?
get() = (myBaseScope as? TopPackageNamesProvider)?.topPackageNames
override fun contains(file: VirtualFile) =
(file.isDirectory || file.fileType !== KotlinFileType.INSTANCE) &&
myBaseScope.contains(file)
val base: GlobalSearchScope = myBaseScope
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.load.java
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.load.java.structure.impl.JavaPackageImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
@@ -33,11 +34,16 @@ class JavaClassFinderImpl : AbstractJavaClassFinder() {
javaFacade = KotlinJavaPsiFacade.getInstance(project)
}
override fun findClass(request: JavaClassFinder.Request): JavaClass? = javaFacade.findClass(request, javaSearchScope)
override fun findClass(request: JavaClassFinder.Request): JavaClass? {
return javaFacade.findClass(request, javaSearchScope)
}
override fun findPackage(fqName: FqName) =
javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) }
override fun findPackage(fqName: FqName): JavaPackage? {
return javaFacade.findPackage(fqName.asString(), javaSearchScope)?.let { JavaPackageImpl(it, javaSearchScope) }
}
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName)
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? {
return javaFacade.knownClassNamesInPackage(packageFqName, javaSearchScope)
}
}
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.keysToMap
class IncrementalPackageFragmentProvider(
@@ -56,10 +57,8 @@ class IncrementalPackageFragmentProvider(
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> = emptySet()
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
return listOfNotNull(fqNameToPackageFragment[fqName])
}
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
packageFragments.addIfNotNull(fqNameToPackageFragment[fqName])
inner class IncrementalPackageFragment(fqName: FqName) : PackageFragmentDescriptorImpl(moduleDescriptor, fqName) {
val target: TargetId
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.storage.NotNullLazyValue
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addIfNotNull
class OptionalAnnotationPackageFragmentProvider(
module: ModuleDescriptor,
@@ -79,8 +80,8 @@ class OptionalAnnotationPackageFragmentProvider(
}
}
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
packages[fqName]?.let(::listOf).orEmpty()
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
packageFragments.addIfNotNull(packages[fqName])
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
emptyList()
@@ -241,10 +241,10 @@ private class DelegatingPackageFragmentProvider<M : ModuleInfo>(
) : PackageFragmentProvider {
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
if (certainlyDoesNotExist(fqName)) return emptyList()
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
if (certainlyDoesNotExist(fqName)) return
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getPackageFragments(fqName)
resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.collectPackageFragments(fqName, packageFragments)
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
@@ -129,23 +129,28 @@ class LazyModuleDependencies<M : ModuleInfo>(
firstDependency: M? = null,
private val resolverForProject: AbstractResolverForProject<M>
) : ModuleDependencies {
private val dependencies = storageManager.createLazyValue {
val moduleDescriptors = mutableSetOf<ModuleDescriptorImpl>()
firstDependency?.let {
moduleDescriptors.add(resolverForProject.descriptorForModule(it))
}
val moduleDescriptor = resolverForProject.descriptorForModule(module)
sequence {
if (firstDependency != null) {
yield(resolverForProject.descriptorForModule(firstDependency))
}
if (module.dependencyOnBuiltIns() == ModuleInfo.DependencyOnBuiltIns.AFTER_SDK) {
yield(moduleDescriptor.builtIns.builtInsModule)
}
for (dependency in module.dependencies()) {
@Suppress("UNCHECKED_CAST")
yield(resolverForProject.descriptorForModule(dependency as M))
}
if (module.dependencyOnBuiltIns() == ModuleInfo.DependencyOnBuiltIns.LAST) {
yield(moduleDescriptor.builtIns.builtInsModule)
}
}.toList()
val dependencyOnBuiltIns = module.dependencyOnBuiltIns()
if (dependencyOnBuiltIns == ModuleInfo.DependencyOnBuiltIns.AFTER_SDK) {
moduleDescriptors.add(moduleDescriptor.builtIns.builtInsModule)
}
for (dependency in module.dependencies()) {
if (dependency == firstDependency) continue
@Suppress("UNCHECKED_CAST")
moduleDescriptors.add(resolverForProject.descriptorForModule(dependency as M))
}
if (dependencyOnBuiltIns == ModuleInfo.DependencyOnBuiltIns.LAST) {
moduleDescriptors.add(moduleDescriptor.builtIns.builtInsModule)
}
moduleDescriptors.toList()
}
override val allDependencies: List<ModuleDescriptorImpl> get() = dependencies()
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve
import com.intellij.codeInsight.completion.CompletionUtilCore
import com.intellij.psi.impl.source.DummyHolder
import com.intellij.util.SmartList
import org.jetbrains.kotlin.config.AnalysisFlags
@@ -573,6 +574,9 @@ class QualifiedExpressionResolver(val languageVersionSettings: LanguageVersionSe
context: ExpressionTypingContext
): Qualifier? {
val name = expression.getReferencedNameAsName()
if (!expression.isPhysical && !name.isSpecial && name.asString().endsWith(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)) {
return null
}
val location = KotlinLookupLocation(expression)
val qualifierDescriptor = when (receiver) {
@@ -180,6 +180,16 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext {
this.declarationProviderFactory = declarationProviderFactory;
this.packageFragmentProvider = new PackageFragmentProvider() {
@Override
public void collectPackageFragments(
@NotNull FqName fqName, @NotNull Collection<PackageFragmentDescriptor> packageFragments
) {
LazyPackageDescriptor fragment = getPackageFragment(fqName);
if (fragment != null) {
packageFragments.add(fragment);
}
}
@NotNull
@Override
public List<PackageFragmentDescriptor> getPackageFragments(@NotNull FqName fqName) {
@@ -21,9 +21,13 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.PackageIndex;
import com.intellij.openapi.util.LowMemoryWatcher;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.*;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiElementFinderImpl;
import com.intellij.psi.impl.file.PsiPackageImpl;
@@ -31,13 +35,11 @@ import com.intellij.psi.impl.file.impl.JavaFileManager;
import com.intellij.psi.impl.light.LightModifierList;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.reference.SoftReference;
import com.intellij.util.CommonProcessors;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.Query;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import kotlin.collections.ArraysKt;
import com.intellij.util.messages.MessageBusConnection;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -60,11 +62,26 @@ public class KotlinJavaPsiFacade {
private volatile KotlinPsiElementFinderWrapper[] elementFinders;
private static class PackageCache {
// long term cache
final ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> packageInLibScopeCache = ContainerUtil.newConcurrentMap();
// short term caches
final ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> packageInScopeCache = ContainerUtil.newConcurrentMap();
final ConcurrentMap<String, Boolean> hasPackageInAllScopeCache = ContainerUtil.newConcurrentMap();
void clear() {
packageInScopeCache.clear();
hasPackageInAllScopeCache.clear();
}
}
private volatile SoftReference<PackageCache> packageCache;
private static final PsiPackage NULL_PACKAGE = new PsiPackageImpl(null, "NULL_PACKAGE");
private static @Nullable PsiPackage unwrap(@NotNull PsiPackage psiPackage) {
return psiPackage == NULL_PACKAGE ? null : psiPackage;
}
private volatile PackageCache packageCache;
private final Project project;
private final LightModifierList emptyModifierList;
@@ -78,27 +95,60 @@ public class KotlinJavaPsiFacade {
emptyModifierList = new LightModifierList(PsiManager.getInstance(project), KotlinLanguage.INSTANCE);
PsiModificationTracker modificationTracker = PsiManager.getInstance(project).getModificationTracker();
MessageBus bus = project.getMessageBus();
// drop entire cache when it is low free memory
LowMemoryWatcher.register(this::clearPackageCaches, project);
bus.connect().subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() {
MessageBusConnection connection = project.getMessageBus().connect();
// VFS changes like create/delete/copy/move directory are subject to clean up short term caches
connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
boolean relevant = false;
for (VFileEvent event : events) {
VirtualFile file = event.getFile();
relevant = ((event instanceof VFileCreateEvent && ((VFileCreateEvent) event).isDirectory()) ||
(file != null && file.isDirectory() &&
(event instanceof VFileDeleteEvent ||
event instanceof VFileMoveEvent ||
event instanceof VFileCopyEvent)));
if (relevant) break;
}
if (relevant) {
clearPackageCaches(false);
}
}
});
// PSI changes (like in R files) could lead to creating virtual packages
// therefore it has to clean up short term caches
PsiModificationTracker modificationTracker = PsiManager.getInstance(project).getModificationTracker();
connection.subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() {
private long lastTimeSeen = -1L;
@Override
@SuppressWarnings("deprecation")
public void modificationCountChanged() {
long now = modificationTracker.getJavaStructureModificationCount();
long now = modificationTracker.getModificationCount();
if (lastTimeSeen != now) {
lastTimeSeen = now;
packageCache = null;
clearPackageCaches(false);
}
}
});
}
public void clearPackageCaches() {
packageCache = null;
clearPackageCaches(true);
}
private void clearPackageCaches(boolean force) {
if (force) {
packageCache = null;
} else {
obtainPackageCache().clear();
}
}
public LightModifierList getEmptyModifierList() {
@@ -106,7 +156,10 @@ public class KotlinJavaPsiFacade {
}
public JavaClass findClass(@NotNull JavaClassFinder.Request request, @NotNull GlobalSearchScope scope) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); // We hope this method is being called often enough to cancel daemon processes smoothly
if (scope == GlobalSearchScope.EMPTY_SCOPE) return null;
// We hope this method is being called often enough to cancel daemon processes smoothly
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
ClassId classId = request.getClassId();
String qualifiedName = classId.asSingleFqName().asString();
@@ -150,7 +203,9 @@ public class KotlinJavaPsiFacade {
}
@Nullable
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName) {
public Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
if (scope == GlobalSearchScope.EMPTY_SCOPE) return null;
KotlinPsiElementFinderWrapper[] finders = finders();
if (finders.length == 1 && finders[0] instanceof CliFinder) {
@@ -207,17 +262,19 @@ public class KotlinJavaPsiFacade {
: new NonCliFinder(project, javaFileManager)
);
List<PsiElementFinder> nonKotlinFinders = ArraysKt.filter(
PsiElementFinder.EP.getPoint(getProject()).getExtensions(),
finder -> (finder instanceof KotlinSafeClassFinder) ||
!(finder instanceof NonClasspathClassFinder ||
finder instanceof KotlinFinderMarker ||
finder instanceof PsiElementFinderImpl)
);
List<PsiElementFinder> nonKotlinFinders = new ArrayList<>();
for (PsiElementFinder finder : PsiElementFinder.EP.getExtensions(getProject())) {
if ((finder instanceof KotlinSafeClassFinder) ||
!(finder instanceof NonClasspathClassFinder ||
finder instanceof KotlinFinderMarker ||
finder instanceof PsiElementFinderImpl)) {
nonKotlinFinders.add(finder);
}
}
elementFinders.addAll(CollectionsKt.map(nonKotlinFinders, KotlinJavaPsiFacade::wrap));
return elementFinders.toArray(new KotlinPsiElementFinderWrapper[elementFinders.size()]);
return elementFinders.toArray(new KotlinPsiElementFinderWrapper[0]);
}
@NotNull
@@ -230,60 +287,116 @@ public class KotlinJavaPsiFacade {
}
public PsiPackage findPackage(@NotNull String qualifiedName, GlobalSearchScope searchScope) {
if (searchScope == GlobalSearchScope.EMPTY_SCOPE) return null;
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (certainlyDoesNotExist(qualifiedName, searchScope)) return null;
PackageCache cache = SoftReference.dereference(packageCache);
if (cache == null) {
packageCache = new SoftReference<>(cache = new PackageCache());
}
Pair<String, GlobalSearchScope> key = new Pair<>(qualifiedName, searchScope);
PsiPackage aPackage = cache.packageInScopeCache.get(key);
if (aPackage != null) {
return aPackage;
}
KotlinPsiElementFinderWrapper[] finders = filteredFinders();
PackageCache cache = obtainPackageCache();
Boolean packageFoundInAllScope = cache.hasPackageInAllScopeCache.get(qualifiedName);
if (packageFoundInAllScope != null) {
if (!packageFoundInAllScope.booleanValue()) return null;
if (packageFoundInAllScope != null && !packageFoundInAllScope.booleanValue()) return null;
// Package was found in AllScope with some of finders but is absent in packageCache for current scope.
// We check only finders that depend on scope.
for (KotlinPsiElementFinderWrapper finder : finders) {
if (!finder.isSameResultForAnyScope()) {
aPackage = finder.findPackage(qualifiedName, searchScope);
if (aPackage != null) {
return ConcurrencyUtil.cacheOrGet(cache.packageInScopeCache, key, aPackage);
Pair<String, GlobalSearchScope> key = new Pair<>(qualifiedName, searchScope);
PsiPackage pkg = cache.packageInLibScopeCache.get(key);
if (pkg != null) {
return unwrap(pkg);
}
pkg = cache.packageInScopeCache.get(key);
if (pkg != null) {
return unwrap(pkg);
}
boolean isALibrarySearchScope = isALibrarySearchScope(searchScope);
{
// store found package in a long term cache if package is found in library search scope
ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> existedPackageInScopeCache =
isALibrarySearchScope ? cache.packageInLibScopeCache : cache.packageInScopeCache;
KotlinPsiElementFinderWrapper[] finders = filteredFinders();
if (packageFoundInAllScope != null) {
// Package was found in AllScope with some of finders but is absent in packageCache for current scope.
// We check only finders that depend on scope.
for (KotlinPsiElementFinderWrapper finder : finders) {
if (!finder.isSameResultForAnyScope()) {
PsiPackage aPackage = finder.findPackage(qualifiedName, searchScope);
if (aPackage != null) {
return unwrap(ConcurrencyUtil.cacheOrGet(existedPackageInScopeCache, key, aPackage));
}
}
}
}
}
else {
for (KotlinPsiElementFinderWrapper finder : finders) {
aPackage = finder.findPackage(qualifiedName, searchScope);
else {
for (KotlinPsiElementFinderWrapper finder : finders) {
PsiPackage aPackage = finder.findPackage(qualifiedName, searchScope);
if (aPackage != null) {
return ConcurrencyUtil.cacheOrGet(cache.packageInScopeCache, key, aPackage);
}
}
boolean found = false;
for (KotlinPsiElementFinderWrapper finder : finders) {
if (!finder.isSameResultForAnyScope()) {
aPackage = finder.findPackage(qualifiedName, GlobalSearchScope.allScope(project));
if (aPackage != null) {
found = true;
break;
return unwrap(ConcurrencyUtil.cacheOrGet(existedPackageInScopeCache, key, aPackage));
}
}
}
cache.hasPackageInAllScopeCache.put(qualifiedName, found);
boolean found = false;
for (KotlinPsiElementFinderWrapper finder : finders) {
if (!finder.isSameResultForAnyScope()) {
PsiPackage aPackage = finder.findPackage(qualifiedName, GlobalSearchScope.allScope(project));
if (aPackage != null) {
found = true;
break;
}
}
}
cache.hasPackageInAllScopeCache.put(qualifiedName, found);
}
}
return null;
// qualifiedName could be like a proper package name, e.g `org.jetbrains.kotlin`
// but it could be as well part of typed text like `fooba`
//
// all those temporary names and those don't even look like a package name should be stored in a short term cache
// while names those are potentially proper package name could be stored for a long time
// (till PROJECT_ROOTS or specific VFS changes)
boolean packageLikeQName = qualifiedName.indexOf('.') > 0;
ConcurrentMap<Pair<String, GlobalSearchScope>, PsiPackage> notFoundPackageInScopeCache =
// store NULL_PACKAGE (attribute that package not found) in a long term cache if:
// - library search scope
// - qualifiedName looks like package (has `.` in its name)
isALibrarySearchScope && packageLikeQName ?
cache.packageInLibScopeCache : cache.packageInScopeCache;
return unwrap(ConcurrencyUtil.cacheOrGet(notFoundPackageInScopeCache, key, NULL_PACKAGE));
}
private PackageCache obtainPackageCache() {
PackageCache cache = packageCache;
if (cache == null) {
packageCache = cache = new PackageCache();
}
return cache;
}
private static boolean isALibrarySearchScope(GlobalSearchScope searchScope) {
return searchScope.isSearchInLibraries();
}
private static boolean certainlyDoesNotExist(@NotNull String qualifiedName, GlobalSearchScope searchScope) {
if (searchScope instanceof TopPackageNamesProvider) {
TopPackageNamesProvider topPackageAwareSearchScope = (TopPackageNamesProvider) searchScope;
Set<String> topPackageNames = topPackageAwareSearchScope.getTopPackageNames();
if (topPackageNames != null) {
String topPackageName = qualifiedName;
int index = topPackageName.indexOf('.');
if (index > 0) {
topPackageName = topPackageName.substring(0, index);
}
if (!topPackageNames.contains(topPackageName)) {
return true;
}
}
}
return false;
}
@NotNull
@@ -292,7 +405,7 @@ public class KotlinJavaPsiFacade {
KotlinPsiElementFinderWrapper[] finders = finders();
if (dumbService.isDumb()) {
List<KotlinPsiElementFinderWrapper> list = dumbService.filterByDumbAwareness(Arrays.asList(finders));
finders = list.toArray(new KotlinPsiElementFinderWrapper[list.size()]);
finders = list.toArray(new KotlinPsiElementFinderWrapper[0]);
}
return finders;
}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.jvm
interface TopPackageNamesProvider {
val topPackageNames: Set<String>?
}
@@ -16,11 +16,13 @@
package org.jetbrains.kotlin.load.java.lazy
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.CacheWithNotNullValues
import org.jetbrains.kotlin.utils.addIfNotNull
class LazyJavaPackageFragmentProvider(
components: JavaResolverComponents
@@ -41,6 +43,9 @@ class LazyJavaPackageFragmentProvider(
override fun getPackageFragments(fqName: FqName) = listOfNotNull(getPackageFragment(fqName))
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
packageFragments.addIfNotNull(getPackageFragment(fqName))
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) =
getPackageFragment(fqName)?.getSubPackageFqNames().orEmpty()
}
@@ -20,13 +20,19 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
interface PackageFragmentProvider {
fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor>
fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>)
fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
val packageFragments = mutableListOf<PackageFragmentDescriptor>()
collectPackageFragments(fqName, packageFragments)
return packageFragments
}
fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName>
object Empty : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName) = emptyList<PackageFragmentDescriptor>()
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) = emptySet<FqName>()
}
@@ -20,14 +20,15 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class PackageFragmentProviderImpl(
private val packageFragments: Collection<PackageFragmentDescriptor>
private val packageFragments: Collection<PackageFragmentDescriptor>
) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
packageFragments.filter { it.fqName == fqName }
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
this.packageFragments.filterTo(packageFragments) { it.fqName == fqName }
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
packageFragments.asSequence()
.map { it.fqName }
.filter { !it.isRoot && it.parent() == fqName }
.toList()
packageFragments.asSequence()
.map { it.fqName }
.filter { !it.isRoot && it.parent() == fqName }
.toList()
}
@@ -23,14 +23,19 @@ import java.util.*
import org.jetbrains.kotlin.name.Name
class CompositePackageFragmentProvider(// can be modified from outside
private val providers: List<PackageFragmentProvider>) : PackageFragmentProvider {
private val providers: List<PackageFragmentProvider>
) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
val result = ArrayList<PackageFragmentDescriptor>()
for (provider in providers) {
result.addAll(provider.getPackageFragments(fqName))
init {
assert(providers.size == providers.toSet().size) {
"providers.size is ${providers.size} while only ${providers.toSet().size} unique providers"
}
}
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
for (provider in providers) {
provider.collectPackageFragments(fqName, packageFragments)
}
return result.toList()
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
class LazyPackageViewDescriptorImpl(
override val module: ModuleDescriptorImpl,
override val fqName: FqName,
storageManager: StorageManager
override val module: ModuleDescriptorImpl,
override val fqName: FqName,
storageManager: StorageManager
) : DeclarationDescriptorImpl(Annotations.EMPTY, fqName.shortNameOrSpecial()), PackageViewDescriptor {
override val fragments: List<PackageFragmentDescriptor> by storageManager.createLazyValue {
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.addIfNotNull
abstract class AbstractDeserializedPackageFragmentProvider(
protected val storageManager: StorageManager,
@@ -38,7 +39,9 @@ abstract class AbstractDeserializedPackageFragmentProvider(
protected abstract fun findPackage(fqName: FqName): DeserializedPackageFragment?
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> = listOfNotNull(fragments(fqName))
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
packageFragments.addIfNotNull(fragments(fqName))
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> = emptySet()
}
@@ -15,6 +15,8 @@ import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.NewVirtualFile
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.PathUtil
import com.intellij.util.SmartList
@@ -58,6 +60,7 @@ import org.jetbrains.kotlin.platform.konan.NativePlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.TopPackageNamesProvider
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.types.typeUtil.closure
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -459,9 +462,40 @@ object NotUnderContentRootModuleInfo : IdeaModuleInfo {
get() = platform.single().findAnalyzerServices()
}
internal open class PoweredLibraryScopeBase(project: Project, classes: Array<VirtualFile>, sources: Array<VirtualFile>) :
LibraryScopeBase(project, classes, sources), TopPackageNamesProvider {
private val entriesVirtualFileSystems: Set<NewVirtualFileSystem>? = run {
val fileSystems = mutableSetOf<NewVirtualFileSystem>()
for (file in classes + sources) {
val newVirtualFile = file as? NewVirtualFile ?: return@run null
fileSystems.add(newVirtualFile.fileSystem)
}
fileSystems
}
override val topPackageNames: Set<String> by lazy {
(classes + sources)
.flatMap { it.children.toList() }
.filter(VirtualFile::isDirectory)
.map(VirtualFile::getName)
.toSet() + "" // empty package is always present
}
override fun contains(file: VirtualFile): Boolean {
((file as? NewVirtualFile)?.fileSystem)?.let {
if (entriesVirtualFileSystems != null && !entriesVirtualFileSystems.contains(it)) {
return false
}
}
return super.contains(file)
}
}
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
PoweredLibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf()) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file)
@@ -474,7 +508,7 @@ private class LibraryWithoutSourceScope(project: Project, private val library: L
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class LibrarySourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, arrayOf<VirtualFile>(), library.getFiles(OrderRootType.SOURCES)) {
PoweredLibraryScopeBase(project, arrayOf(), library.getFiles(OrderRootType.SOURCES)) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file)
@@ -488,7 +522,7 @@ private class LibrarySourceScope(project: Project, private val library: Library)
//TODO: (module refactoring) android sdk has modified scope
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class SdkScope(project: Project, val sdk: Sdk) :
LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
PoweredLibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf()) {
override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk
@@ -29,8 +29,8 @@ abstract class DeserializerForDecompilerBase(val directoryPackageFqName: FqName)
protected val moduleDescriptor: ModuleDescriptorImpl = createDummyModule("module for building decompiled sources")
protected val packageFragmentProvider: PackageFragmentProvider = object : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
return listOf(createDummyPackageFragment(fqName))
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
packageFragments.add(createDummyPackageFragment(fqName))
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
@@ -16,10 +16,13 @@
package org.jetbrains.kotlin.idea;
import com.intellij.ProjectTopics;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
import org.jetbrains.annotations.NotNull;
@@ -27,6 +30,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter;
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs;
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm;
import org.jetbrains.kotlin.resolve.konan.diagnostics.ErrorsNative;
@@ -44,6 +48,13 @@ public class PluginStartupActivity implements StartupActivity {
StartupCompatKt.runActivity(project);
PluginStartupService.Companion.getInstance(project).register(project);
project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
@Override
public void rootsChanged(@NotNull ModuleRootEvent event) {
KotlinJavaPsiFacade.getInstance(project).clearPackageCaches();
}
});
initializeDiagnostics();
try {
@@ -17,8 +17,9 @@ internal class CommonizedPackageFragmentProvider : PackageFragmentProvider {
this.packageFragments += packageFragment
}
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> =
packageFragments.filter { it.fqName == fqName }
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
this.packageFragments.filterTo(packageFragments) { it.fqName == fqName }
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> =
packageFragments.asSequence()
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.addIfNotNull
abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension {
protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager?
@@ -132,7 +133,8 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
class AndroidSyntheticPackageFragmentProvider(
val packages: Map<FqName, () -> PackageFragmentDescriptor>
) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName) = listOfNotNull(packages[fqName]?.invoke())
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) =
packageFragments.addIfNotNull(packages[fqName]?.invoke())
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): List<FqName> {
return packages.asSequence()