diff --git a/analysis/symbol-light-classes/build.gradle.kts b/analysis/symbol-light-classes/build.gradle.kts index 8bd5266a313..9913acd3443 100644 --- a/analysis/symbol-light-classes/build.gradle.kts +++ b/analysis/symbol-light-classes/build.gradle.kts @@ -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()) } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/IDEKotlinAsJavaFirSupport.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/IDEKotlinAsJavaFirSupport.kt index 71523953e12..90475c4977a 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/IDEKotlinAsJavaFirSupport.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/IDEKotlinAsJavaFirSupport.kt @@ -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 = 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 = 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 { 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 } \ No newline at end of file diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/caches/SymbolLightClassFacadeCache.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/caches/SymbolLightClassFacadeCache.kt index 8b405bfb719..e2391565b0e 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/caches/SymbolLightClassFacadeCache.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/caches/SymbolLightClassFacadeCache.kt @@ -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() + ConcurrentMapBasedCache(ConcurrentHashMap()) } fun getOrCreateSymbolLightFacade( ktFiles: List, 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, 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().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) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/decompiled/KtLightClassForDecompiledFacade.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/decompiled/KtLightClassForDecompiledFacade.kt new file mode 100644 index 00000000000..7fe9e344fc8 --- /dev/null +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/decompiled/KtLightClassForDecompiledFacade.kt @@ -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, +) : KtLightClassForDecompiledDeclaration(clsDelegate, clsParent, file, kotlinOrigin), KtLightClassForFacade { + override fun getName(): String = super.getName() + + override val facadeClassFqName: FqName = file.javaFileFacadeFqName + + override val originKind: LightClassOriginKind + get() = LightClassOriginKind.BINARY +} \ No newline at end of file