Build light classes for file facades

This commit is contained in:
Pavel V. Talanov
2015-09-07 17:21:36 +03:00
parent a9b4587b82
commit cda35f609a
7 changed files with 111 additions and 82 deletions
@@ -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
@@ -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<String> 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<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
for (JetClassOrObject declaration : declarations) {
@@ -73,13 +73,18 @@ public abstract class LightClassGenerationSupport {
@NotNull
public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@Nullable
public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject);
@NotNull
public abstract Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope);
@NotNull
public abstract Collection<PsiClass> getFacadeClassesInPackage(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@NotNull
public abstract Collection<String> getFacadeNames(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@NotNull
public abstract Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope);
@@ -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<PsiClass> {
val result = ArrayList<PsiClass>()
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<JetFile>,
moduleInfo: IdeaModuleInfo
private fun createLightClassForPackageFacade(
files: List<JetFile>,
moduleInfo: IdeaModuleInfo,
packageFqName: FqName
): List<PsiClass> {
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<JetFile>
): List<PsiClass> {
@@ -188,36 +190,32 @@ public class IDELightClassGenerationSupport(private val project: Project) : Ligh
}
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val result = ArrayList<PsiClass>()
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<JetFile>,
moduleInfo: IdeaModuleInfo
): List<PsiClass> {
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<JetFile> {
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<String> {
val facadeFilesInPackage = JetFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope)
return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() }
}
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
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<JetFile>): List<PsiClass> {
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<JetClassOrObject>().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<CachedJavaStub>("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
}
@@ -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 {
@@ -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<TDeclaration : JetCallableDeclaration>(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)
}
}
@@ -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<JetFile> =
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,