From d59d66e87694cf6473e24c47eb2c9bc434731853 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 15 Feb 2023 14:22:59 +0100 Subject: [PATCH] [AA, LL] use stubs to build deserialized Fir elements in IDE ^KTIJ-24638 Notice on `DebugSymbolRenderer`: stub based deserializer sets source directly, but it's available in IDE mode only. Thus, standalone and IDE tests have different results. In order to avoid this, sources for compiled code are explicitly ignored Notice on distinct callables: for a file which belong to multiple libraries, decompiled code would be build per library. In order to avoid ambiguity errors for members in that file, we need to distinct provided elements by origins failed test from IJ repo: FirReferenceResolveWithCrossLibTestGenerated#testSetWithTypeParameters --- .../AnalysisApiFe10TestConfigurator.kt | 2 + ...ysisApiFirLibraryBinaryTestConfigurator.kt | 2 + ...ysisApiFirLibrarySourceTestConfigurator.kt | 2 + .../AnalysisApiBaseTestServiceRegistrar.kt | 24 +- ...isApiDecompiledCodeTestServiceRegistrar.kt | 42 ++ ...lysisApiLibraryBaseTestServiceRegistrar.kt | 5 - .../providers/KotlinDeclarationProvider.kt | 2 + .../impl/KotlinStaticDeclarationProvider.kt | 75 ++- .../structure/KtStaticModuleProvider.kt | 4 + .../StandaloneAnalysisAPISessionBuilder.kt | 2 + .../StandaloneModeTestServiceRegistrar.kt | 4 + .../api/symbols/DebugSymbolRenderer.kt | 6 +- .../classFromJdk.descriptors.txt | 43 -- .../constructorViaTypeAlias.descriptors.txt | 6 +- .../samConstructor.descriptors.txt | 3 +- .../decompiler/stub/clsStubBuilding.kt | 3 +- .../CompositeKotlinDeclarationProvider.kt | 4 + .../EmptyKotlinDeclarationProvider.kt | 1 + .../FileBasedKotlinDeclarationProvider.kt | 3 + ...JvmFirDeserializedSymbolProviderFactory.kt | 41 ++ .../structure/LLFirLibraryProviderFactory.kt | 45 +- .../structure/sessionFactoryHelpers.kt | 2 +- ...FirModuleWithDependenciesSymbolProvider.kt | 3 +- .../extensions/LLFirResolveExtensionTool.kt | 4 + .../JvmFromStubDecompilerSource.kt | 36 ++ ...bBasedDeserializedSymbolProviderFactory.kt | 56 ++ ...mStubBasedFirDeserializedSymbolProvider.kt | 209 +++++++ .../StubBasedAnnotationDeserializer.kt | 180 ++++++ .../StubBasedClassDeserialization.kt | 222 +++++++ .../StubBasedFirContractDeserializer.kt | 150 +++++ .../StubBasedFirMemberDeserializer.kt | 547 ++++++++++++++++++ .../StubBasedFirTypeDeserializer.kt | 283 +++++++++ .../AnalysisApiFirTestServiceRegistrar.kt | 3 + ...irLowLevelCompilerBasedTestConfigurator.kt | 2 + ...nalysisApiFirSourceLikeTestConfigurator.kt | 2 + .../deserialization/ClassDeserialization.kt | 2 +- .../kotlin/fir/java/JavaSymbolProvider.kt | 1 + .../kotlin/fir/builder/BaseFirBuilder.kt | 1 + .../jetbrains/kotlin/fir/EnumClassUtils.kt | 2 +- .../KtNameReferenceExpressionElementType.java | 5 +- ...KotlinNameReferenceExpressionStubImpl.java | 18 + 41 files changed, 1957 insertions(+), 90 deletions(-) create mode 100644 analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt diff --git a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt index 99b8f413a71..e5f21ea7441 100644 --- a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt +++ b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtModuleFactory import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtSourceModuleFactory @@ -43,6 +44,7 @@ object AnalysisApiFe10TestConfigurator : AnalysisApiTestConfigurator() { override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFe10TestServiceRegistrar, ) diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt index ed7836a6e71..e0e20862659 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiLibraryBaseTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure @@ -47,6 +48,7 @@ object AnalysisApiFirLibraryBinaryTestConfigurator : AnalysisApiTestConfigurator override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, AnalysisApiLibraryBaseTestServiceRegistrar, ) diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt index 73ec8ab5dcd..633a1b0fe28 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiLibraryBaseTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure @@ -57,6 +58,7 @@ object AnalysisApiFirLibrarySourceTestConfigurator : AnalysisApiTestConfigurator override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, AnalysisApiLibraryBaseTestServiceRegistrar, ) diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt index d4d9c3ca8ad..2804bba9393 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt @@ -12,12 +12,17 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService import org.jetbrains.kotlin.analysis.api.lifetime.KtDefaultLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService +import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl import org.jetbrains.kotlin.analysis.providers.* import org.jetbrains.kotlin.analysis.providers.impl.* import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider +import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService import org.jetbrains.kotlin.psi.KtFile @@ -45,17 +50,28 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() { } override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { - val allKtFiles = testServices.ktModuleProvider.getModuleStructure().mainModules.flatMap { it.files.filterIsInstance() } - + val moduleStructure = testServices.ktModuleProvider.getModuleStructure() + val allKtFiles = moduleStructure.mainModules.flatMap { it.files.filterIsInstance() } + val roots = StandaloneProjectFactory.getVirtualFilesForLibraryRoots( + moduleStructure.binaryModules.flatMap { binary -> binary.getBinaryRoots() }, + testServices.environmentManager.getProjectEnvironment() + ).distinct() project.apply { registerService(KtModuleScopeProvider::class.java, KtModuleScopeProviderImpl()) registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(allKtFiles)) - registerService(KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory(project, allKtFiles)) + + registerService(KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory( + project, + allKtFiles, + additionalRoots = roots + )) registerService(KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(project, allKtFiles)) registerService(KotlinReferenceProvidersService::class.java, HLApiReferenceProviderService::class.java) registerService(KotlinResolutionScopeProvider::class.java, KotlinByModulesResolutionScopeProvider::class.java) } } - override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {} + override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { + application.registerService(KotlinFakeClsStubsCache::class.java, KotlinFakeClsStubsCache()) + } } diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt new file mode 100644 index 00000000000..9abf56d4d73 --- /dev/null +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt @@ -0,0 +1,42 @@ +/* + * 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.api.impl.base.test.configurators + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import com.intellij.psi.ClassFileViewProviderFactory +import com.intellij.psi.FileTypeFileViewProviders +import com.intellij.psi.compiled.ClassFileDecompilers +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService +import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar +import org.jetbrains.kotlin.test.TestInfrastructureInternals +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.analysis.test.framework.services.disposableProvider + + +object AnalysisApiDecompiledCodeTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { + override fun registerProjectExtensionPoints(project: MockProject, testServices: TestServices) { + } + + override fun registerProjectServices(project: MockProject, testServices: TestServices) { + } + + override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { + } + + override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { + application.apply { + registerService(ClsKotlinBinaryClassCache::class.java) + registerService(FileAttributeService::class.java, DummyFileAttributeService) + } + } +} \ No newline at end of file diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt index 78fd841c4c6..3f6ef16a8f1 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt @@ -34,11 +34,6 @@ object AnalysisApiLibraryBaseTestServiceRegistrar : AnalysisApiTestServiceRegist } override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { - application.apply { - registerService(ClsKotlinBinaryClassCache::class.java) - registerService(FileAttributeService::class.java, DummyFileAttributeService) - } - FileTypeFileViewProviders.INSTANCE.addExplicitExtension(JavaClassFileType.INSTANCE, ClassFileViewProviderFactory()) ClassFileDecompilers.getInstance().EP_NAME.point.apply { diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt index 1ff572adb5b..86e82a62dd8 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt @@ -42,6 +42,8 @@ public abstract class KotlinDeclarationProvider { public abstract fun findInternalFilesForFacade(facadeFqName: FqName): Collection public abstract fun findFilesForScript(scriptFqName: FqName): Collection + + public abstract fun computePackageSetWithTopLevelCallableDeclarations(): Set } public abstract class KotlinDeclarationProviderFactory { diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt index 3a3b97a528e..6a828aeca2c 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt @@ -5,24 +5,34 @@ package org.jetbrains.kotlin.analysis.providers.impl +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileVisitor import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import com.intellij.psi.PsiElement import com.intellij.psi.PsiManager import com.intellij.psi.SingleRootFileViewProvider import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.stubs.StubElement +import com.intellij.util.containers.CollectionFactory import com.intellij.util.indexing.FileContent import com.intellij.util.indexing.FileContentImpl import com.intellij.util.io.URLUtil +import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.impl.* import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol @@ -84,6 +94,11 @@ public class KotlinStaticDeclarationProvider internal constructor( return index.scriptMap[scriptFqName].orEmpty().filter { it.containingKtFile.virtualFile in scope } } + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + val packageNames = index.topLevelPropertyMap.keys + index.topLevelFunctionMap.keys + return packageNames.mapTo(mutableSetOf()) { it.asString() } + } + override fun getTopLevelProperties(callableId: CallableId): Collection = index.topLevelPropertyMap[callableId.packageName] ?.filter { ktProperty -> @@ -109,6 +124,7 @@ public class KotlinStaticDeclarationProviderFactory( private val project: Project, files: Collection, private val jarFileSystem: CoreJarFileSystem = CoreJarFileSystem(), + additionalRoots: List = emptyList(), ) : KotlinDeclarationProviderFactory() { private val index = KotlinStaticDeclarationIndex() @@ -247,13 +263,23 @@ public class KotlinStaticDeclarationProviderFactory( // member functions and properties stub.childrenStubs.forEach(::indexStub) } + is KotlinObjectStubImpl -> { + addToClassMap(stub.psi) + // member functions and properties + stub.childrenStubs.forEach(::indexStub) + } is KotlinTypeAliasStubImpl -> addToTypeAliasMap(stub.psi) is KotlinFunctionStubImpl -> addToFunctionMap(stub.psi) is KotlinPropertyStubImpl -> addToPropertyMap(stub.psi) + is KotlinPlaceHolderStubImpl -> { + if (stub.stubType == KtStubElementTypes.CLASS_BODY) { + stub.getChildrenStubs().filterIsInstance>().forEach(::indexStub) + } + } } } - loadBuiltIns().forEach { ktFileStub -> + fun processStub(ktFileStub: KotlinFileStubImpl) { val ktFile: KtFile = ktFileStub.psi addToFacadeFileMap(ktFile) @@ -270,6 +296,32 @@ public class KotlinStaticDeclarationProviderFactory( ktFileStub.childrenStubs.forEach(::indexStub) } + loadBuiltIns().forEach { processStub(it) } + + val binaryClassCache = ClsKotlinBinaryClassCache.getInstance() + for (root in additionalRoots) { + KotlinFakeClsStubsCache.processAdditionalRoot(root) { additionalRoot -> + val stubs = mutableListOf() + VfsUtilCore.visitChildrenRecursively(additionalRoot, object : VirtualFileVisitor() { + override fun visitFile(file: VirtualFile): Boolean { + if (!file.isDirectory && file.fileType == JavaClassFileType.INSTANCE) { + val fileContent = FileContentImpl.createByFile(file) + if (!binaryClassCache.isKotlinJvmCompiledFile(file, fileContent.content)) return true + val stub = KotlinClsStubBuilder().buildFileStub(fileContent) as? KotlinFileStubImpl ?: return true + val fakeFile = object : KtFile(KtClassFileViewProvider(psiManager, fileContent.file), isCompiled = true) { + override fun getStub() = stub + override fun isPhysical() = false + } + stub.psi = fakeFile + stubs.add(stub) + } + return true + } + }) + stubs + }?.forEach { processStub(it) } + } + // Indexing user source files files.forEach { it.accept(recorder) @@ -280,3 +332,24 @@ public class KotlinStaticDeclarationProviderFactory( return KotlinStaticDeclarationProvider(index, searchScope) } } + +/** + * Test application service to store stubs of shared between tests libraries. + * + * Otherwise, each test would start indexing of stdlib from scratch, + * and under the lock which makes tests extremely slow*/ +class KotlinFakeClsStubsCache { + private val fakeFileClsStubs = CollectionFactory.createConcurrentWeakValueMap>() + + companion object { + fun processAdditionalRoot(root: VirtualFile, storage: (VirtualFile) -> List): List? { + val service = ApplicationManager.getApplication().getService(KotlinFakeClsStubsCache::class.java) ?: return null + if (service.fakeFileClsStubs[root.path] == null) { + service.fakeFileClsStubs[root.path] = storage(root) + } + return service.fakeFileClsStubs.computeIfAbsent(root.path) { _ -> + storage(root) + } + } + } +} diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt index 6ad60476c8b..da7e39924f8 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt @@ -25,6 +25,10 @@ class KtStaticModuleProvider( containingFileAsVirtualFile.analysisExtensionFileContextModule?.let { return it } + projectStructure.binaryModules.firstOrNull { binaryModule -> + containingFileAsVirtualFile in binaryModule.contentScope + }?.let { return it } + return projectStructure.mainModules .first { module -> element in module.ktModule.contentScope diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt index 6e58dae80d2..91cff126bbd 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory 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.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBuiltinsSessionFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider @@ -167,6 +168,7 @@ public class StandaloneAnalysisAPISessionBuilder( registerService(ClsJavaStubByVirtualFileCache::class.java, ClsJavaStubByVirtualFileCache()) registerService(KotlinAsJavaSupport::class.java, SymbolKotlinAsJavaSupport(this)) registerService(ReadWriteAccessChecker::class.java, ReadWriteAccessCheckerFirImpl()) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmFirDeserializedSymbolProviderFactory()) } @Suppress("DEPRECATION") diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt index f2b344394a9..ba905170d7e 100644 --- a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt @@ -10,9 +10,12 @@ import com.intellij.mock.MockProject import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticModuleProvider import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPsiDeclarationProviderFactory import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar @@ -41,6 +44,7 @@ object StandaloneModeTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { projectEnvironment.environment.jarFileSystem as CoreJarFileSystem ) ) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmFirDeserializedSymbolProviderFactory::class.java) } } diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt index 98eda7efb3f..f9ea551a7e5 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt @@ -266,7 +266,11 @@ public class DebugSymbolRenderer( withIndent { appendLine().append("psi: ") - renderValue(call.psi?.javaClass?.simpleName, renderSymbolsFully = false) + val psi = + if (call.psi?.containingKtFile?.isCompiled == true) { + null + } else call.psi + renderValue(psi?.javaClass?.simpleName, renderSymbolsFully = false) } } diff --git a/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt deleted file mode 100644 index 94339479c70..00000000000 --- a/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt +++ /dev/null @@ -1,43 +0,0 @@ -KtNamedClassOrObjectSymbol: - annotationsList: [] - classIdIfNonLocal: java/lang/String - classKind: CLASS - companionObject: null - contextReceivers: [] - isData: false - isExternal: false - isFun: false - isInline: false - isInner: false - modality: FINAL - name: String - origin: JAVA - superTypes: [ - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: kotlin/Any - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: java/io/Serializable - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [ - KtFlexibleType: - annotationsList: [] - type: kotlin/String! - ] - type: kotlin/Comparable - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: kotlin/CharSequence - ] - symbolKind: TOP_LEVEL - typeParameters: [] - visibility: Public - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - - annotationApplicableTargets: null - deprecationStatus: null \ No newline at end of file diff --git a/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt index 465bc7f49ea..625a7edbba8 100644 --- a/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt +++ b/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt @@ -40,11 +40,9 @@ KtConstructorSymbol: type: (kotlin/collections/MutableCollection..kotlin/collections/Collection?) symbolKind: LOCAL typeParameters: [] - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null ] visibility: Public - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null \ No newline at end of file diff --git a/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt index 980a94efa6b..47820a24fd6 100644 --- a/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt +++ b/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt @@ -43,6 +43,5 @@ KtSamConstructorSymbol: deprecationStatus: null ] - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt index df7522253b5..d1d3378dba7 100644 --- a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt +++ b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt @@ -141,6 +141,7 @@ fun createStubForTypeName( val segments = fqName.pathSegments().asReversed() assert(segments.isNotEmpty()) + val packageLength = if (substituteWithAny) 1 else typeClassId.packageFqName.pathSegments().size fun recCreateStubForType(current: StubElement, level: Int): KotlinUserTypeStub { val lastSegment = segments[level] @@ -148,7 +149,7 @@ fun createStubForTypeName( if (level + 1 < segments.size) { recCreateStubForType(userTypeStub, level + 1) } - KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref()) + KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref(), level < segments.size - packageLength) if (!substituteWithAny) { bindTypeArguments(userTypeStub, level) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt index 96c6f186338..3446921147d 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt @@ -65,6 +65,10 @@ private constructor( return providers.flatMapTo(mutableListOf()) { it.findFilesForScript(scriptFqName) } } + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return providers.flatMapTo(mutableSetOf()) { it.computePackageSetWithTopLevelCallableDeclarations() } + } + companion object { fun create(providers: List): KotlinDeclarationProvider { return when (providers.size) { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt index 3cc5eb67d57..043790c86fb 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt @@ -25,4 +25,5 @@ internal object EmptyKotlinDeclarationProvider : KotlinDeclarationProvider() { override fun findFilesForFacade(facadeFqName: FqName) = emptyList() override fun findInternalFilesForFacade(facadeFqName: FqName) = emptyList() override fun findFilesForScript(scriptFqName: FqName) = emptyList() + override fun computePackageSetWithTopLevelCallableDeclarations(): Set = emptySet() } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt index 15f53209e09..303af4bf835 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt @@ -129,6 +129,9 @@ internal class FileBasedKotlinDeclarationProvider(val kotlinFile: KtFile) : Kotl } override fun findInternalFilesForFacade(facadeFqName: FqName): Collection = emptyList() + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return setOf(kotlinFile.packageFqName.asString()) + } override fun findFilesForScript(scriptFqName: FqName): Collection = listOfNotNull(kotlinFile.script?.takeIf { it.fqName == scriptFqName }) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt new file mode 100644 index 00000000000..ceb104f0361 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2023 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.low.level.api.fir.project.structure + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.FirJavaFacade +import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory + +open class JvmFirDeserializedSymbolProviderFactory { + open fun createJvmFirDeserializedSymbolProviders( + project: Project, + session: FirSession, + moduleData: LLFirModuleData, + kotlinScopeProvider: FirKotlinScopeProvider, + moduleDataProvider: SingleModuleDataProvider, + firJavaFacade: FirJavaFacade, + packagePartProvider: PackagePartProvider, + scope: GlobalSearchScope + ): List { + return listOf( + JvmClassFileBasedSymbolProvider( + session, + moduleDataProvider, + kotlinScopeProvider, + packagePartProvider, + VirtualFileFinderFactory.getInstance(project).create(scope), + firJavaFacade + ) + ) + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt index c1a78f5c89a..344ef802919 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirDependenci import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirJavaFacadeForBinaries import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubBasedFirDeserializedSymbolProvider import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider import org.jetbrains.kotlin.fir.BuiltinTypes +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider import org.jetbrains.kotlin.fir.java.deserialization.OptionalAnnotationClassesProvider @@ -20,6 +22,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.load.java.createJavaClassFinder import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.utils.addIfNotNull internal object LLFirLibraryProviderFactory { fun createLibraryProvidersForScope( @@ -31,21 +34,9 @@ internal object LLFirLibraryProviderFactory { scope: GlobalSearchScope, builtinSymbolProvider: FirSymbolProvider, ): LLFirModuleWithDependenciesSymbolProvider { - val moduleDataProvider = SingleModuleDataProvider(moduleData) - val packagePartProvider = project.createPackagePartProvider(scope) return LLFirModuleWithDependenciesSymbolProvider( session, - providers = listOfNotNull( - JvmClassFileBasedSymbolProvider( - session, - moduleDataProvider, - kotlinScopeProvider, - packagePartProvider, - VirtualFileFinderFactory.getInstance(project).create(scope), - LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) - ), - OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider), - ), + providers = createLibraryProvidersForAllProjectLibraries(session, moduleData, kotlinScopeProvider, project, builtinTypes, scope), LLFirDependenciesSymbolProvider(session, listOf(builtinSymbolProvider)), ) } @@ -60,16 +51,22 @@ internal object LLFirLibraryProviderFactory { ): List { val moduleDataProvider = SingleModuleDataProvider(moduleData) val packagePartProvider = project.createPackagePartProvider(scope) - return listOfNotNull( - JvmClassFileBasedSymbolProvider( - session, - moduleDataProvider, - kotlinScopeProvider, - packagePartProvider, - VirtualFileFinderFactory.getInstance(project).create(scope), - LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) - ), - OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider), - ) + return buildList { + val firJavaFacade = LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) + val service = project.getService(JvmFirDeserializedSymbolProviderFactory::class.java) + addAll( + service.createJvmFirDeserializedSymbolProviders( + project, + session, + moduleData, + kotlinScopeProvider, + moduleDataProvider, + firJavaFacade, + packagePartProvider, + scope + ) + ) + addIfNotNull(OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider)) + } } } \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt index 58b20561a3d..e3c4f978724 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt @@ -94,7 +94,7 @@ internal fun LLFirSourcesSession.registerCompilerPluginServices( } internal fun createJavaSymbolProvider( - firSession: LLFirSession, + firSession: FirSession, moduleData: LLFirModuleData, project: Project, contentScope: GlobalSearchScope diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt index 5e4a6db46c1..7b76f3abbcd 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -161,7 +162,7 @@ internal class LLFirDependenciesSymbolProvider( } private fun FirCallableSymbol<*>.jvmClassName(): JvmClassName? { - val jvmPackagePartSource = fir.containerSource as? JvmPackagePartSource ?: return null + val jvmPackagePartSource = fir.containerSource as? FacadeClassSource ?: return null return jvmPackagePartSource.facadeClassName ?: jvmPackagePartSource.className } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt index cdf839b72cf..83b720fdcfc 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt @@ -238,6 +238,10 @@ class LLFirResolveExtensionToolDeclarationProvider internal constructor( .mapNotNullTo(mutableListOf()) { it.kotlinFile.script } } + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return emptySet() //todo + } + private inline fun getDeclarationProvidersByPackage( packageFqName: FqName, crossinline filter: (KtResolveExtensionFile) -> Boolean diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt new file mode 100644 index 00000000000..c949ff9ab95 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2023 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.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.descriptors.SourceFile +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerAbiStability +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +//required for LLFirDependenciesSymbolProvider#jvmClassName, to resolve ambiguities +//todo check if moving builtins to stubs would solve the issue +class JvmFromStubDecompilerSource( + override val className: JvmClassName, + override val facadeClassName: JvmClassName? = null, + override val incompatibility: IncompatibleVersionErrorData? = null, + override val isPreReleaseInvisible: Boolean = false, + override val abiStability: DeserializedContainerAbiStability = DeserializedContainerAbiStability.STABLE, +) : DeserializedContainerSource, FacadeClassSource { + constructor(packageName: FqName) : + this(JvmClassName.byClassId(ClassId.topLevel(JvmClassName.byInternalName(packageName.asString()).fqNameForTopLevelClassMaybeWithDollars))) + + override fun getContainingFile(): SourceFile { + return SourceFile.NO_SOURCE_FILE + } + + override val presentableString: String + get() = className.internalName +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt new file mode 100644 index 00000000000..fb57d342922 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2023 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.low.level.api.fir.stubBased.deserialization + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.DelegatingGlobalSearchScope +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.createJavaSymbolProvider +import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.FirJavaFacade +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol + +class JvmStubBasedDeserializedSymbolProviderFactory : JvmFirDeserializedSymbolProviderFactory() { + override fun createJvmFirDeserializedSymbolProviders( + project: Project, + session: FirSession, + moduleData: LLFirModuleData, + kotlinScopeProvider: FirKotlinScopeProvider, + moduleDataProvider: SingleModuleDataProvider, + firJavaFacade: FirJavaFacade, + packagePartProvider: PackagePartProvider, + scope: GlobalSearchScope + ): List { + return buildList { + //stub based provider here works over kotlin-only indices and thus provides only kotlin declarations + //in order to find java declarations, one need to explicitly setup java symbol provider. + //for ProtoBuf based provider (used in compiler), there is no need in separated java provider, + //because all declarations are retrieved at once and are not distinguished + add( + JvmStubBasedFirDeserializedSymbolProvider( + session, + moduleDataProvider, + kotlinScopeProvider, + project.createDeclarationProvider(object : DelegatingGlobalSearchScope(project, scope) { + override fun contains(file: VirtualFile): Boolean { + if (file.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) return false + return super.contains(file) + } + }) + ) + ) + add(createJavaSymbolProvider(session, moduleData, project, scope)) + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt new file mode 100644 index 00000000000..fba7b9a6398 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt @@ -0,0 +1,209 @@ +/* + * 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.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolProviderNameCache +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirCache +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.deserialization.KotlinBuiltins +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtTypeAlias +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment + +typealias DeserializedTypeAliasPostProcessor = (FirTypeAliasSymbol) -> Unit + +/** + * [JvmStubBasedFirDeserializedSymbolProvider] works over existing stubs, + * retrieving them by classId/callableId from [KotlinDeclarationProvider]. + * + * It works in IDE only, in standalone mode works [JvmClassFileBasedSymbolProvider]. + * + * Because it works over existing stubs, there is no need to keep huge protobuf in memory. + * At the same time, there is no need to guess sources for fir elements anymore, + * they are set during deserialization. + * + * Same as [JvmClassFileBasedSymbolProvider], resulting fir elements are already resolved. + */ +class JvmStubBasedFirDeserializedSymbolProvider( + session: FirSession, + moduleDataProvider: SingleModuleDataProvider, + private val kotlinScopeProvider: FirKotlinScopeProvider, + private val declarationProvider: KotlinDeclarationProvider +) : FirSymbolProvider(session) { + private val moduleData = moduleDataProvider.getModuleData(null) + private val packageSetWithTopLevelCallableDeclarations: Set by lazy(LazyThreadSafetyMode.PUBLICATION) { + declarationProvider.computePackageSetWithTopLevelCallableDeclarations() + } + + private val namesByPackageCache = LLFirKotlinSymbolProviderNameCache(session, declarationProvider) + + private val typeAliasCache: FirCache = + session.firCachesFactory.createCacheWithPostCompute( + createValue = { classId, _ -> findAndDeserializeTypeAlias(classId) }, + postCompute = { _, symbol, postProcessor -> + if (postProcessor != null && symbol != null) { + postProcessor.invoke(symbol) + } + } + ) + private val classCache: FirCache = + session.firCachesFactory.createCache( + createValue = { classId, context -> findAndDeserializeClass(classId, context) } + ) + + private val functionCache = session.firCachesFactory.createCache(::loadFunctionsByCallableId) + private val propertyCache = session.firCachesFactory.createCache(::loadPropertiesByCallableId) + + override fun computePackageSetWithTopLevelCallables(): Set { + return packageSetWithTopLevelCallableDeclarations + } + + override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = + namesByPackageCache.getTopLevelCallableNamesInPackage(packageFqName) + + override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? { + return namesByPackageCache.getTopLevelClassifierNamesInPackage(packageFqName) + } + + private fun findAndDeserializeTypeAlias(classId: ClassId): Pair { + val classLikeDeclaration = declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement + if (classLikeDeclaration is KtTypeAlias) { + val symbol = FirTypeAliasSymbol(classId) + val postProcessor: DeserializedTypeAliasPostProcessor = { + val rootContext = StubBasedFirDeserializationContext.createRootContext( + moduleData, + StubBasedAnnotationDeserializer(session), + classId.packageFqName, + classId.relativeClassName, + classLikeDeclaration, + null, null, symbol + ) + rootContext.memberDeserializer.loadTypeAlias(classLikeDeclaration, symbol) + } + return symbol to postProcessor + } + return null to null + } + + private fun findAndDeserializeClass( + classId: ClassId, + parentContext: StubBasedFirDeserializationContext? = null + ): FirRegularClassSymbol? { + val classLikeDeclaration = declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement ?: return null + val symbol = FirRegularClassSymbol(classId) + if (classLikeDeclaration is KtClassOrObject) { + deserializeClassToSymbol( + classId, + classLikeDeclaration, + symbol, + session, + moduleData, + StubBasedAnnotationDeserializer(session), + kotlinScopeProvider, + parentContext, + JvmFromStubDecompilerSource(JvmClassName.byClassId(classId)), + deserializeNestedClass = this::getClass, + ) + return symbol + } + return null + } + + private fun loadFunctionsByCallableId(callableId: CallableId): List { + val topLevelFunctions = declarationProvider.getTopLevelFunctions(callableId) + val origins = if (topLevelFunctions.size > 1) mutableSetOf() else null + return topLevelFunctions + .mapNotNull { function -> + val original = function.originalElement as? KtNamedFunction ?: return@mapNotNull null + if (origins != null && !origins.add(original)) return@mapNotNull null + val file = original.containingKtFile + val virtualFile = file.virtualFile + if (virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION) return@mapNotNull null + if (file.packageFqName.asString() + .replace(".", "/") + "/" + virtualFile.nameWithoutExtension in KotlinBuiltins + ) return@mapNotNull null + val symbol = FirNamedFunctionSymbol(callableId) + val rootContext = + StubBasedFirDeserializationContext.createRootContext(session, moduleData, callableId, original, symbol) + rootContext.memberDeserializer.loadFunction(original, null, session, symbol).symbol + } + } + + private fun loadPropertiesByCallableId(callableId: CallableId): List { + val topLevelProperties = declarationProvider.getTopLevelProperties(callableId) + val origins = if (topLevelProperties.size > 1) mutableSetOf() else null + return topLevelProperties + .mapNotNull { property -> + val original = property.originalElement as? KtProperty ?: return@mapNotNull null + if (origins != null && !origins.add(original)) return@mapNotNull null + val symbol = FirPropertySymbol(callableId) + val rootContext = + StubBasedFirDeserializationContext.createRootContext(session, moduleData, callableId, original, symbol) + rootContext.memberDeserializer.loadProperty(original, null, symbol).symbol + } + } + + private fun getClass( + classId: ClassId, + parentContext: StubBasedFirDeserializationContext? = null + ): FirRegularClassSymbol? { + return classCache.getValue(classId, parentContext) + } + + private fun getTypeAlias(classId: ClassId): FirTypeAliasSymbol? { + if (!classId.relativeClassName.isOneSegmentFQN()) return null + return typeAliasCache.getValue(classId) + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { + val callableId = CallableId(packageFqName, name) + destination += functionCache.getCallables(callableId) + destination += propertyCache.getCallables(callableId) + } + + private fun > FirCache, Nothing?>.getCallables(id: CallableId): List { + if (id.packageName.asString() !in packageSetWithTopLevelCallableDeclarations) return emptyList() + if (!namesByPackageCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList() + return getValue(id) + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + destination += functionCache.getCallables(CallableId(packageFqName, name)) + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + destination += propertyCache.getCallables(CallableId(packageFqName, name)) + } + + override fun getPackage(fqName: FqName): FqName? { + return if (!namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName) + .isNullOrEmpty() || packageSetWithTopLevelCallableDeclarations.contains(fqName.asString()) + ) { + fqName + } else null + } + + override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { + if (!namesByPackageCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null + return getClass(classId) ?: getTypeAlias(classId) + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt new file mode 100644 index 00000000000..1803e6ecb26 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2010-2020 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.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.constant.* +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.collectEnumEntries +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.builder.* +import org.jetbrains.kotlin.fir.references.builder.buildFromMissingDependenciesNamedReference +import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.* +import org.jetbrains.kotlin.types.ConstantValueKind + +class StubBasedAnnotationDeserializer( + private val session: FirSession, +) { + //fun inheritAnnotationInfo(parent: StubBasedAnnotationDeserializer) {} + + fun loadAnnotations( + ktAnnotated: KtAnnotated, + useSiteTarget: AnnotationUseSiteTarget? = null + ): List { + val annotations = ktAnnotated.annotationEntries + if (annotations.isEmpty()) return emptyList() + return annotations.map { deserializeAnnotation(it, useSiteTarget) } + } + + private val constantCache = mutableMapOf() + + fun loadConstant(property: KtProperty, callableId: CallableId): FirExpression? { + if (!property.hasModifier(KtTokens.CONST_KEYWORD)) return null + constantCache[callableId]?.let { return it } + val propertyStub = property.stub as? KotlinPropertyStubImpl ?: return null + val constantValue = propertyStub.constantInitializer ?: return null + return resolveValue(property, constantValue) + } + + private fun deserializeAnnotation( + ktAnnotation: KtAnnotationEntry, + useSiteTarget: AnnotationUseSiteTarget? = null + ): FirAnnotation { + val userType = + ktAnnotation.getStubOrPsiChild(KtStubElementTypes.CONSTRUCTOR_CALLEE)?.getStubOrPsiChild(KtStubElementTypes.TYPE_REFERENCE) + ?.getStubOrPsiChild(KtStubElementTypes.USER_TYPE)!! + return deserializeAnnotation( + ktAnnotation, + userType.classId(), + (ktAnnotation.stub as? KotlinAnnotationEntryStubImpl)?.valueArguments, + useSiteTarget + ) + } + + private fun deserializeAnnotation( + ktAnnotation: PsiElement, + classId: ClassId, + valueArguments: Map>?, + useSiteTarget: AnnotationUseSiteTarget? = null + ): FirAnnotation { + return buildAnnotation { + source = KtRealPsiSourceElement(ktAnnotation) + annotationTypeRef = buildResolvedTypeRef { + type = classId.toLookupTag().constructClassType(emptyArray(), isNullable = false) + } + session.lazyDeclarationResolver.disableLazyResolveContractChecksInside { + this.argumentMapping = buildAnnotationArgumentMapping { + valueArguments?.forEach { (name, constantValue) -> + mapping[name] = resolveValue(ktAnnotation, constantValue) + } + } + } + useSiteTarget?.let { + this.useSiteTarget = it + } + } + } + + private fun resolveValue( + sourceElement: PsiElement, + value: ConstantValue<*> + ): FirExpression { + return when (value) { + is EnumValue -> sourceElement.toEnumEntryReferenceExpression(value.enumClassId, value.enumEntryName) + is KClassValue -> buildGetClassCall { + source = KtRealPsiSourceElement(sourceElement) + val lookupTag = (value.value as KClassValue.Value.NormalClass).classId.toLookupTag() + val referencedType = lookupTag.constructType(emptyArray(), isNullable = false) + val resolvedTypeRef = buildResolvedTypeRef { + type = StandardClassIds.KClass.constructClassLikeType(arrayOf(referencedType), false) + } + argumentList = buildUnaryArgumentList( + buildClassReferenceExpression { + classTypeRef = buildResolvedTypeRef { type = referencedType } + typeRef = resolvedTypeRef + } + ) + } + is ArrayValue -> buildArrayOfCall { + source = KtRealPsiSourceElement(sourceElement) + argumentList = buildArgumentList { + value.value.mapTo(arguments) { resolveValue(sourceElement, it) } + } +// typeRef = buildResolvedTypeRef { +// type = expectedArrayElementType.createArrayType() //todo +// } + } + is AnnotationValue -> { + deserializeAnnotation( + sourceElement, + (value.value.type as KotlinClassTypeBean).classId, + value.value.argumentsMapping + ) + } + is BooleanValue -> const(ConstantValueKind.Boolean, value.value, session.builtinTypes.booleanType, sourceElement) + is ByteValue -> const(ConstantValueKind.Byte, value.value, session.builtinTypes.byteType, sourceElement) + is CharValue -> const(ConstantValueKind.Char, value.value, session.builtinTypes.charType, sourceElement) + is ShortValue -> const(ConstantValueKind.Short, value.value, session.builtinTypes.shortType, sourceElement) + is LongValue -> const(ConstantValueKind.Long, value.value, session.builtinTypes.longType, sourceElement) + is FloatValue -> const(ConstantValueKind.Float, value.value, session.builtinTypes.floatType, sourceElement) + is DoubleValue -> const(ConstantValueKind.Double, value.value, session.builtinTypes.doubleType, sourceElement) + is UByteValue -> const(ConstantValueKind.UnsignedByte, value.value, session.builtinTypes.byteType, sourceElement) + is UShortValue -> const(ConstantValueKind.UnsignedShort, value.value, session.builtinTypes.shortType, sourceElement) + is UIntValue -> const(ConstantValueKind.UnsignedInt, value.value, session.builtinTypes.intType, sourceElement) + is ULongValue -> const(ConstantValueKind.UnsignedLong, value.value, session.builtinTypes.longType, sourceElement) + is IntValue -> const(ConstantValueKind.Int, value.value, session.builtinTypes.intType, sourceElement) + NullValue -> const(ConstantValueKind.Null, null, session.builtinTypes.nullableAnyType, sourceElement) + is StringValue -> const(ConstantValueKind.String, value.value, session.builtinTypes.stringType, sourceElement) + else -> error("Unexpected value $value") + } + } + + private fun const(kind: ConstantValueKind, value: T, typeRef: FirResolvedTypeRef, sourceElement: PsiElement): FirConstExpression { + return buildConstExpression( + KtRealPsiSourceElement(sourceElement), + kind, + value, + setType = true + ).apply { this.replaceTypeRef(typeRef) } + } + + private fun PsiElement.toEnumEntryReferenceExpression(classId: ClassId, entryName: Name): FirExpression { + return buildPropertyAccessExpression { + source = KtRealPsiSourceElement(this@toEnumEntryReferenceExpression) + val enumLookupTag = classId.toLookupTag() + val enumSymbol = enumLookupTag.toSymbol(session) + val firClass = enumSymbol?.fir as? FirRegularClass + val enumEntries = firClass?.collectEnumEntries() ?: emptyList() + val enumEntrySymbol = enumEntries.find { it.name == entryName } + calleeReference = enumEntrySymbol?.let { + buildResolvedNamedReference { + name = entryName + resolvedSymbol = it.symbol + } + } ?: buildFromMissingDependenciesNamedReference { + name = entryName + } + if (enumEntrySymbol != null) { + typeRef = enumEntrySymbol.returnTypeRef + } + } + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt new file mode 100644 index 00000000000..cf829439a37 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2010-2020 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.low.level.api.fir.stubBased.deserialization + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.KtFakeSourceElement +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.builder.createDataClassCopyFunction +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.comparators.FirMemberDeclarationComparator +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.deserialization.* +import org.jetbrains.kotlin.fir.scopes.FirScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +internal val KtModifierListOwner.visibility: Visibility + get() = with(modifierList) { + when { + this == null -> Visibilities.Public + hasModifier(KtTokens.PRIVATE_KEYWORD) -> Visibilities.Private + hasModifier(KtTokens.PUBLIC_KEYWORD) -> Visibilities.Public + hasModifier(KtTokens.PROTECTED_KEYWORD) -> Visibilities.Protected + else -> if (hasModifier(KtTokens.INTERNAL_KEYWORD)) Visibilities.Internal else Visibilities.Public + } + } + +internal val KtDeclaration.modality: Modality + get() { + return when { + hasModifier(KtTokens.SEALED_KEYWORD) -> Modality.SEALED + hasModifier(KtTokens.ABSTRACT_KEYWORD) || this is KtClass && isInterface() -> Modality.ABSTRACT + hasModifier(KtTokens.OPEN_KEYWORD) -> Modality.OPEN + else -> Modality.FINAL + } + } + +fun deserializeClassToSymbol( + classId: ClassId, + classOrObject: KtClassOrObject, + symbol: FirRegularClassSymbol, + session: FirSession, + moduleData: FirModuleData, + defaultAnnotationDeserializer: StubBasedAnnotationDeserializer?, + scopeProvider: FirScopeProvider, + parentContext: StubBasedFirDeserializationContext? = null, + containerSource: DeserializedContainerSource? = null, + deserializeNestedClass: (ClassId, StubBasedFirDeserializationContext) -> FirRegularClassSymbol? +) { + val kind = when (classOrObject) { + is KtObjectDeclaration -> ClassKind.OBJECT + is KtClass -> when { + classOrObject.isInterface() -> ClassKind.INTERFACE + classOrObject.isEnum() -> ClassKind.ENUM_CLASS + classOrObject.isAnnotation() -> ClassKind.ANNOTATION_CLASS + else -> ClassKind.CLASS + } + else -> throw AssertionError("Unexpected class or object: ${classOrObject.text}") + } + val modality = classOrObject.modality + val visibility = classOrObject.visibility + val status = FirResolvedDeclarationStatusImpl( + visibility, + modality, + visibility.toEffectiveVisibility(parentContext?.outerClassSymbol, forClass = true) + ).apply { + isExpect = classOrObject.hasModifier(KtTokens.EXPECT_KEYWORD) + isActual = false + isInner = classOrObject.hasModifier(KtTokens.INNER_KEYWORD) + isCompanion = (classOrObject as? KtObjectDeclaration)?.isCompanion() == true + isData = classOrObject.hasModifier(KtTokens.DATA_KEYWORD) + isInline = classOrObject.hasModifier(KtTokens.INLINE_KEYWORD) || classOrObject.hasModifier(KtTokens.VALUE_KEYWORD) + isFun = classOrObject.hasModifier(KtTokens.FUN_KEYWORD) + isExternal = classOrObject.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + val annotationDeserializer = defaultAnnotationDeserializer ?: StubBasedAnnotationDeserializer(session) + val context = + parentContext?.childContext( + classOrObject, + classId.relativeClassName, + containerSource, + symbol, + annotationDeserializer, + status.isInner + ) ?: StubBasedFirDeserializationContext.createForClass( + classId, + classOrObject, + moduleData, + annotationDeserializer, + containerSource, + symbol + ) +// if (status.isCompanion) { +// parentContext?.let { +// context.annotationDeserializer.inheritAnnotationInfo(it.annotationDeserializer) +// } +// } + buildRegularClass { + source = KtRealPsiSourceElement(classOrObject) + this.moduleData = moduleData + this.origin = FirDeclarationOrigin.Library + name = classId.shortClassName + this.status = status + classKind = kind + this.scopeProvider = scopeProvider + this.symbol = symbol + + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + + typeParameters += context.typeDeserializer.ownTypeParameters.map { it.fir } + if (status.isInner) + typeParameters += parentContext?.allTypeParameters?.map { buildOuterClassTypeParameterRef { this.symbol = it } }.orEmpty() + + val typeDeserializer = context.typeDeserializer + val memberDeserializer = context.memberDeserializer + + val superTypeList = classOrObject.getSuperTypeList() + if (superTypeList != null) { + superTypeRefs.addAll(superTypeList.entries.map { + typeDeserializer.typeRef( + it.typeReference ?: error("Super entry doesn't have type reference $it") + ) + }) + } else if (StandardClassIds.Any != classId) { + superTypeRefs.add(session.builtinTypes.anyType) + } + + val firPrimaryConstructor = classOrObject.primaryConstructor?.let { + val constructor = memberDeserializer.loadConstructor(it, classOrObject, this) + addDeclaration(constructor) + constructor + } + classOrObject.body?.declarations?.forEach { declaration -> + when (declaration) { + is KtConstructor<*> -> addDeclaration(memberDeserializer.loadConstructor(declaration, classOrObject, this)) + is KtNamedFunction -> addDeclaration(memberDeserializer.loadFunction(declaration, symbol, session)) + is KtProperty -> addDeclaration(memberDeserializer.loadProperty(declaration, symbol)) + is KtEnumEntry -> addDeclaration(memberDeserializer.loadEnumEntry(declaration, symbol, classId)) + is KtClassOrObject -> { + val nestedClassId = + classId.createNestedClassId(Name.identifier(declaration.name ?: error("Class doesn't have name $declaration"))) + deserializeNestedClass(nestedClassId, context)?.fir?.let { addDeclaration(it) } + } + } + } + + if (classKind == ClassKind.ENUM_CLASS) { + generateValuesFunction( + moduleData, + classId.packageFqName, + classId.relativeClassName, + origin = FirDeclarationOrigin.Library + ) + generateValueOfFunction(moduleData, classId.packageFqName, classId.relativeClassName, origin = FirDeclarationOrigin.Library) + generateEntriesGetter(moduleData, classId.packageFqName, classId.relativeClassName, origin = FirDeclarationOrigin.Library) + } + + if (classOrObject.isData() && firPrimaryConstructor != null) { + val zippedParameters = + classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() } zip declarations.filterIsInstance() + addDeclaration(createDataClassCopyFunction(classId, classOrObject, context.dispatchReceiver, zippedParameters, + createClassTypeRefWithSourceKind = { firPrimaryConstructor.returnTypeRef.copyWithNewSourceKind(it) }, + createParameterTypeRefWithSourceKind = { property, newKind -> + property.returnTypeRef.copyWithNewSourceKind(newKind) + }) { src, kind -> + KtFakeSourceElement(src as PsiElement, kind) + }) + } + + addCloneForArrayIfNeeded(classId, context.dispatchReceiver) + session.deserializedClassConfigurator?.run { + configure(classId) + } + + declarations.sortWith(object : Comparator { + override fun compare(a: FirDeclaration, b: FirDeclaration): Int { + // Reorder members based on their type and name only. + // See FE 1.0's [DeserializedMemberScope#addMembers]. + if (a is FirMemberDeclaration && b is FirMemberDeclaration) { + return FirMemberDeclarationComparator.TypeAndNameComparator.compare(a, b) + } + return 0 + } + }) + companionObjectSymbol = (declarations.firstOrNull { it is FirRegularClass && it.isCompanion } as FirRegularClass?)?.symbol + + contextReceivers.addAll(memberDeserializer.createContextReceiversForClass(classOrObject)) + }.apply { + //todo sealed inheritors + //if (modality == Modality.SEALED) { +// val inheritors = classOrObject.sealedSubclassFqNameList.map { nameIndex -> +// ClassId.fromString(nameResolver.getQualifiedClassName(nameIndex)) +// } +// setSealedClassInheritors(inheritors) + //} + + valueClassRepresentation = computeValueClassRepresentation(this, session) + + replaceAnnotations( + context.annotationDeserializer.loadAnnotations(classOrObject) + ) + + + sourceElement = containerSource + + replaceDeprecationsProvider(getDeprecationsProvider(session)) + + session.deserializedClassConfigurator?.run { + configure(classId) + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt new file mode 100644 index 00000000000..c83c2bb3458 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt @@ -0,0 +1,150 @@ +/* + * Copyright 2010-2023 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.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.contracts.description.* +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.builder.buildEffectDeclaration +import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription +import org.jetbrains.kotlin.fir.contracts.description.* +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.stubs.impl.* + +class StubBasedFirContractDeserializer( + private val simpleFunction: FirSimpleFunction, + private val typeDeserializer: StubBasedFirTypeDeserializer +) { + fun loadContract(function: KtNamedFunction): FirContractDescription? { + val functionStub = function.stub as? KotlinFunctionStubImpl ?: return null + val effects = functionStub.contract?.map { + it.accept(ContractDescriptionConvertingVisitor(), null) + } + + if (effects.isNullOrEmpty()) return null + return buildResolvedContractDescription { + this.effects += effects.map { description -> + buildEffectDeclaration { + effect = description as ConeEffectDeclaration + } + } + } + } + + inner class ContractDescriptionConvertingVisitor : + KtContractDescriptionVisitor() { + override fun visitCallsEffectDeclaration( + callsEffect: KtCallsEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeCallsEffectDeclaration( + callsEffect.valueParameterReference.accept( + this, + data + ) as KtValueParameterReference, callsEffect.kind + ) + } + + override fun visitReturnsEffectDeclaration( + returnsEffect: KtReturnsEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeReturnsEffectDeclaration( + returnsEffect.value.accept( + this, + data + ) as KtConstantReference + ) + } + + override fun visitConditionalEffectDeclaration( + conditionalEffect: KtConditionalEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeConditionalEffectDeclaration( + conditionalEffect.effect.accept(this, data) as KtEffectDeclaration, + conditionalEffect.condition.accept(this, data) as KtBooleanExpression + ) + } + + override fun visitLogicalBinaryOperationContractExpression( + binaryLogicExpression: KtBinaryLogicExpression, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeBinaryLogicExpression( + binaryLogicExpression.left.accept(this, data) as KtBooleanExpression, + binaryLogicExpression.right.accept(this, data) as KtBooleanExpression, + binaryLogicExpression.kind + ) + } + + override fun visitLogicalNot( + logicalNot: KtLogicalNot, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeLogicalNot(logicalNot.arg.accept(this, data) as KtBooleanExpression) + } + + override fun visitIsInstancePredicate( + isInstancePredicate: KtIsInstancePredicate, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeIsInstancePredicate( + isInstancePredicate.arg.accept(this, data) as KtValueParameterReference, + typeDeserializer.type(isInstancePredicate.type)!!, + isInstancePredicate.isNegated + ) + } + + override fun visitIsNullPredicate( + isNullPredicate: KtIsNullPredicate, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeIsNullPredicate( + isNullPredicate.arg.accept(this, data) as KtValueParameterReference, + isNullPredicate.isNegated + ) + } + + override fun visitValueParameterReference( + valueParameterReference: KtValueParameterReference, + data: Nothing? + ): ConeContractDescriptionElement { + val parameterIndex = valueParameterReference.parameterIndex + return ConeValueParameterReference(parameterIndex, getParameterName(parameterIndex)) + } + + override fun visitBooleanValueParameterReference( + booleanValueParameterReference: KtBooleanValueParameterReference, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeBooleanValueParameterReference(booleanValueParameterReference.parameterIndex, + getParameterName(booleanValueParameterReference.parameterIndex)) + } + + private fun getParameterName(parameterIndex: Int): String { + return if (parameterIndex < 0) "this" else simpleFunction.valueParameters[parameterIndex].name.asString() + } + + override fun visitConstantDescriptor( + constantReference: KtConstantReference, + data: Nothing? + ): ConeContractDescriptionElement { + return when (constantReference) { + KotlinContractConstantValues.FALSE -> ConeContractConstantValues.FALSE + KotlinContractConstantValues.TRUE -> ConeContractConstantValues.TRUE + KotlinContractConstantValues.NULL -> ConeContractConstantValues.NULL + KotlinContractConstantValues.NOT_NULL -> ConeContractConstantValues.NOT_NULL + KotlinContractConstantValues.WILDCARD -> ConeContractConstantValues.WILDCARD + else -> { + error("Unexpected constant: $constantReference") + } + } + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt new file mode 100644 index 00000000000..2a8962e7fd3 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt @@ -0,0 +1,547 @@ +/* + * 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.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.KtFakeSourceElement +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.declarations.utils.sourceElement +import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionStub +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.toEffectiveVisibility +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +class StubBasedFirDeserializationContext( + val moduleData: FirModuleData, + val packageFqName: FqName, + val relativeClassName: FqName?, + val typeDeserializer: StubBasedFirTypeDeserializer, + val annotationDeserializer: StubBasedAnnotationDeserializer, + val containerSource: DeserializedContainerSource?, + val outerClassSymbol: FirRegularClassSymbol?, + val outerTypeParameters: List +) { + val session: FirSession = moduleData.session + + val allTypeParameters: List = + typeDeserializer.ownTypeParameters + outerTypeParameters + + fun childContext( + owner: KtTypeParameterListOwner, + relativeClassName: FqName? = this.relativeClassName, + containerSource: DeserializedContainerSource? = this.containerSource, + outerClassSymbol: FirRegularClassSymbol? = this.outerClassSymbol, + annotationDeserializer: StubBasedAnnotationDeserializer = this.annotationDeserializer, + capturesTypeParameters: Boolean = true, + containingDeclarationSymbol: FirBasedSymbol<*>? = this.outerClassSymbol + ): StubBasedFirDeserializationContext = StubBasedFirDeserializationContext( + moduleData, + packageFqName, + relativeClassName, + StubBasedFirTypeDeserializer( + moduleData, + annotationDeserializer, + typeDeserializer, + containingDeclarationSymbol, + owner + ), + annotationDeserializer, + containerSource, + outerClassSymbol, + if (capturesTypeParameters) allTypeParameters else emptyList() + ) + + val memberDeserializer: StubBasedFirMemberDeserializer = StubBasedFirMemberDeserializer(this) + val dispatchReceiver = relativeClassName?.let { ClassId(packageFqName, it, false).defaultType(allTypeParameters) } + + companion object { + + fun createForClass( + classId: ClassId, + classOrObject: KtClassOrObject, + moduleData: FirModuleData, + annotationDeserializer: StubBasedAnnotationDeserializer, + containerSource: DeserializedContainerSource?, + outerClassSymbol: FirRegularClassSymbol + ): StubBasedFirDeserializationContext = createRootContext( + moduleData, + annotationDeserializer, + classId.packageFqName, + classId.relativeClassName, + classOrObject, + containerSource, + outerClassSymbol, + outerClassSymbol + ) + + fun createRootContext( + moduleData: FirModuleData, + annotationDeserializer: StubBasedAnnotationDeserializer, + packageFqName: FqName, + relativeClassName: FqName?, + owner: KtTypeParameterListOwner, + containerSource: DeserializedContainerSource?, + outerClassSymbol: FirRegularClassSymbol?, + containingDeclarationSymbol: FirBasedSymbol<*>? + ): StubBasedFirDeserializationContext = StubBasedFirDeserializationContext( + moduleData, + packageFqName, + relativeClassName, + StubBasedFirTypeDeserializer( + moduleData, + annotationDeserializer, + null, + containingDeclarationSymbol, + owner + ), + annotationDeserializer, + containerSource, + outerClassSymbol, + emptyList() + ) + + fun createRootContext( + session: FirSession, + moduleData: FirModuleData, + callableId: CallableId, + parameterListOwner: KtTypeParameterListOwner, + symbol: FirBasedSymbol<*> + ): StubBasedFirDeserializationContext = createRootContext( + moduleData, + StubBasedAnnotationDeserializer(session), + callableId.packageName, + callableId.className, + parameterListOwner, + JvmFromStubDecompilerSource(callableId.packageName), + null, + symbol + ) + } +} + +class StubBasedFirMemberDeserializer(private val c: StubBasedFirDeserializationContext) { + + fun loadTypeAlias(typeAlias: KtTypeAlias, aliasSymbol: FirTypeAliasSymbol): FirTypeAlias { + val name = typeAlias.nameAsSafeName + val local = c.childContext(typeAlias, containingDeclarationSymbol = aliasSymbol) + return buildTypeAlias { + source = KtRealPsiSourceElement(typeAlias) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.name = name + val visibility = typeAlias.visibility + status = FirResolvedDeclarationStatusImpl( + visibility, + Modality.FINAL, + visibility.toEffectiveVisibility(owner = null) + ).apply { + isExpect = typeAlias.hasModifier(KtTokens.EXPECT_KEYWORD) + isActual = false + } + + annotations += c.annotationDeserializer.loadAnnotations(typeAlias) + symbol = aliasSymbol + expandedTypeRef = typeAlias.getTypeReference()?.toTypeRef(local) ?: error("Type alias doesn't have type reference $typeAlias") + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + }.apply { + sourceElement = c.containerSource + } + } + + private fun loadPropertyGetter( + getter: KtPropertyAccessor, + classSymbol: FirClassSymbol<*>?, + returnTypeRef: FirTypeRef, + propertySymbol: FirPropertySymbol, + ): FirPropertyAccessor { + val visibility = getter.visibility + val accessorModality = getter.modality + val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol) + return buildPropertyAccessor { + source = KtRealPsiSourceElement(getter) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = returnTypeRef + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + isGetter = true + status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply { + isInline = getter.hasModifier(KtTokens.INLINE_KEYWORD) + isExternal = getter.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + this.symbol = FirPropertyAccessorSymbol() + dispatchReceiverType = c.dispatchReceiver + this.propertySymbol = propertySymbol + }.apply { + replaceAnnotations( + c.annotationDeserializer.loadAnnotations( + getter, AnnotationUseSiteTarget.PROPERTY_GETTER + ) + ) + containingClassForStaticMemberAttr = c.dispatchReceiver?.lookupTag + } + } + + private fun loadPropertySetter( + setter: KtPropertyAccessor, + classSymbol: FirClassSymbol<*>?, + propertySymbol: FirPropertySymbol, + local: StubBasedFirDeserializationContext, + ): FirPropertyAccessor { + val visibility = setter.visibility + val accessorModality = setter.modality + val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol) + return buildPropertyAccessor { + source = KtRealPsiSourceElement(setter) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = FirImplicitUnitTypeRef(source) + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + isGetter = false + status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply { + isInline = setter.hasModifier(KtTokens.INLINE_KEYWORD) + isExternal = setter.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + this.symbol = FirPropertyAccessorSymbol() + dispatchReceiverType = c.dispatchReceiver + valueParameters += local.memberDeserializer.valueParameters( + setter.valueParameters, + symbol + ) + this.propertySymbol = propertySymbol + }.apply { + replaceAnnotations( + c.annotationDeserializer.loadAnnotations( + setter, AnnotationUseSiteTarget.PROPERTY_SETTER + ) + ) + containingClassForStaticMemberAttr = c.dispatchReceiver?.lookupTag + } + } + + fun loadProperty( + property: KtProperty, + classSymbol: FirClassSymbol<*>? = null, + existingSymbol: FirPropertySymbol? = null + ): FirProperty { + val callableName = property.nameAsSafeName + val callableId = CallableId(c.packageFqName, c.relativeClassName, callableName) + val symbol = existingSymbol ?: FirPropertySymbol(callableId) + val local = c.childContext(property, containingDeclarationSymbol = symbol) + + val returnTypeRef = property.typeReference?.toTypeRef(local) ?: error("Property doesn't have type reference, $property") + + val getter = property.getter + val receiverTypeReference = property.receiverTypeReference + val receiverAnnotations = if (getter != null && receiverTypeReference != null) { + c.annotationDeserializer.loadAnnotations(receiverTypeReference, AnnotationUseSiteTarget.PROPERTY_GETTER) + } else { + emptyList() + } + + val propertyModality = property.modality + + val isVar = property.isVar + return buildProperty { + source = KtRealPsiSourceElement(property) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = returnTypeRef + receiverParameter = receiverTypeReference?.toTypeRef(local)?.let { receiverType -> + buildReceiverParameter { + typeRef = receiverType + annotations += receiverAnnotations + } + } + + name = callableName + this.isVar = isVar + this.symbol = symbol + dispatchReceiverType = c.dispatchReceiver + isLocal = false + val visibility = property.visibility + status = FirResolvedDeclarationStatusImpl(visibility, propertyModality, visibility.toEffectiveVisibility(classSymbol)).apply { + isExpect = property.hasExpectModifier() + isActual = false + isOverride = false + isConst = property.hasModifier(KtTokens.CONST_KEYWORD) + isLateInit = property.hasModifier(KtTokens.LATEINIT_KEYWORD) + isExternal = property.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + annotations += + c.annotationDeserializer.loadAnnotations(property, AnnotationUseSiteTarget.PROPERTY) + if (getter != null) { + this.getter = loadPropertyGetter( + getter, + classSymbol, + returnTypeRef, + symbol + ) + } + val setter = property.setter + if (setter != null) { + this.setter = loadPropertySetter( + setter, + classSymbol, + symbol, + local + ) + } + this.containerSource = c.containerSource + this.initializer = c.annotationDeserializer.loadConstant(property, symbol.callableId) + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + + property.contextReceivers.mapNotNull { it.typeReference() }.mapTo(contextReceivers, ::loadContextReceiver) + } + } + + private fun loadContextReceiver(typeReference: KtTypeReference): FirContextReceiver { + val typeRef = typeReference.toTypeRef(c) + return buildContextReceiver { + source = KtRealPsiSourceElement(typeReference) + val type = typeRef.coneType + this.labelNameFromTypeRef = (type as? ConeLookupTagBasedType)?.lookupTag?.name + this.typeRef = typeRef + } + } + + internal fun createContextReceiversForClass(classOrObject: KtClassOrObject): List = + classOrObject.contextReceivers.mapNotNull { it.typeReference() }.map(::loadContextReceiver) + + fun loadFunction( + function: KtNamedFunction, + classSymbol: FirClassSymbol<*>? = null, + session: FirSession, + existingSymbol: FirNamedFunctionSymbol? = null + ): FirSimpleFunction { + val receiverAnnotations = if (function.receiverTypeReference != null) { + c.annotationDeserializer.loadAnnotations( + function + ) + } else { + emptyList() + } + + val callableName = function.nameAsSafeName + val callableId = CallableId(c.packageFqName, c.relativeClassName, callableName) + val symbol = existingSymbol ?: FirNamedFunctionSymbol(callableId) + val local = c.childContext(function, containingDeclarationSymbol = symbol) + + val simpleFunction = buildSimpleFunction { + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + source = KtRealPsiSourceElement(function) + returnTypeRef = function.typeReference?.toTypeRef(local) ?: session.builtinTypes.unitType + receiverParameter = function.receiverTypeReference?.toTypeRef(local)?.let { receiverType -> + buildReceiverParameter { + typeRef = receiverType + annotations += receiverAnnotations + } + } + + name = callableName + val visibility = function.visibility + status = FirResolvedDeclarationStatusImpl( + visibility, + function.modality, + visibility.toEffectiveVisibility(classSymbol) + ).apply { + isExpect = function.hasExpectModifier() + isActual = false + isOverride = false + isOperator = function.hasModifier(KtTokens.OPERATOR_KEYWORD) + isInfix = function.hasModifier(KtTokens.INFIX_KEYWORD) + isInline = function.hasModifier(KtTokens.INLINE_KEYWORD) + isTailRec = function.hasModifier(KtTokens.TAILREC_KEYWORD) + isExternal = function.hasModifier(KtTokens.EXTERNAL_KEYWORD) + isSuspend = function.hasModifier(KtTokens.SUSPEND_KEYWORD) + } + this.symbol = symbol + dispatchReceiverType = c.dispatchReceiver + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + valueParameters += local.memberDeserializer.valueParameters( + function.valueParameters, + symbol + ) + annotations += + c.annotationDeserializer.loadAnnotations(function) + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + this.containerSource = c.containerSource + + function.contextReceivers.mapNotNull { it.typeReference() }.mapTo(contextReceivers, ::loadContextReceiver) + } + if (function.mayHaveContract()) { + val resolvedDescription = StubBasedFirContractDeserializer(simpleFunction, local.typeDeserializer).loadContract(function) + if (resolvedDescription != null) { + simpleFunction.replaceContractDescription(resolvedDescription) + } + } + return simpleFunction + } + + fun loadConstructor( + constructor: KtConstructor<*>, + classOrObject: KtClassOrObject, + classBuilder: FirRegularClassBuilder + ): FirConstructor { + val relativeClassName = c.relativeClassName!! + val callableId = CallableId(c.packageFqName, relativeClassName, relativeClassName.shortName()) + val symbol = FirConstructorSymbol(callableId) + val local = c.childContext(constructor, containingDeclarationSymbol = symbol) + val isPrimary = constructor is KtPrimaryConstructor + + val typeParameters = classBuilder.typeParameters + + val delegatedSelfType = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + classBuilder.symbol.toLookupTag(), + typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) }.toTypedArray(), + false + ) + source = KtFakeSourceElement(classOrObject, KtFakeSourceElementKind.ClassSelfTypeRef) + } + + return if (isPrimary) { + FirPrimaryConstructorBuilder() + } else { + FirConstructorBuilder() + }.apply { + moduleData = c.moduleData + source = KtRealPsiSourceElement(constructor) + origin = FirDeclarationOrigin.Library + returnTypeRef = delegatedSelfType + val visibility = constructor.visibility + val isInner = classBuilder.status.isInner + status = FirResolvedDeclarationStatusImpl( + visibility, + Modality.FINAL, + visibility.toEffectiveVisibility(classBuilder.symbol) + ).apply { + isExpect = constructor.hasExpectModifier() || classOrObject.hasExpectModifier() + isActual = false + isOverride = false + this.isInner = isInner + } + this.symbol = symbol + dispatchReceiverType = + if (!isInner) null + else with(c) { + ClassId(packageFqName, relativeClassName.parent(), false).defaultType(outerTypeParameters) + } + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + this.typeParameters += + typeParameters.filterIsInstance() + .map { buildConstructedClassTypeParameterRef { this.symbol = it.symbol } } + valueParameters += local.memberDeserializer.valueParameters( + constructor.valueParameters, + symbol, + addDefaultValue = classBuilder.symbol.classId == StandardClassIds.Enum + ) + annotations += + c.annotationDeserializer.loadAnnotations(constructor) + containerSource = c.containerSource + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + + contextReceivers.addAll(createContextReceiversForClass(classOrObject)) + }.build().apply { + containingClassForStaticMemberAttr = c.dispatchReceiver!!.lookupTag + } + } + + private fun valueParameters( + valueParameters: List, + functionSymbol: FirFunctionSymbol<*>, + addDefaultValue: Boolean = false + ): List { + return valueParameters.map { ktParameter -> + val name = ktParameter.nameAsSafeName + buildValueParameter { + source = KtRealPsiSourceElement(ktParameter) + moduleData = c.moduleData + this.containingFunctionSymbol = functionSymbol + origin = FirDeclarationOrigin.Library + returnTypeRef = + ktParameter.typeReference?.toTypeRef(c) ?: error("KtParameter $ktParameter doesn't have type, $functionSymbol") + isVararg = ktParameter.isVarArg + if (isVararg) { + returnTypeRef = returnTypeRef.withReplacedReturnType(returnTypeRef.coneType.createOutArrayType()) + } + this.name = name + symbol = FirValueParameterSymbol(name) + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + + defaultValue = if (ktParameter.hasDefaultValue()) { + buildExpressionStub() + } else null + if (addDefaultValue) { + defaultValue = buildExpressionStub() + } + isCrossinline = ktParameter.hasModifier(KtTokens.CROSSINLINE_KEYWORD) + isNoinline = ktParameter.hasModifier(KtTokens.NOINLINE_KEYWORD) + annotations += c.annotationDeserializer.loadAnnotations( + ktParameter + ) + } + }.toList() + } + + private fun KtTypeReference.toTypeRef(context: StubBasedFirDeserializationContext): FirTypeRef = + context.typeDeserializer.typeRef(this) + + fun loadEnumEntry( + declaration: KtEnumEntry, + symbol: FirRegularClassSymbol, + classId: ClassId + ): FirEnumEntry { + val enumEntryName = declaration.name ?: error("Enum entry doesn't provide name $declaration") + + val enumType = ConeClassLikeTypeImpl(symbol.toLookupTag(), emptyArray(), false) + val enumEntry = buildEnumEntry { + source = KtRealPsiSourceElement(declaration) + this.moduleData = c.moduleData + this.origin = FirDeclarationOrigin.Library + returnTypeRef = buildResolvedTypeRef { type = enumType } + name = Name.identifier(enumEntryName) + this.symbol = FirEnumEntrySymbol(CallableId(classId, name)) + this.status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public + ).apply { + isStatic = true + } + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + }.apply { + containingClassForStaticMemberAttr = c.dispatchReceiver!!.lookupTag + } + return enumEntry + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt new file mode 100644 index 00000000000..97384b07e43 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt @@ -0,0 +1,283 @@ +/* + * Copyright 2010-2020 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.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.computeTypeAttributes +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner +import org.jetbrains.kotlin.fir.declarations.builder.FirTypeParameterBuilder +import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary +import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier +import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.* +import org.jetbrains.kotlin.types.Variance + +class StubBasedFirTypeDeserializer( + private val moduleData: FirModuleData, + private val annotationDeserializer: StubBasedAnnotationDeserializer, + private val parent: StubBasedFirTypeDeserializer?, + private val containingSymbol: FirBasedSymbol<*>?, + owner: KtTypeParameterListOwner +) { + private val typeParametersByName: Map + + val ownTypeParameters: List + get() = typeParametersByName.values.toList() + + init { + val typeParameters = owner.typeParameters + if (typeParameters.isNotEmpty()) { + typeParametersByName = mutableMapOf() + val builders = mutableListOf() + for (typeParameter in typeParameters) { + val name = typeParameter.nameAsSafeName + val symbol = FirTypeParameterSymbol().also { + typeParametersByName[name.asString()] = it + } + builders += FirTypeParameterBuilder().apply { + source = KtRealPsiSourceElement(typeParameter) + moduleData = this@StubBasedFirTypeDeserializer.moduleData + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + origin = FirDeclarationOrigin.Library + this.name = name + this.symbol = symbol + this.containingDeclarationSymbol = containingSymbol ?: error("Top-level type parameter ???") + variance = typeParameter.variance + isReified = typeParameter.hasModifier(KtTokens.REIFIED_KEYWORD) + annotations += annotationDeserializer.loadAnnotations(typeParameter) + } + } + + for ((index, typeParameter) in typeParameters.withIndex()) { + val builder = builders[index] + builder.apply { + typeParameter.extendsBound?.let { bounds.add(typeRef(it)) } + owner.typeConstraints + .filter { it.subjectTypeParameterName?.getReferencedNameAsName() == typeParameter.nameAsName } + .forEach { typeConstraint -> typeConstraint.boundTypeReference?.let { bounds += typeRef(it) } } + addDefaultBoundIfNecessary() + }.build() + } + } else { + typeParametersByName = emptyMap() + } + } + + private fun computeClassifier(classId: ClassId?): ConeClassLikeLookupTag? { + return classId?.toLookupTag() + } + + fun typeRef(typeReference: KtTypeReference): FirTypeRef { + return buildResolvedTypeRef { + source = KtRealPsiSourceElement(typeReference) + annotations += annotationDeserializer.loadAnnotations(typeReference) + type = type(typeReference, annotations.computeTypeAttributes(moduleData.session, shouldExpandTypeAliases = false)) + } + } + + private fun attributesFromAnnotations(typeReference: KtTypeReference): ConeAttributes = + annotationDeserializer.loadAnnotations(typeReference).computeTypeAttributes(moduleData.session, shouldExpandTypeAliases = false) + + fun type(typeReference: KtTypeReference): ConeKotlinType { + return type(typeReference, attributesFromAnnotations(typeReference)) + } + + fun type(type: KotlinTypeBean): ConeKotlinType? { + when (type) { + is KotlinTypeParameterTypeBean -> { + val lookupTag = + typeParametersByName[type.typeParameterName]?.toLookupTag() ?: parent?.typeParameterSymbol(type.typeParameterName) + ?: return null + return ConeTypeParameterTypeImpl(lookupTag, isNullable = type.nullable).let { + if (type.definitelyNotNull) + ConeDefinitelyNotNullType.create(it, moduleData.session.typeContext, avoidComprehensiveCheck = true) ?: it + else + it + } + } + is KotlinClassTypeBean -> { + val projections = type.arguments.map { typeArgumentBean -> + val kind = typeArgumentBean.projectionKind + if (kind == KtProjectionKind.STAR) { + return@map ConeStarProjection + } + val argBean = typeArgumentBean.type!! + val lowerBound = type(argBean) ?: error("Broken type argument ${typeArgumentBean.type}") + typeArgument(lowerBound, kind) + } + return ConeClassLikeTypeImpl( + type.classId.toLookupTag(), + projections.toTypedArray(), + isNullable = type.nullable, + ConeAttributes.Empty + ) + } + is KotlinFlexibleTypeBean -> { + val lowerBound = type(type.lowerBound) + val upperBound = type(type.upperBound) + return ConeFlexibleType( + lowerBound as? ConeSimpleKotlinType ?: error("Unexpected lower bound $lowerBound"), + upperBound as? ConeSimpleKotlinType ?: error("Unexpected upper bound $upperBound") + ) + } + } + } + + private fun type(typeReference: KtTypeReference, attributes: ConeAttributes): ConeKotlinType { + val upperBoundType = ((typeReference.typeElement as? KtUserType)?.stub as? KotlinUserTypeStubImpl)?.upperBound + if (upperBoundType != null) { + val lowerBound = simpleType(typeReference, attributes) + val upperBound = type(upperBoundType) + + val isDynamic = lowerBound == moduleData.session.builtinTypes.nothingType.coneType && + upperBound == moduleData.session.builtinTypes.nullableAnyType.coneType + + return if (isDynamic) { + ConeDynamicType.create(moduleData.session) + } else { + ConeFlexibleType(lowerBound!!, upperBound as ConeSimpleKotlinType) + } + } + + return simpleType(typeReference, attributes) ?: ConeErrorType(ConeSimpleDiagnostic("?!id:0", DiagnosticKind.DeserializationError)) + } + + private fun typeParameterSymbol(typeParameterName: String): ConeTypeParameterLookupTag? = + typeParametersByName[typeParameterName]?.toLookupTag() ?: parent?.typeParameterSymbol(typeParameterName) + + fun FirClassLikeSymbol<*>.typeParameters(): List = + (fir as? FirTypeParameterRefsOwner)?.typeParameters?.map { it.symbol }.orEmpty() + + private fun simpleType(typeReference: KtTypeReference, attributes: ConeAttributes): ConeSimpleKotlinType? { + val constructor = typeSymbol(typeReference) ?: return null + val isNullable = typeReference.typeElement is KtNullableType + if (constructor is ConeTypeParameterLookupTag) { + return ConeTypeParameterTypeImpl(constructor, isNullable = isNullable).let { + if (typeReference.typeElement?.unwrapNullability() is KtIntersectionType) { + ConeDefinitelyNotNullType.create(it, moduleData.session.typeContext, avoidComprehensiveCheck = true) ?: it + } else it + } + } + if (constructor !is ConeClassLikeLookupTag) return null + + val typeElement = typeReference.typeElement?.unwrapNullability() + val arguments = when (typeElement) { + is KtUserType -> typeElement.typeArguments.map { typeArgument(it) }.toTypedArray() + is KtFunctionType -> buildList { + typeElement.receiver?.let { add(type(it.typeReference).toTypeProjection(Variance.INVARIANT)) } + addAll(typeElement.parameters.map { type(it.typeReference!!).toTypeProjection(Variance.INVARIANT) }) + add(type(typeElement.returnTypeReference!!).toTypeProjection(Variance.INVARIANT)) + }.toTypedArray() + else -> error("not supported $typeElement") + } + + return ConeClassLikeTypeImpl( + constructor, + arguments, + isNullable = isNullable, + if (typeElement is KtFunctionType && typeElement.receiver != null) ConeAttributes.WithExtensionFunctionType else attributes + ) + } + + private fun KtElementImplStub<*>.getAllModifierLists(): Array = + getStubOrPsiChildren(KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST.arrayFactory) + + private fun typeSymbol(typeReference: KtTypeReference): ConeClassifierLookupTag? { + val typeElement = typeReference.typeElement?.unwrapNullability() + if (typeElement is KtFunctionType) { + val arity = (if (typeElement.receiver != null) 1 else 0) + typeElement.parameters.size + val isSuspend = typeReference.getAllModifierLists().any { it.hasSuspendModifier() } + val functionClassId = if (isSuspend) StandardNames.getSuspendFunctionClassId(arity) else StandardNames.getFunctionClassId(arity) + return computeClassifier(functionClassId) + } + if (typeElement is KtIntersectionType) { + val leftTypeRef = typeElement.getLeftTypeRef() ?: return null + //T&Any + return typeSymbol(leftTypeRef) + } + val type = typeElement as KtUserType + val referencedName = type.referencedName + return typeParameterSymbol(referencedName!!) ?: computeClassifier(type.classId()) + } + + + private fun typeArgument(projection: KtTypeProjection): ConeTypeProjection { + if (projection.projectionKind == KtProjectionKind.STAR) { + return ConeStarProjection + } + + val type = type(projection.typeReference!!) + return typeArgument(type, projection.projectionKind) + } + + private fun typeArgument( + type: ConeKotlinType, + projectionKind: KtProjectionKind + ): ConeTypeProjection { + val variance = when (projectionKind) { + KtProjectionKind.IN -> Variance.IN_VARIANCE + KtProjectionKind.OUT -> Variance.OUT_VARIANCE + KtProjectionKind.NONE -> Variance.INVARIANT + KtProjectionKind.STAR -> throw AssertionError("* should not be here") + } + return type.toTypeProjection(variance) + } +} + +/** + * Retrieves classId from [KtUserType] for compiled code only. + * + * It relies on [org.jetbrains.kotlin.psi.stubs.impl.KotlinNameReferenceExpressionStubImpl.isClassRef], + * which is set during cls analysis only. + */ +internal fun KtUserType.classId(): ClassId { + val packageFragments = mutableListOf() + val classFragments = mutableListOf() + + fun collectFragments(type: KtUserType) { + val userType = type.getStubOrPsiChild(KtStubElementTypes.USER_TYPE) + if (userType != null) { + collectFragments(userType) + } + val referenceExpression = type.referenceExpression as? KtNameReferenceExpression + if (referenceExpression != null) { + val referencedName = referenceExpression.getReferencedName() + val stub = referenceExpression.stub + if (stub is KotlinNameReferenceExpressionStubImpl && stub.isClassRef) { + classFragments.add(referencedName) + } else { + packageFragments.add(referencedName) + } + } + } + collectFragments(this) + return ClassId( + FqName.fromSegments(packageFragments), + FqName.fromSegments(classFragments), + false + ) +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt index e3199f9b6e6..0ceb7f2c0cd 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt @@ -16,10 +16,12 @@ import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirGlobalResolveComponents import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBuiltinsSessionFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.services.NoOpKtCompilerPluginsProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLFirSealedClassInheritorsProcessorFactoryForTests import org.jetbrains.kotlin.analysis.low.level.api.fir.services.PackagePartProviderTestImpl +import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubBasedDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider @@ -59,6 +61,7 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { registerService(KtCompilerPluginsProvider::class.java, NoOpKtCompilerPluginsProvider) registerService(ReadWriteAccessChecker::class.java, ReadWriteAccessCheckerFirImpl()) registerService(KotlinReferenceProviderContributor::class.java, KotlinFirReferenceContributor::class.java) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmStubBasedDeserializedSymbolProviderFactory::class.java) } } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt index 8790cf9eb15..f092f9b6f00 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.services.TestModuleStructure import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule @@ -33,6 +34,7 @@ object FirLowLevelCompilerBasedTestConfigurator : AnalysisApiTestConfigurator() override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, ) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt index 74c7dbca397..f82435a5556 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.SealedClassesInheritorsCaclulatorPreAnalysisHandler import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AnalysisApiFirTestServiceRegistrar @@ -35,6 +36,7 @@ abstract class AnalysisApiFirSourceLikeTestConfigurator(override val analyseInDe override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, ) diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt index 8f9820026c2..f55f03f8cca 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt @@ -270,7 +270,7 @@ private fun FirRegularClassBuilder.addSerializableIfNeeded(classId: ClassId) { } } -private fun FirRegularClassBuilder.addCloneForArrayIfNeeded(classId: ClassId, dispatchReceiver: ConeClassLikeType?) { +fun FirRegularClassBuilder.addCloneForArrayIfNeeded(classId: ClassId, dispatchReceiver: ConeClassLikeType?) { if (classId.packageFqName != StandardClassIds.BASE_KOTLIN_PACKAGE) return if (classId.shortClassName !in ARRAY_CLASSES) return superTypeRefs += buildResolvedTypeRef { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index ecb09add760..8f7a78362f4 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.name.Name // Use it in application sessions for loading classes from Java files listed on the command line. // For library and incremental compilation sessions use `KotlinDeserializedJvmSymbolsProvider` // in order to load Kotlin classes as well. +//Also used in IDE for loading java classes separately from stub based kotlin classes class JavaSymbolProvider( session: FirSession, private val javaFacade: FirJavaFacade, diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt index 71168c94264..378641dc354 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt @@ -1056,6 +1056,7 @@ fun FirRegularClassBuilder.createDataClassCopyFunction( status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL) symbol = FirNamedFunctionSymbol(CallableId(classId.packageFqName, classId.relativeClassName, StandardNames.DATA_CLASS_COPY)) dispatchReceiverType = dispatchReceiver + resolvePhase = this@createDataClassCopyFunction.resolvePhase for ((ktParameter, firProperty) in zippedParameters) { val propertyName = firProperty.name val parameterSource = toFirSource(ktParameter, KtFakeSourceElementKind.DataClassGeneratedMembers) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt index 022cc83799c..509dc73c318 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt @@ -160,7 +160,7 @@ fun FirRegularClassBuilder.generateEntriesGetter( getter = FirDefaultPropertyGetter( sourceElement?.fakeElement(KtFakeSourceElementKind.EnumGeneratedDeclaration), moduleData, origin, returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.EnumGeneratedDeclaration), - Visibilities.Public, symbol + Visibilities.Public, symbol, resolvePhase = this@generateEntriesGetter.resolvePhase ).apply { this.status = createStatus(this@generateEntriesGetter.status).apply { isStatic = true diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java index 3b82c005eed..985792e7496 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java @@ -41,12 +41,15 @@ public class KtNameReferenceExpressionElementType extends KtStubElementType parent, + @NotNull StringRef referencedName, + boolean myClassRef + ) { + super(parent, KtStubElementTypes.REFERENCE_EXPRESSION); + this.referencedName = referencedName; + this.myClassRef = myClassRef; + } + + public boolean isClassRef() { + return myClassRef; } @NotNull