findDecompiledDeclaration: rewrite using stub indices
This commit is contained in:
+39
-77
@@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user