[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
This commit is contained in:
Anna Kozlova
2023-02-15 14:22:59 +01:00
committed by teamcity
parent 96c15f6c71
commit d59d66e876
41 changed files with 1957 additions and 90 deletions
@@ -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<AnalysisApiTestServiceRegistrar> = listOf(
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFe10TestServiceRegistrar,
)
@@ -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<AnalysisApiTestServiceRegistrar> =
listOf(
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFirTestServiceRegistrar,
AnalysisApiLibraryBaseTestServiceRegistrar,
)
@@ -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<AnalysisApiTestServiceRegistrar> =
listOf(
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFirTestServiceRegistrar,
AnalysisApiLibraryBaseTestServiceRegistrar,
)
@@ -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<KtFile>() }
val moduleStructure = testServices.ktModuleProvider.getModuleStructure()
val allKtFiles = moduleStructure.mainModules.flatMap { it.files.filterIsInstance<KtFile>() }
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())
}
}
@@ -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)
}
}
}
@@ -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 {
@@ -42,6 +42,8 @@ public abstract class KotlinDeclarationProvider {
public abstract fun findInternalFilesForFacade(facadeFqName: FqName): Collection<KtFile>
public abstract fun findFilesForScript(scriptFqName: FqName): Collection<KtScript>
public abstract fun computePackageSetWithTopLevelCallableDeclarations(): Set<String>
}
public abstract class KotlinDeclarationProviderFactory {
@@ -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<String> {
val packageNames = index.topLevelPropertyMap.keys + index.topLevelFunctionMap.keys
return packageNames.mapTo(mutableSetOf()) { it.asString() }
}
override fun getTopLevelProperties(callableId: CallableId): Collection<KtProperty> =
index.topLevelPropertyMap[callableId.packageName]
?.filter { ktProperty ->
@@ -109,6 +124,7 @@ public class KotlinStaticDeclarationProviderFactory(
private val project: Project,
files: Collection<KtFile>,
private val jarFileSystem: CoreJarFileSystem = CoreJarFileSystem(),
additionalRoots: List<VirtualFile> = 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<KotlinClassOrObjectStub<*>>().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<KotlinFileStubImpl>()
VfsUtilCore.visitChildrenRecursively(additionalRoot, object : VirtualFileVisitor<Void>() {
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<String, List<KotlinFileStubImpl>>()
companion object {
fun processAdditionalRoot(root: VirtualFile, storage: (VirtualFile) -> List<KotlinFileStubImpl>): List<KotlinFileStubImpl>? {
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)
}
}
}
}
@@ -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
@@ -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")
@@ -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)
}
}
@@ -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)
}
}
@@ -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<kotlin/String!>
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
@@ -40,11 +40,9 @@ KtConstructorSymbol:
type: (kotlin/collections/MutableCollection<out E!>..kotlin/collections/Collection<E!>?)
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
@@ -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
@@ -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<out PsiElement>, 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)
}
@@ -65,6 +65,10 @@ private constructor(
return providers.flatMapTo(mutableListOf()) { it.findFilesForScript(scriptFqName) }
}
override fun computePackageSetWithTopLevelCallableDeclarations(): Set<String> {
return providers.flatMapTo(mutableSetOf()) { it.computePackageSetWithTopLevelCallableDeclarations() }
}
companion object {
fun create(providers: List<KotlinDeclarationProvider>): KotlinDeclarationProvider {
return when (providers.size) {
@@ -25,4 +25,5 @@ internal object EmptyKotlinDeclarationProvider : KotlinDeclarationProvider() {
override fun findFilesForFacade(facadeFqName: FqName) = emptyList<KtFile>()
override fun findInternalFilesForFacade(facadeFqName: FqName) = emptyList<KtFile>()
override fun findFilesForScript(scriptFqName: FqName) = emptyList<KtScript>()
override fun computePackageSetWithTopLevelCallableDeclarations(): Set<String> = emptySet()
}
@@ -129,6 +129,9 @@ internal class FileBasedKotlinDeclarationProvider(val kotlinFile: KtFile) : Kotl
}
override fun findInternalFilesForFacade(facadeFqName: FqName): Collection<KtFile> = emptyList()
override fun computePackageSetWithTopLevelCallableDeclarations(): Set<String> {
return setOf(kotlinFile.packageFqName.asString())
}
override fun findFilesForScript(scriptFqName: FqName): Collection<KtScript> =
listOfNotNull(kotlinFile.script?.takeIf { it.fqName == scriptFqName })
@@ -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<FirSymbolProvider> {
return listOf(
JvmClassFileBasedSymbolProvider(
session,
moduleDataProvider,
kotlinScopeProvider,
packagePartProvider,
VirtualFileFinderFactory.getInstance(project).create(scope),
firJavaFacade
)
)
}
}
@@ -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<FirSymbolProvider> {
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))
}
}
}
@@ -94,7 +94,7 @@ internal fun LLFirSourcesSession.registerCompilerPluginServices(
}
internal fun createJavaSymbolProvider(
firSession: LLFirSession,
firSession: FirSession,
moduleData: LLFirModuleData,
project: Project,
contentScope: GlobalSearchScope
@@ -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
}
}
@@ -238,6 +238,10 @@ class LLFirResolveExtensionToolDeclarationProvider internal constructor(
.mapNotNullTo(mutableListOf()) { it.kotlinFile.script }
}
override fun computePackageSetWithTopLevelCallableDeclarations(): Set<String> {
return emptySet() //todo
}
private inline fun getDeclarationProvidersByPackage(
packageFqName: FqName,
crossinline filter: (KtResolveExtensionFile) -> Boolean
@@ -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<JvmMetadataVersion>? = 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
}
@@ -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<FirSymbolProvider> {
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))
}
}
}
@@ -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<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
declarationProvider.computePackageSetWithTopLevelCallableDeclarations()
}
private val namesByPackageCache = LLFirKotlinSymbolProviderNameCache(session, declarationProvider)
private val typeAliasCache: FirCache<ClassId, FirTypeAliasSymbol?, StubBasedFirDeserializationContext?> =
session.firCachesFactory.createCacheWithPostCompute(
createValue = { classId, _ -> findAndDeserializeTypeAlias(classId) },
postCompute = { _, symbol, postProcessor ->
if (postProcessor != null && symbol != null) {
postProcessor.invoke(symbol)
}
}
)
private val classCache: FirCache<ClassId, FirRegularClassSymbol?, StubBasedFirDeserializationContext?> =
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<String> {
return packageSetWithTopLevelCallableDeclarations
}
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
namesByPackageCache.getTopLevelCallableNamesInPackage(packageFqName)
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? {
return namesByPackageCache.getTopLevelClassifierNamesInPackage(packageFqName)
}
private fun findAndDeserializeTypeAlias(classId: ClassId): Pair<FirTypeAliasSymbol?, DeserializedTypeAliasPostProcessor?> {
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<FirNamedFunctionSymbol> {
val topLevelFunctions = declarationProvider.getTopLevelFunctions(callableId)
val origins = if (topLevelFunctions.size > 1) mutableSetOf<KtNamedFunction>() 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<FirPropertySymbol> {
val topLevelProperties = declarationProvider.getTopLevelProperties(callableId)
val origins = if (topLevelProperties.size > 1) mutableSetOf<KtProperty>() 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<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
val callableId = CallableId(packageFqName, name)
destination += functionCache.getCallables(callableId)
destination += propertyCache.getCallables(callableId)
}
private fun <C : FirCallableSymbol<*>> FirCache<CallableId, List<C>, Nothing?>.getCallables(id: CallableId): List<C> {
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<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
destination += functionCache.getCallables(CallableId(packageFqName, name))
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, 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)
}
}
@@ -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<FirAnnotation> {
val annotations = ktAnnotated.annotationEntries
if (annotations.isEmpty()) return emptyList()
return annotations.map { deserializeAnnotation(it, useSiteTarget) }
}
private val constantCache = mutableMapOf<CallableId, FirExpression>()
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<Name, ConstantValue<*>>?,
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 <T> const(kind: ConstantValueKind<T>, value: T, typeRef: FirResolvedTypeRef, sourceElement: PsiElement): FirConstExpression<T> {
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
}
}
}
}
@@ -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<FirProperty>()
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<FirDeclaration> {
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)
}
}
}
@@ -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<ConeContractDescriptionElement, Nothing?, KotlinTypeBean, Nothing?>() {
override fun visitCallsEffectDeclaration(
callsEffect: KtCallsEffectDeclaration<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeCallsEffectDeclaration(
callsEffect.valueParameterReference.accept(
this,
data
) as KtValueParameterReference<ConeKotlinType, ConeDiagnostic>, callsEffect.kind
)
}
override fun visitReturnsEffectDeclaration(
returnsEffect: KtReturnsEffectDeclaration<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeReturnsEffectDeclaration(
returnsEffect.value.accept(
this,
data
) as KtConstantReference<ConeKotlinType, ConeDiagnostic>
)
}
override fun visitConditionalEffectDeclaration(
conditionalEffect: KtConditionalEffectDeclaration<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeConditionalEffectDeclaration(
conditionalEffect.effect.accept(this, data) as KtEffectDeclaration<ConeKotlinType, ConeDiagnostic>,
conditionalEffect.condition.accept(this, data) as KtBooleanExpression<ConeKotlinType, ConeDiagnostic>
)
}
override fun visitLogicalBinaryOperationContractExpression(
binaryLogicExpression: KtBinaryLogicExpression<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeBinaryLogicExpression(
binaryLogicExpression.left.accept(this, data) as KtBooleanExpression<ConeKotlinType, ConeDiagnostic>,
binaryLogicExpression.right.accept(this, data) as KtBooleanExpression<ConeKotlinType, ConeDiagnostic>,
binaryLogicExpression.kind
)
}
override fun visitLogicalNot(
logicalNot: KtLogicalNot<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeLogicalNot(logicalNot.arg.accept(this, data) as KtBooleanExpression<ConeKotlinType, ConeDiagnostic>)
}
override fun visitIsInstancePredicate(
isInstancePredicate: KtIsInstancePredicate<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeIsInstancePredicate(
isInstancePredicate.arg.accept(this, data) as KtValueParameterReference<ConeKotlinType, ConeDiagnostic>,
typeDeserializer.type(isInstancePredicate.type)!!,
isInstancePredicate.isNegated
)
}
override fun visitIsNullPredicate(
isNullPredicate: KtIsNullPredicate<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
return ConeIsNullPredicate(
isNullPredicate.arg.accept(this, data) as KtValueParameterReference<ConeKotlinType, ConeDiagnostic>,
isNullPredicate.isNegated
)
}
override fun visitValueParameterReference(
valueParameterReference: KtValueParameterReference<KotlinTypeBean, Nothing?>,
data: Nothing?
): ConeContractDescriptionElement {
val parameterIndex = valueParameterReference.parameterIndex
return ConeValueParameterReference(parameterIndex, getParameterName(parameterIndex))
}
override fun visitBooleanValueParameterReference(
booleanValueParameterReference: KtBooleanValueParameterReference<KotlinTypeBean, Nothing?>,
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<KotlinTypeBean, Nothing?>,
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")
}
}
}
}
}
@@ -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<FirTypeParameterSymbol>
) {
val session: FirSession = moduleData.session
val allTypeParameters: List<FirTypeParameterSymbol> =
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<FirContextReceiver> =
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<FirTypeParameter>()
.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<KtParameter>,
functionSymbol: FirFunctionSymbol<*>,
addDefaultValue: Boolean = false
): List<FirValueParameter> {
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
}
}
@@ -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<String, FirTypeParameterSymbol>
val ownTypeParameters: List<FirTypeParameterSymbol>
get() = typeParametersByName.values.toList()
init {
val typeParameters = owner.typeParameters
if (typeParameters.isNotEmpty()) {
typeParametersByName = mutableMapOf()
val builders = mutableListOf<FirTypeParameterBuilder>()
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<FirTypeParameterSymbol> =
(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<out KtDeclarationModifierList> =
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<String>()
val classFragments = mutableListOf<String>()
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
)
}
@@ -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)
}
}
@@ -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<AnalysisApiTestServiceRegistrar> = listOf(
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFirTestServiceRegistrar,
)
@@ -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<AnalysisApiTestServiceRegistrar> = listOf(
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFirTestServiceRegistrar,
)
@@ -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 {
@@ -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,
@@ -1056,6 +1056,7 @@ fun <T> 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)
@@ -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
@@ -41,12 +41,15 @@ public class KtNameReferenceExpressionElementType extends KtStubElementType<Kotl
@Override
public void serialize(@NotNull KotlinNameReferenceExpressionStub stub, @NotNull StubOutputStream dataStream) throws IOException {
dataStream.writeName(stub.getReferencedName());
dataStream.writeBoolean(
stub instanceof KotlinNameReferenceExpressionStubImpl && ((KotlinNameReferenceExpressionStubImpl) stub).isClassRef());
}
@NotNull
@Override
public KotlinNameReferenceExpressionStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
StringRef referencedName = dataStream.readName();
return new KotlinNameReferenceExpressionStubImpl(parentStub, referencedName);
boolean isClassRef = dataStream.readBoolean();
return new KotlinNameReferenceExpressionStubImpl(parentStub, referencedName, isClassRef);
}
}
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.psi.stubs.impl;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.io.StringRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.psi.KtNameReferenceExpression;
import org.jetbrains.kotlin.psi.stubs.KotlinNameReferenceExpressionStub;
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
@@ -27,10 +29,26 @@ public class KotlinNameReferenceExpressionStubImpl extends KotlinStubBaseImpl<Kt
KotlinNameReferenceExpressionStub {
@NotNull
private final StringRef referencedName;
private final boolean myClassRef;
public KotlinNameReferenceExpressionStubImpl(StubElement parent, @NotNull StringRef referencedName) {
super(parent, KtStubElementTypes.REFERENCE_EXPRESSION);
this.referencedName = referencedName;
myClassRef = false;
}
public KotlinNameReferenceExpressionStubImpl(
@Nullable StubElement<?> parent,
@NotNull StringRef referencedName,
boolean myClassRef
) {
super(parent, KtStubElementTypes.REFERENCE_EXPRESSION);
this.referencedName = referencedName;
this.myClassRef = myClassRef;
}
public boolean isClassRef() {
return myClassRef;
}
@NotNull