diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt index c66eab06cfc..afd27e5ef8d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt @@ -80,6 +80,5 @@ internal tailrec fun FirDeclaration.ktSymbolOrigin(): KtSymbolOrigin = when (ori } } - class InvalidFirDeclarationOriginForSymbol(declaration: FirDeclaration) : IllegalStateException("Invalid FirDeclarationOrigin ${declaration.origin::class.simpleName} for ${declaration.render()}") diff --git a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/KotlinProjectStructureProviderTestImpl.kt b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/KotlinProjectStructureProviderTestImpl.kt index e81ec0caf91..2a6f6bf5757 100644 --- a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/KotlinProjectStructureProviderTestImpl.kt +++ b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/KotlinProjectStructureProviderTestImpl.kt @@ -11,10 +11,14 @@ import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.services.TestServices -class KotlinProjectStructureProviderTestImpl(testServices: TestServices) : ProjectStructureProvider() { +class KotlinProjectStructureProviderTestImpl(private val testServices: TestServices) : ProjectStructureProvider() { private val moduleInfoProvider = testServices.projectModuleProvider override fun getKtModuleForKtElement(element: PsiElement): KtModule { val containingFile = element.containingFile as KtFile return moduleInfoProvider.getModuleInfoByKtFile(containingFile) as KtModule } + + override fun getKtLibraryModules(): Collection { + return moduleInfoProvider.getLibraryModules() + } } diff --git a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt index c2f7b1c872f..d867ef89f80 100644 --- a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt +++ b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt @@ -26,6 +26,10 @@ class TestKtModuleProvider( fun getModule(moduleName: String): TestKtModule = cache.getValue(moduleName) + + fun getLibraryModules(): Collection { + return cache.values.filterIsInstance() + } } val TestServices.projectModuleProvider: TestKtModuleProvider by TestServices.testServiceAccessor() diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/AbstractDeclarationFromLibraryModuleProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/AbstractDeclarationFromLibraryModuleProvider.kt new file mode 100644 index 00000000000..d53c216b139 --- /dev/null +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/AbstractDeclarationFromLibraryModuleProvider.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2022 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.analysis.providers.impl + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileSystem +import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.util.io.URLUtil +import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule +import org.jetbrains.kotlin.name.FqName +import java.nio.file.Path + +public interface AbstractDeclarationFromLibraryModuleProvider { + public val scope: GlobalSearchScope + public val jarFileSystem: CoreJarFileSystem + + public fun virtualFilesFromModule( + libraryModule: KtLibraryModule, + fqName: FqName, + isPackageName: Boolean, + ): Collection { + val fqNameString = fqName.asString() + val fs = StandardFileSystems.local() + return libraryModule.getBinaryRoots().flatMap r@{ rootPath -> + val root = findRoot(rootPath, fs) ?: return@r emptySet() + val files = mutableSetOf() + VfsUtilCore.iterateChildrenRecursively( + root, + /*filter=*/filter@{ + // Return `false` will skip the children. + if (it == root) return@filter true + // If it is a directory, then check if its path starts with fq name of interest + val relativeFqName = relativeFqName(root, it) + if (it.isDirectory && fqNameString.startsWith(relativeFqName)) { + return@filter true + } + // Otherwise, i.e., if it is a file, we are already in that matched directory (or directory in the middle). + // But, for files at the top-level, double-check if its parent (dir) and fq name of interest match. + if (isPackageName) + relativeFqName(root, it.parent).endsWith(fqNameString) + else // exact class fq name + relativeFqName == fqNameString + }, + /*iterator=*/{ + // We reach here after filtering above. + // Directories in the middle, e.g., com/android, can reach too. + if (!it.isDirectory && + isCompiledFile(it) && + it in scope + ) { + files.add(it) + } + true + } + ) + files + } + } + + private fun findRoot( + rootPath: Path, + fs: VirtualFileSystem, + ): VirtualFile? { + return if (rootPath.toFile().isDirectory) { + fs.findFileByPath(rootPath.toAbsolutePath().toString()) + } else { + jarFileSystem.refreshAndFindFileByPath(rootPath.toAbsolutePath().toString() + URLUtil.JAR_SEPARATOR) + } + } + + private fun relativeFqName( + root: VirtualFile, + virtualFile: VirtualFile, + ): String { + return if (root.isDirectory) { + val fragments = buildList { + var cur = virtualFile + while (cur != root) { + add(cur.nameWithoutExtension) + cur = cur.parent + } + } + fragments.reversed().joinToString(".") + } else { + virtualFile.path.split(URLUtil.JAR_SEPARATOR).lastOrNull()?.replace("/", ".") + ?: URLUtil.JAR_SEPARATOR // random string that will bother membership test. + } + } + + private fun isCompiledFile( + virtualFile: VirtualFile, + ): Boolean { + return virtualFile.extension?.endsWith(JavaClassFileType.INSTANCE.defaultExtension) == true + } +} diff --git a/analysis/analysis-api-standalone/build.gradle.kts b/analysis/analysis-api-standalone/build.gradle.kts index 71e4f6dbbb5..eef1fb1a504 100644 --- a/analysis/analysis-api-standalone/build.gradle.kts +++ b/analysis/analysis-api-standalone/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { api(project(":analysis:analysis-api-fir")) api(project(":analysis:low-level-api-fir")) api(project(":analysis:symbol-light-classes")) + api(project(":analysis:decompiled:light-classes-for-decompiled")) testApi(projectTests(":analysis:analysis-api-impl-base")) testApi(projectTests(":analysis:analysis-api-fir")) diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt index 8f1ee280f35..1991ca2b817 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt @@ -17,20 +17,15 @@ import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService +import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider import org.jetbrains.kotlin.analysis.project.structure.impl.ProjectStructureProviderByCompilerConfiguration -import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolverFactory -import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory -import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory -import org.jetbrains.kotlin.analysis.providers.KotlinPackageProviderFactory -import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticAnnotationsResolverFactory -import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory -import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticModificationTrackerFactory -import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPackageProviderFactory +import org.jetbrains.kotlin.analysis.providers.* +import org.jetbrains.kotlin.analysis.providers.impl.* import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport import org.jetbrains.kotlin.asJava.finder.JavaElementFinder import org.jetbrains.kotlin.cli.jvm.config.javaSourceRoots @@ -76,6 +71,7 @@ public fun configureApplicationEnvironment(app: MockApplication) { * * [KtAnalysisSessionProvider] * * [KotlinAsJavaFirSupport] * * [SymbolLightClassFacadeCache] for FIR light class support + * * [ClsJavaStubByVirtualFileCache] * * [KotlinModificationTrackerFactory] * * [KotlinAnnotationsResolverFactory] * * [LLFirResolveStateService] @@ -151,6 +147,9 @@ internal fun configureProjectEnvironment( project.registerService( SymbolLightClassFacadeCache::class.java ) + project.registerService( + ClsJavaStubByVirtualFileCache::class.java + ) project.picoContainer.registerComponentInstance( KotlinModificationTrackerFactory::class.qualifiedName, diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt index 18e512249ec..dd96611f1f3 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt @@ -54,7 +54,7 @@ internal class KtSourceModuleByCompilerConfiguration( ktFiles: List, jarFileSystem: CoreJarFileSystem, ) : BaseKtModuleByCompilerConfiguration(compilerConfig, project), KtSourceModule { - override val directRegularDependencies: List by lazy { + override val directRegularDependencies: List by lazy { val libraryRoots = compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots val libraryRootsByType = libraryRoots.groupBy { it.isDirectory } buildList { diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt index 6c3fa5dea47..0189ed4fa0b 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.project.structure.impl import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider import org.jetbrains.kotlin.config.CompilerConfiguration @@ -21,6 +22,10 @@ internal class ProjectStructureProviderByCompilerConfiguration( ) : ProjectStructureProvider() { private val sourceModule = KtSourceModuleByCompilerConfiguration(compilerConfig, project, ktFiles, jarFileSystem) + private val libraryModules: Collection by lazy { + sourceModule.directRegularDependencies + } + override fun getKtModuleForKtElement(element: PsiElement): KtModule { val containingFile = element.containingFile.virtualFile return if (containingFile in sourceModule.contentScope) { @@ -30,4 +35,8 @@ internal class ProjectStructureProviderByCompilerConfiguration( ?: error("Can't find module for ${containingFile.path}") } } + + override fun getKtLibraryModules(): Collection { + return libraryModules + } } diff --git a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt index f02966a4aa9..6d9489d62c2 100644 --- a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt +++ b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt @@ -13,6 +13,12 @@ public abstract class ProjectStructureProvider { * For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs. */ public abstract fun getKtModuleForKtElement(element: PsiElement): KtModule + + /** + * Returns all [KtLibraryModule]s in this project. + */ + // TODO: We rather need a session or facade that maintains this information. + public abstract fun getKtLibraryModules(): Collection } /** @@ -37,4 +43,4 @@ public inline fun PsiElement.getKtModuleOfType(project: P * @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly */ public inline fun PsiElement.getKtModuleOfTypeSafe(project: Project = this.project): M? = - getKtModule(project) as M? \ No newline at end of file + getKtModule(project) as M?