FIR LC: add supports for decompiled declarations

This commit is contained in:
Jinseong Jeon
2021-12-21 18:55:37 +03:00
committed by Ilya Kirillov
parent aa87524513
commit 09f5ec7bd3
4 changed files with 90 additions and 29 deletions
@@ -12,6 +12,7 @@ dependencies {
implementation(project(":analysis:analysis-api"))
implementation(project(":analysis:analysis-internal-utils"))
implementation(project(":analysis:project-structure"))
implementation(project(":analysis:decompiled:light-classes-for-decompiled"))
implementation(intellijCore())
}
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.light.classes.symbol
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.analysis.decompiled.light.classes.DecompiledLightClassesFactory
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createPackageProvider
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
@@ -20,7 +23,10 @@ import org.jetbrains.kotlin.light.classes.symbol.classes.getOrCreateFirLightClas
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.parentOrNull
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupport() {
@@ -62,14 +68,9 @@ class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupp
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> =
fqName.toClassIdSequence().flatMap {
project.createDeclarationProvider(searchScope).getClassesByClassId(it)
}.filter {
//TODO Do not return LC came from LibrarySources
when (it.getKtModule(project)) {
is KtLibrarySourceModule -> false
is KtNotUnderContentRootModule -> false
else -> true
}
}.toSet()
}
.filter { it.isFromSourceOrLibraryBinary(project) }
.toSet()
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean =
project.createPackageProvider(scope).doKotlinPackageExists(fqName)
@@ -80,8 +81,11 @@ class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupp
.map { fqn.child(it) }
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? {
if (!classOrObject.isFromSource()) return null
return getOrCreateFirLightClass(classOrObject)
return when (classOrObject.getKtModule(project)) {
is KtSourceModule -> getOrCreateFirLightClass(classOrObject)
is KtLibraryModule -> DecompiledLightClassesFactory.getLightClassForDecompiledClassOrObject(classOrObject, project)
else -> null
}
}
override fun getLightClassForScript(script: KtScript): KtLightClass =
@@ -105,7 +109,7 @@ class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupp
project.createDeclarationProvider(scope)
.getFacadeFilesInPackage(packageFqName)
.asSequence()
.filter { it.isFromSource() }
.filter { it.isFromSourceOrLibraryBinary(project) }
.groupBy { it.javaFileFacadeFqName }
.mapNotNull {
project.getService(SymbolLightClassFacadeCache::class.java)
@@ -115,13 +119,13 @@ class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupp
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> =
project.createDeclarationProvider(scope)
.getFacadeFilesInPackage(packageFqName)
.filter { it.isFromSource() }
.filter { it.isFromSourceOrLibraryBinary(project) }
.mapTo(mutableSetOf()) { it.javaFileFacadeFqName.shortName().asString() }
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
return project.createDeclarationProvider(scope)
.findFilesForFacade(facadeFqName)
.filter { it.isFromSource() }
.filter { it.isFromSourceOrLibraryBinary(project) }
}
override fun getFakeLightClass(classOrObject: KtClassOrObject): KtFakeLightClass =
@@ -132,10 +136,10 @@ class IDEKotlinAsJavaFirSupport(private val project: Project) : KotlinAsJavaSupp
}
private fun KtElement.isFromSource(): Boolean {
if (this is KtFile && isCompiled) {
// small optimisation to not invoke expensive getKtModule
return false
private fun KtElement.isFromSourceOrLibraryBinary(project: Project): Boolean {
return when (getKtModule(project)) {
is KtSourceModule -> true
is KtLibraryModule -> true
else -> false
}
return getKtModule(project) is KtSourceModule
}
@@ -6,28 +6,35 @@
package org.jetbrains.kotlin.light.classes.symbol.caches
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.decompiled.light.classes.DecompiledLightClassesFactory
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
import org.jetbrains.kotlin.light.classes.symbol.FirLightClassForFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.analysis.utils.caches.*
import org.jetbrains.kotlin.analysis.utils.collections.ConcurrentMapBasedCache
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.light.classes.symbol.FirLightClassForFacade
import org.jetbrains.kotlin.light.classes.symbol.decompiled.KtLightClassForDecompiledFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import java.util.concurrent.ConcurrentHashMap
class SymbolLightClassFacadeCache(project: Project) {
class SymbolLightClassFacadeCache(private val project: Project) {
private val cache by softCachedValue(
project,
project.createProjectWideOutOfBlockModificationTracker()
) {
ConcurrentHashMap<FacadeKey, FirLightClassForFacade>()
ConcurrentMapBasedCache<FacadeKey, KtLightClassForFacade?>(ConcurrentHashMap())
}
fun getOrCreateSymbolLightFacade(
ktFiles: List<KtFile>,
facadeClassFqName: FqName,
): FirLightClassForFacade? {
): KtLightClassForFacade? {
if (ktFiles.isEmpty()) return null
val key = FacadeKey(facadeClassFqName, ktFiles.toSet())
return cache.computeIfAbsent(key) {
return cache.getOrPut(key) {
getOrCreateFirLightFacadeNoCache(ktFiles, facadeClassFqName)
}
}
@@ -35,9 +42,26 @@ class SymbolLightClassFacadeCache(project: Project) {
private fun getOrCreateFirLightFacadeNoCache(
ktFiles: List<KtFile>,
facadeClassFqName: FqName,
): FirLightClassForFacade {
): KtLightClassForFacade? {
val firstFile = ktFiles.first()
return FirLightClassForFacade(firstFile.manager, facadeClassFqName, ktFiles)
return when {
ktFiles.none { it.isCompiled } ->
return FirLightClassForFacade(firstFile.manager, facadeClassFqName, ktFiles)
ktFiles.all { it.isCompiled } -> {
val file = ktFiles.firstOrNull { it.javaFileFacadeFqName == facadeClassFqName } as? KtClsFile
?: error("Can't find the representative decompiled file for $facadeClassFqName")
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
val clsDelegate = DecompiledLightClassesFactory.createClsJavaClassFromVirtualFile(
mirrorFile = file,
classFile = file.virtualFile,
correspondingClassOrObject = classOrObject,
project = project,
) ?: return null
KtLightClassForDecompiledFacade(clsDelegate, clsDelegate.parent, file, classOrObject, ktFiles)
}
else ->
error("Source and compiled files are mixed: $ktFiles}")
}
}
private data class FacadeKey(val fqName: FqName, val files: Set<KtFile>)
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2021 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.light.classes.symbol.decompiled
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.decompiled.light.classes.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
internal class KtLightClassForDecompiledFacade(
override val clsDelegate: PsiClass,
clsParent: PsiElement,
file: KtClsFile,
kotlinOrigin: KtClassOrObject?,
override val files: Collection<KtFile>,
) : KtLightClassForDecompiledDeclaration(clsDelegate, clsParent, file, kotlinOrigin), KtLightClassForFacade {
override fun getName(): String = super<KtLightClassForFacade>.getName()
override val facadeClassFqName: FqName = file.javaFileFacadeFqName
override val originKind: LightClassOriginKind
get() = LightClassOriginKind.BINARY
}