findDecompiledDeclaration: rewrite using stub indices

This commit is contained in:
Pavel V. Talanov
2015-12-10 13:18:06 +03:00
parent 7ccdbfd596
commit b5568f9ace
@@ -17,41 +17,32 @@
package org.jetbrains.kotlin.idea.decompiler.navigation package org.jetbrains.kotlin.idea.decompiler.navigation
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
import org.jetbrains.kotlin.idea.vfilefinder.JsVirtualFileFinderFactory import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyFqnNameIndex
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory
import org.jetbrains.kotlin.load.kotlin.OldPackageFacadeClassUtils.getPackageClassId
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptPackageFragment
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import java.util.*
fun findDecompiledDeclaration( fun findDecompiledDeclaration(
project: Project, project: Project,
referencedDescriptor: DeclarationDescriptor): KtDeclaration? { referencedDescriptor: DeclarationDescriptor
): KtDeclaration? {
if (ErrorUtils.isError(referencedDescriptor)) return null
if (isLocal(referencedDescriptor)) return null if (isLocal(referencedDescriptor)) return null
if (referencedDescriptor is PackageFragmentDescriptor || referencedDescriptor is PackageViewDescriptor) return null
val virtualFile = findVirtualFileContainingDescriptor(project, referencedDescriptor) ?: return null val decompiledFiles = findDecompiledFilesForDescriptor(project, referencedDescriptor)
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) return decompiledFiles.asSequence().mapNotNull {
if (psiFile !is KtDecompiledFile) { it.getDeclarationForDescriptor(referencedDescriptor)
return null }.firstOrNull()
}
return psiFile.getDeclarationForDescriptor(referencedDescriptor)
} }
private fun isLocal(descriptor: DeclarationDescriptor): Boolean { private fun isLocal(descriptor: DeclarationDescriptor): Boolean {
@@ -63,70 +54,41 @@ private fun isLocal(descriptor: DeclarationDescriptor): Boolean {
} }
} }
/* private fun findDecompiledFilesForDescriptor(
Find virtual file which contains the declaration of descriptor we're navigating to.
*/
private fun findVirtualFileContainingDescriptor(
project: Project, project: Project,
referencedDescriptor: DeclarationDescriptor): VirtualFile? { referencedDescriptor: DeclarationDescriptor
if (ErrorUtils.isError(referencedDescriptor)) return null ): Collection<KtDecompiledFile> {
return findCandidateDeclarationsInIndex(project, referencedDescriptor).mapNotNullTo(LinkedHashSet()) {
val containerClassId = getContainerClassId(project, referencedDescriptor) ?: return null it?.containingFile as? KtDecompiledFile
val scopeToSearchIn = KotlinSourceFilterScope.sourceAndClassFiles(GlobalSearchScope.allScope(project), project)
val virtualFileFinderFactory: VirtualFileFinderFactory
if (isFromKotlinJavasriptMetadata(referencedDescriptor)) {
virtualFileFinderFactory = JsVirtualFileFinderFactory.SERVICE.getInstance(project)
} }
else {
virtualFileFinderFactory = JvmVirtualFileFinderFactory.SERVICE.getInstance(project)
}
val fileFinder = virtualFileFinderFactory.create(scopeToSearchIn)
return fileFinder.findVirtualFileWithHeader(containerClassId)
} }
private fun isFromKotlinJavasriptMetadata(referencedDescriptor: DeclarationDescriptor): Boolean { private fun findCandidateDeclarationsInIndex(
val packageFragmentDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, PackageFragmentDescriptor::class.java, false) project: Project,
return packageFragmentDescriptor is KotlinJavascriptPackageFragment referencedDescriptor: DeclarationDescriptor
} ): Collection<KtDeclaration?> {
val scope = KotlinSourceFilterScope.libraryClassFiles(GlobalSearchScope.allScope(project), project)
//TODO: navigate to inner classes val containingClass = DescriptorUtils.getParentOfType(referencedDescriptor, ClassDescriptor::class.java, false)
//TODO: should we construct proper SourceElement's for decompiled parts / facades? if (containingClass != null) {
private fun getContainerClassId(project: Project, referencedDescriptor: DeclarationDescriptor): ClassId? { return KotlinFullClassNameIndex.getInstance().get(containingClass.fqNameSafe.asString(), project, scope)
val deserializedCallableContainer = DescriptorUtils.getParentOfType(referencedDescriptor, DeserializedCallableMemberDescriptor::class.java, true)
if (deserializedCallableContainer != null) {
return getContainerClassId(project, deserializedCallableContainer)
} }
val containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrPackageFragmentDescriptor::class.java, false) val topLevelDeclaration = DescriptorUtils.getParentOfType(referencedDescriptor, PropertyDescriptor::class.java, false)
if (containerDescriptor is PackageFragmentDescriptor) { ?: DescriptorUtils.getParentOfType(referencedDescriptor, FunctionDescriptor::class.java, false) ?: return emptyList()
val packageFQN = containerDescriptor.fqName
if (referencedDescriptor is DeserializedCallableMemberDescriptor) { // filter out synthetic descriptors
val partClassName = JvmFileClassUtil.getImplClassName(referencedDescriptor) if (!DescriptorUtils.isTopLevelDeclaration(topLevelDeclaration)) return emptyList()
if (partClassName != null) {
val partFQN = packageFQN.child(partClassName) val fqName = topLevelDeclaration.fqNameSafe.asString()
val multifileFacadeJetFiles = StaticFacadeIndexUtil.getMultifileClassForPart(partFQN, GlobalSearchScope.allScope(project), project) when (topLevelDeclaration) {
if (multifileFacadeJetFiles.isEmpty()) { is FunctionDescriptor -> {
return ClassId(packageFQN, partClassName) return KotlinTopLevelFunctionFqnNameIndex.getInstance().get(fqName, project, scope)
}
else {
val multifileFacade = multifileFacadeJetFiles.iterator().next()
val multifileFacadeName = multifileFacade.virtualFile.nameWithoutExtension
return ClassId(packageFQN, Name.identifier(multifileFacadeName))
}
}
} }
is PropertyDescriptor -> {
return getPackageClassId(packageFQN) return KotlinTopLevelPropertyFqnNameIndex.getInstance().get(fqName, project, scope)
}
if (containerDescriptor is ClassDescriptor) {
if (containerDescriptor.containingDeclaration is ClassDescriptor || ExpressionTypingUtils.isLocal(containerDescriptor.containingDeclaration, containerDescriptor)) {
return getContainerClassId(project, containerDescriptor.containingDeclaration)
} }
return containerDescriptor.classId else -> error("Referenced non local declaration that is not inside top level function, property of class:\n $referencedDescriptor")
} }
return null
} }