Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2019 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.fir.java.deserialization
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl
import org.jetbrains.kotlin.fir.deserialization.FirConstDeserializer
import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext
import org.jetbrains.kotlin.fir.deserialization.deserializeClassToSymbol
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
import org.jetbrains.kotlin.fir.java.createConstant
import org.jetbrains.kotlin.fir.java.topLevelName
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.*
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmNameResolver
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.isOneSegmentFQN
import org.jetbrains.kotlin.resolve.constants.ClassLiteralValue
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.getName
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
@ThreadSafeMutableState
class KotlinDeserializedJvmSymbolsProvider(
session: FirSession,
val project: Project,
private val packagePartProvider: PackagePartProvider,
private val javaSymbolProvider: JavaSymbolProvider,
private val kotlinClassFinder: KotlinClassFinder,
private val javaClassFinder: JavaClassFinder,
private val kotlinScopeProvider: KotlinScopeProvider,
) : FirSymbolProvider(session) {
private val classCache = SymbolProviderCache()
private val typeAliasCache = SymbolProviderCache()
private val packagePartsCache = SymbolProviderCache>()
// TODO: implement thread safety for this property
private val handledByJava = HashSet()
private class PackagePartsCacheData(
val proto: ProtoBuf.Package,
val context: FirDeserializationContext,
val source: JvmPackagePartSource,
) {
val topLevelFunctionNameIndex by lazy {
proto.functionList.withIndex()
.groupBy({ context.nameResolver.getName(it.value.name) }) { (index) -> index }
}
val topLevelPropertyNameIndex by lazy {
proto.propertyList.withIndex()
.groupBy({ context.nameResolver.getName(it.value.name) }) { (index) -> index }
}
val typeAliasNameIndex by lazy {
proto.typeAliasList.withIndex()
.groupBy({ context.nameResolver.getName(it.value.name) }) { (index) -> index }
}
}
private val knownClassNamesInPackage = mutableMapOf?>()
// This function returns true if we are sure that no top-level class with this id is available
// If it returns false, it means we can say nothing about this id
private fun hasNoTopLevelClassOf(classId: ClassId): Boolean {
val knownNames = knownClassNamesInPackage.getOrPut(classId.packageFqName) {
javaClassFinder.knownClassNamesInPackage(classId.packageFqName)
} ?: return false
return classId.relativeClassName.topLevelName() !in knownNames
}
private fun computePackagePartsInfos(packageFqName: FqName): List {
return packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName ->
val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars)
if (hasNoTopLevelClassOf(classId)) return@mapNotNull null
val (kotlinJvmBinaryClass, byteContent) =
kotlinClassFinder.findKotlinClassOrContent(classId) as? KotlinClassFinder.Result.KotlinClass ?: return@mapNotNull null
val facadeName = kotlinJvmBinaryClass.classHeader.multifileClassName?.takeIf { it.isNotEmpty() }
val facadeFqName = facadeName?.let { JvmClassName.byInternalName(it).fqNameForTopLevelClassMaybeWithDollars }
val facadeBinaryClass = facadeFqName?.let { kotlinClassFinder.findKotlinClass(ClassId.topLevel(it)) }
val header = kotlinJvmBinaryClass.classHeader
val data = header.data ?: header.incompatibleData ?: return@mapNotNull null
val strings = header.strings ?: return@mapNotNull null
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings)
val source = JvmPackagePartSource(
kotlinJvmBinaryClass, packageProto, nameResolver,
kotlinJvmBinaryClass.incompatibility, kotlinJvmBinaryClass.isPreReleaseInvisible,
)
PackagePartsCacheData(
packageProto,
FirDeserializationContext.createForPackage(
packageFqName, packageProto, nameResolver, session,
JvmBinaryAnnotationDeserializer(session, kotlinJvmBinaryClass, byteContent),
FirConstDeserializer(session, kotlinJvmBinaryClass, facadeBinaryClass),
source
),
source,
)
}
}
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData?
get() {
// TODO: skipMetadataVersionCheck
if (classHeader.metadataVersion.isCompatible()) return null
return IncompatibleVersionErrorData(classHeader.metadataVersion, JvmMetadataVersion.INSTANCE, location, classId)
}
private val KotlinJvmBinaryClass.isPreReleaseInvisible: Boolean
get() = classHeader.isPreRelease
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
return findAndDeserializeClass(classId) ?: findAndDeserializeTypeAlias(classId)
}
private fun findAndDeserializeTypeAlias(
classId: ClassId,
): FirTypeAliasSymbol? {
if (!classId.relativeClassName.isOneSegmentFQN()) return null
return typeAliasCache.lookupCacheOrCalculate(classId) {
getPackageParts(classId.packageFqName).firstNotNullResult { part ->
val ids = part.typeAliasNameIndex[classId.shortClassName]
if (ids == null || ids.isEmpty()) return@firstNotNullResult null
val aliasProto = ids.map { part.proto.getTypeAlias(it) }.single()
part.context.memberDeserializer.loadTypeAlias(aliasProto).symbol
}
}
}
private fun KotlinJvmBinaryClass.readClassDataFrom(): Pair? {
val data = classHeader.data ?: return null
val strings = classHeader.strings ?: return null
return JvmProtoBufUtil.readClassDataFrom(data, strings)
}
private fun ConeClassLikeLookupTag.toDefaultResolvedTypeRef(): FirResolvedTypeRef =
buildResolvedTypeRef {
type = constructClassType(emptyArray(), isNullable = false)
}
private fun loadAnnotation(
annotationClassId: ClassId, result: MutableList,
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val lookupTag = ConeClassLikeLookupTagImpl(annotationClassId)
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor {
private val argumentMap = mutableMapOf()
override fun visit(name: Name?, value: Any?) {
if (name != null) {
argumentMap[name] = createConstant(value)
}
}
private fun ClassLiteralValue.toFirClassReferenceExpression(): FirClassReferenceExpression {
val literalLookupTag = ConeClassLikeLookupTagImpl(classId)
return buildClassReferenceExpression {
classTypeRef = literalLookupTag.toDefaultResolvedTypeRef()
}
}
private fun ClassId.toEnumEntryReferenceExpression(name: Name): FirExpression {
return buildFunctionCall {
val entryCallableSymbol =
this@KotlinDeserializedJvmSymbolsProvider.session.firSymbolProvider.getClassDeclaredCallableSymbols(
this@toEnumEntryReferenceExpression, name,
).firstOrNull()
calleeReference = when {
entryCallableSymbol != null -> {
buildResolvedNamedReference {
this.name = name
resolvedSymbol = entryCallableSymbol
}
}
else -> {
buildErrorNamedReference {
diagnostic = ConeSimpleDiagnostic(
"Strange deserialized enum value: ${this@toEnumEntryReferenceExpression}.$name",
DiagnosticKind.Java,
)
}
}
}
}
}
override fun visitClassLiteral(name: Name, value: ClassLiteralValue) {
argumentMap[name] = buildGetClassCall {
argumentList = buildUnaryArgumentList(value.toFirClassReferenceExpression())
}
}
override fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name) {
argumentMap[name] = enumClassId.toEnumEntryReferenceExpression(enumEntryName)
}
override fun visitArray(name: Name): AnnotationArrayArgumentVisitor? {
return object : AnnotationArrayArgumentVisitor {
private val elements = mutableListOf()
override fun visit(value: Any?) {
elements.add(createConstant(value))
}
override fun visitEnum(enumClassId: ClassId, enumEntryName: Name) {
elements.add(enumClassId.toEnumEntryReferenceExpression(enumEntryName))
}
override fun visitClassLiteral(value: ClassLiteralValue) {
elements.add(
buildGetClassCall {
argumentList = buildUnaryArgumentList(value.toFirClassReferenceExpression())
}
)
}
override fun visitEnd() {
argumentMap[name] = buildArrayOfCall {
argumentList = buildArgumentList {
arguments += elements
}
}
}
}
}
override fun visitAnnotation(name: Name, classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val list = mutableListOf()
val visitor = loadAnnotation(classId, list)!!
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
visitor.visitEnd()
argumentMap[name] = list.single()
}
}
}
override fun visitEnd() {
result += buildAnnotationCall {
annotationTypeRef = lookupTag.toDefaultResolvedTypeRef()
argumentList = buildArgumentList {
for ((name, expression) in argumentMap) {
arguments += buildNamedArgumentExpression {
this.expression = expression
this.name = name
isSpread = false
}
}
}
calleeReference = FirReferencePlaceholderForResolvedAnnotations
}
}
private fun createConstant(value: Any?): FirExpression {
return value.createConstant(session)
}
}
}
internal fun loadAnnotationIfNotSpecial(
annotationClassId: ClassId, result: MutableList,
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
if (annotationClassId in AbstractBinaryClassAnnotationAndConstantLoader.SPECIAL_ANNOTATIONS) return null
return loadAnnotation(annotationClassId, result)
}
private fun findAndDeserializeClass(
classId: ClassId,
parentContext: FirDeserializationContext? = null
): FirRegularClassSymbol? {
if (hasNoTopLevelClassOf(classId)) return null
if (classId in classCache) return classCache[classId]
if (classId in handledByJava) return null
val result = try {
kotlinClassFinder.findKotlinClassOrContent(classId)
} catch (e: ProcessCanceledException) {
return null
}
val kotlinClassWithContent = when (result) {
is KotlinClassFinder.Result.KotlinClass -> result
is KotlinClassFinder.Result.ClassFileContent -> {
handledByJava.add(classId)
return try {
javaSymbolProvider.getFirJavaClass(classId, result)
} catch (e: ProcessCanceledException) {
null
}
}
null -> null
}
if (kotlinClassWithContent == null) {
val outerClassId = classId.outerClassId ?: return null
findAndDeserializeClass(outerClassId) ?: return null
} else {
val (kotlinJvmBinaryClass, byteContent) = kotlinClassWithContent
if (kotlinJvmBinaryClass.classHeader.kind != KotlinClassHeader.Kind.CLASS) return null
val (nameResolver, classProto) = kotlinJvmBinaryClass.readClassDataFrom() ?: return null
val symbol = FirRegularClassSymbol(classId)
deserializeClassToSymbol(
classId, classProto, symbol, nameResolver, session,
JvmBinaryAnnotationDeserializer(session, kotlinJvmBinaryClass, byteContent),
kotlinScopeProvider,
parentContext, KotlinJvmBinarySourceElement(kotlinJvmBinaryClass),
this::findAndDeserializeClass
)
classCache[classId] = symbol
val annotations = mutableListOf()
kotlinJvmBinaryClass.loadClassAnnotations(
object : KotlinJvmBinaryClass.AnnotationVisitor {
override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
return loadAnnotationIfNotSpecial(classId, annotations)
}
override fun visitEnd() {
}
},
byteContent,
)
(symbol.fir.annotations as MutableList) += annotations
}
return classCache[classId]
}
private fun loadFunctionsByName(part: PackagePartsCacheData, name: Name): List> {
val functionIds = part.topLevelFunctionNameIndex[name] ?: return emptyList()
return functionIds.map { part.proto.getFunction(it) }
.map {
val firNamedFunction = part.context.memberDeserializer.loadFunction(it) as FirSimpleFunctionImpl
firNamedFunction.symbol
}
}
private fun loadPropertiesByName(part: PackagePartsCacheData, name: Name): List> {
val propertyIds = part.topLevelPropertyNameIndex[name] ?: return emptyList()
return propertyIds.map { part.proto.getProperty(it) }
.map {
val firProperty = part.context.memberDeserializer.loadProperty(it)
firProperty.symbol
}
}
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) {
getPackageParts(packageFqName).flatMapTo(destination) { part ->
loadFunctionsByName(part, name) + loadPropertiesByName(part, name)
}
}
private fun getPackageParts(packageFqName: FqName): Collection {
return packagePartsCache.lookupCacheOrCalculate(packageFqName) {
try {
computePackagePartsInfos(packageFqName)
} catch (e: ProcessCanceledException) {
emptyList()
}
}!!
}
private fun findRegularClass(classId: ClassId): FirRegularClass? =
getClassLikeSymbolByFqName(classId)?.fir as? FirRegularClass
override fun getPackage(fqName: FqName): FqName? = null
}