From cda35f609a56c14170743a1753aa2605edc55699 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Mon, 7 Sep 2015 17:21:36 +0300 Subject: [PATCH] Build light classes for file facades --- .../load/kotlin/PackagePartClassUtils.kt | 3 - .../kotlin/asJava/JavaElementFinder.java | 9 +- .../asJava/LightClassGenerationSupport.java | 7 +- .../resolve/IDELightClassGenerationSupport.kt | 142 ++++++++++-------- .../idea/caches/resolve/getModuleInfo.kt | 6 +- .../kotlin/idea/stubindex/IndexUtils.kt | 18 ++- .../idea/stubindex/StaticFacadeIndexUtil.kt | 8 - 7 files changed, 111 insertions(+), 82 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt index c07d31b822c..9517875cf17 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt @@ -62,9 +62,6 @@ public object PackagePartClassUtils { return packageFqName.child(Name.identifier(partClassName)) } - public @jvmStatic fun isPartClassFqName(classFqName: FqName): Boolean = - classFqName.shortName().asString().endsWith(PART_CLASS_NAME_SUFFIX) - @deprecated("Migrate to JvmFileClassesProvider") public @jvmStatic fun getPackagePartInternalName(file: JetFile): String = JvmClassName.byFqNameWithoutInnerClasses(getPackagePartFqName(file)).internalName diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java index bfdff4eb5ed..81ab8113f58 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java @@ -118,10 +118,9 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM findClassesAndObjects(qualifiedName, scope, answer); - if (PackagePartClassUtils.isPartClassFqName(qualifiedName)) { - answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); - } - else if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { + answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); + + if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { answer.addAll(lightClassGenerationSupport.getPackageClasses(qualifiedName.parent(), scope)); } @@ -152,6 +151,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM Set answer = Sets.newHashSet(); answer.add(PackageClassUtils.getPackageClassName(packageFQN)); + answer.addAll(lightClassGenerationSupport.getFacadeNames(packageFQN, scope)); for (JetClassOrObject declaration : declarations) { String name = declaration.getName(); @@ -204,6 +204,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM FqName packageFQN = new FqName(psiPackage.getQualifiedName()); answer.addAll(lightClassGenerationSupport.getPackageClasses(packageFQN, scope)); + answer.addAll(lightClassGenerationSupport.getFacadeClassesInPackage(packageFQN, scope)); Collection declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); for (JetClassOrObject declaration : declarations) { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java index 52ccc8dc6ca..03131569e53 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java @@ -73,13 +73,18 @@ public abstract class LightClassGenerationSupport { @NotNull public abstract Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); - @Nullable public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject); @NotNull public abstract Collection getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope); + @NotNull + public abstract Collection getFacadeClassesInPackage(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); + + @NotNull + public abstract Collection getFacadeNames(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); + @NotNull public abstract Collection findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index db971e2cb55..fea4252ddff 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.roots.libraries.LibraryUtil import com.intellij.openapi.util.Key +import com.intellij.openapi.util.UserDataHolder import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.ClassFileViewProvider import com.intellij.psi.PsiClass @@ -37,12 +38,9 @@ import com.intellij.util.cls.ClsFormatException import org.jetbrains.kotlin.asJava.* import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.decompiler.navigation.JetSourceNavigationHelper -import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex +import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourcesAndLibraries -import org.jetbrains.kotlin.idea.stubindex.JetTopLevelClassByPackageIndex -import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil -import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.kotlin.PackageClassUtils @@ -140,38 +138,42 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh } override fun getPackageClasses(packageFqName: FqName, scope: GlobalSearchScope): Collection { - val result = ArrayList() val filesWithCallables = PackagePartClassUtils.getFilesWithCallables(findFilesForPackage(packageFqName, scope)) val filesByModule = filesWithCallables.groupBy { it.getModuleInfo() } - for ((moduleInfo, files) in filesByModule) { - if (files.isEmpty()) continue - - if (moduleInfo is ModuleSourceInfo) { - result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo)) - } - else { - val packageClassName = PackageClassUtils.getPackageClassName(packageFqName) - val virtualFileForPackageClass = files.asSequence().map { - it.virtualFile?.parent?.findChild("$packageClassName.class") - }.firstOrNull { it != null } ?: continue - - val clsClassFromPackageClass = createClsJavaClassFromVirtualFile(files.first(), virtualFileForPackageClass, null) ?: continue - result.add(KotlinLightClassForDecompiledDeclaration(clsClassFromPackageClass, null)) - } + return filesByModule.flatMap { + createLightClassForPackageFacade(it.value, it.key, packageFqName) } - return result } - private fun getLightClassesForPackageFacadeWithSources( - packageFqName: FqName, - facadeFiles: List, - moduleInfo: IdeaModuleInfo + private fun createLightClassForPackageFacade( + files: List, + moduleInfo: IdeaModuleInfo, + packageFqName: FqName ): List { - val lightClassForFacade = KotlinLightClassForFacade.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), facadeFiles) - return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles) + if (moduleInfo is ModuleSourceInfo) { + val lightClassForFacade = KotlinLightClassForFacade.createForPackageFacade( + psiManager, packageFqName, moduleInfo.contentScope(), files + ) + return withFakeLightClasses(lightClassForFacade, files) + + } + else { + val packageClassName = PackageClassUtils.getPackageClassName(packageFqName) + val virtualFileForPackageClass = files.asSequence().map { + it.virtualFile?.parent?.findChild("$packageClassName.class") + }.firstOrNull { it != null } ?: return emptyList() + + val clsClassFromPackageClass = createClsJavaClassFromVirtualFile( + mirrorFile = files.first(), + classFile = virtualFileForPackageClass, + cacheHolder = virtualFileForPackageClass, + correspondingClassOrObject = null + ) ?: return emptyList() + return listOf(KotlinLightClassForDecompiledDeclaration(clsClassFromPackageClass, null)) + } } - private fun getLightClassesForFacadeWithFiles( + private fun withFakeLightClasses( lightClassForFacade: KotlinLightClassForFacade?, facadeFiles: List ): List { @@ -188,36 +190,32 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh } override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection { - val result = ArrayList() - val filesByModule = PackagePartClassUtils.getFilesWithCallables(findFilesForFacade(facadeFqName, scope)).groupBy { + val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy { it.getModuleInfo() } - for ((moduleInfo, files) in filesByModule) { - if (files.isEmpty()) continue - - if (moduleInfo is ModuleSourceInfo) { - result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo)) - } - else { - result.addAll(getLightClassesForDecompiledFacadeFiles(files)) - } + return filesByModule.flatMap { + createLightClassForFileFacade(facadeFqName, it.value, it.key) } - return result } - private fun getLightClassesForStaticFacadeWithSources( + public fun createLightClassForFileFacade( facadeFqName: FqName, facadeFiles: List, moduleInfo: IdeaModuleInfo ): List { - val lightClassForFacade = KotlinLightClassForFacade.createForFacade( - psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles) - return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles) + if (moduleInfo is ModuleSourceInfo) { + val lightClassForFacade = KotlinLightClassForFacade.createForFacade( + psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles) + return withFakeLightClasses(lightClassForFacade, facadeFiles) + } + else { + return facadeFiles.filter { it.isCompiled }.map { createLightClassForDecompiledKotlinFile(it) }.filterNotNull() + } } override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection { - return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project) + return JetFileFacadeClassIndex.INSTANCE.get(facadeFqName.asString(), project, scope) } override fun resolveClassToDescriptor(classOrObject: JetClassOrObject): ClassDescriptor? { @@ -229,6 +227,24 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh } } + override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection { + val facadeFilesInPackage = JetFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope) + return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() } + } + + override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection { + val facadeFilesInPackage = JetFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope) + val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy { + Pair(it.javaFileFacadeFqName, it.getModuleInfo()) + } + + return groupedByFqNameAndModuleInfo.flatMap { + val (key, files) = it + val (fqName, moduleInfo) = key + createLightClassForFileFacade(fqName, files, moduleInfo) + } + } + private class CachedJavaStub(public var modificationStamp: Long, public var javaFileStub: PsiJavaFileStubImpl) companion object { @@ -316,30 +332,32 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh return getClassRelativeName(parent).child(name) } - private fun getLightClassesForDecompiledFacadeFiles(filesWithCallables: List): List { - return filesWithCallables.filter { it.isCompiled }.map { createLightClassForDecompiledKotlinFile(it) }.filterNotNull() - } - private fun createLightClassForDecompiledKotlinFile(file: JetFile): KotlinLightClassForDecompiledDeclaration? { val virtualFile = file.virtualFile ?: return null val classOrObject = file.declarations.filterIsInstance().singleOrNull() - val javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile, classOrObject) ?: return null + val javaClsClass = createClsJavaClassFromVirtualFile( + file, virtualFile, + cacheHolder = file, + correspondingClassOrObject = classOrObject + ) ?: return null + return KotlinLightClassForDecompiledDeclaration(javaClsClass, classOrObject) } private fun createClsJavaClassFromVirtualFile( - decompiledKotlinFile: JetFile, - virtualFile: VirtualFile, - decompiledClassOrObject: JetClassOrObject? + mirrorFile: JetFile, + classFile: VirtualFile, + cacheHolder: UserDataHolder, + correspondingClassOrObject: JetClassOrObject? ): ClsClassImpl? { - val javaFileStub = getOrCreateJavaFileStub(decompiledKotlinFile, virtualFile) ?: return null - val manager = PsiManager.getInstance(decompiledKotlinFile.project) - val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) { + val javaFileStub = getOrCreateJavaFileStub(cacheHolder, classFile) ?: return null + val manager = PsiManager.getInstance(mirrorFile.project) + val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, classFile)) { override fun getNavigationElement(): PsiElement { - if (decompiledClassOrObject != null) { - return decompiledClassOrObject.navigationElement.containingFile + if (correspondingClassOrObject != null) { + return correspondingClassOrObject.navigationElement.containingFile } return super.getNavigationElement() } @@ -349,7 +367,7 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh } override fun getMirror(): PsiElement { - return decompiledKotlinFile + return mirrorFile } } fakeFile.isPhysical = false @@ -360,17 +378,17 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh private val cachedJavaStubKey = Key.create("CACHED_JAVA_STUB") private fun getOrCreateJavaFileStub( - decompiledKotlinFile: JetFile, + userDataHolder: UserDataHolder, virtualFile: VirtualFile ): PsiJavaFileStubImpl? { - val cachedJavaStub = decompiledKotlinFile.getUserData(cachedJavaStubKey) + val cachedJavaStub = userDataHolder.getUserData(cachedJavaStubKey) val fileModificationStamp = virtualFile.modificationStamp if (cachedJavaStub != null && cachedJavaStub.modificationStamp == fileModificationStamp) { return cachedJavaStub.javaFileStub } val stub = createStub(virtualFile) as PsiJavaFileStubImpl? if (stub != null) { - decompiledKotlinFile.putUserData(cachedJavaStubKey, CachedJavaStub(fileModificationStamp, stub)) + userDataHolder.putUserData(cachedJavaStubKey, CachedJavaStub(fileModificationStamp, stub)) } return stub } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt index 08a936a13ab..b2cad6480cf 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt @@ -63,7 +63,11 @@ fun PsiElement.getModuleInfo(): IdeaModuleInfo { val virtualFile = containingFile.getOriginalFile().getVirtualFile() ?: return logAndReturnDefault("Analyzing non-physical file $containingFile of type ${containingFile.javaClass}") - return getModuleInfoByVirtualFile(project, virtualFile, (containingFile as? JetFile)?.isCompiled() ?: false) + return getModuleInfoByVirtualFile( + project, + virtualFile, + isDecompiledFile = (containingFile as? JetFile)?.isCompiled() ?: false + ) } private fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile, isDecompiledFile: Boolean): IdeaModuleInfo { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IndexUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IndexUtils.kt index c9b41db7b80..96c74ff56a3 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IndexUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IndexUtils.kt @@ -16,10 +16,13 @@ package org.jetbrains.kotlin.idea.stubindex -import com.google.common.collect.HashMultimap -import com.google.common.collect.Multimap -import com.intellij.openapi.util.Key import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.stubs.KotlinCallableStubBase import org.jetbrains.kotlin.util.aliasImportMap @@ -72,3 +75,12 @@ private fun JetTypeElement.index(declarat } } +public val JetFile.javaFileFacadeFqName: FqName + get() { + return CachedValuesManager.getCachedValue(this) { + val facadeFqName = + if (isCompiled) packageFqName.child(Name.identifier(virtualFile.nameWithoutExtension)) + else JvmFileClassUtil.getFileClassInfoNoResolve(this).facadeClassFqName + CachedValueProvider.Result(facadeFqName, this) + } + } \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt index 3be9bc37901..80e918f2273 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/StaticFacadeIndexUtil.kt @@ -21,17 +21,9 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.JetFile -import kotlin.platform.platformStatic public object StaticFacadeIndexUtil { - @jvmStatic public fun findFilesForStaticFacade( - facadeFqName: FqName, - searchScope: GlobalSearchScope, - project: Project - ) : Collection = - JetFileFacadeClassIndex.INSTANCE.get(facadeFqName.asString(), project, searchScope) - // TODO change as we introduce multi-file facades (this will require a separate index) @jvmStatic public fun findFilesForFilePart( partFqName: FqName,