
org.jetbrains.kotlin.fir.java.JavaSymbolProvider.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2018 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
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.*
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.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.resolve.constructType
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.SymbolProviderCache
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.load.java.structure.impl.JavaElementImpl
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
import org.jetbrains.kotlin.types.Variance.INVARIANT
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
@ThreadSafeMutableState
class JavaSymbolProvider(
session: FirSession,
val project: Project,
private val searchScope: GlobalSearchScope,
) : FirSymbolProvider(session) {
companion object {
internal val VALUE_METHOD_NAME = Name.identifier("value")
}
private val classCache = SymbolProviderCache()
private val packageCache = SymbolProviderCache()
private val scopeProvider = JavaScopeProvider(::wrapScopeWithJvmMapped, this)
private val facade: KotlinJavaPsiFacade get() = KotlinJavaPsiFacade.getInstance(project)
private val parentClassTypeParameterStackCache: SymbolProviderCache =
SymbolProviderCache()
private fun findClass(
classId: ClassId,
content: KotlinClassFinder.Result.ClassFileContent?,
): JavaClass? = facade.findClass(JavaClassFinder.Request(classId, previouslyFoundClassFileContent = content?.content), searchScope)
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) {
}
private fun JavaTypeParameter.toFirTypeParameterSymbol(
javaTypeParameterStack: JavaTypeParameterStack
): Pair {
val stored = javaTypeParameterStack.safeGet(this)
if (stored != null) return stored to true
val firSymbol = FirTypeParameterSymbol()
javaTypeParameterStack.addParameter(this, firSymbol)
return firSymbol to false
}
private fun JavaTypeParameter.toFirTypeParameter(
firSymbol: FirTypeParameterSymbol,
javaTypeParameterStack: JavaTypeParameterStack
): FirTypeParameter {
return FirTypeParameterBuilder().apply {
this.session = [email protected]
origin = FirDeclarationOrigin.Java
this.name = [email protected]
symbol = firSymbol
variance = INVARIANT
isReified = false
addBounds(this@toFirTypeParameter, javaTypeParameterStack)
}.build()
}
private fun FirTypeParameterBuilder.addBounds(
javaTypeParameter: JavaTypeParameter,
stack: JavaTypeParameterStack
) {
for (upperBound in javaTypeParameter.upperBounds) {
bounds += upperBound.toFirResolvedTypeRef(
[email protected],
stack,
isForSupertypes = false,
forTypeParameterBounds = true
)
}
addDefaultBoundIfNecessary(isFlexible = true)
}
private fun List.convertTypeParameters(stack: JavaTypeParameterStack): List {
return map { it.toFirTypeParameterSymbol(stack) }.mapIndexed { index, (symbol, initialized) ->
// This nasty logic is required, because type parameter bound can refer other type parameter from the list
// So we have to create symbols first, and type parameters themselves after them
if (initialized) symbol.fir
else this[index].toFirTypeParameter(symbol, stack)
}
}
override fun getClassLikeSymbolByFqName(classId: ClassId): FirRegularClassSymbol? {
return try {
getFirJavaClass(classId)
} catch (e: ProcessCanceledException) {
null
}
}
fun getFirJavaClass(classId: ClassId, content: KotlinClassFinder.Result.ClassFileContent? = null): FirRegularClassSymbol? {
if (!hasTopLevelClassOf(classId)) return null
return classCache.lookupCacheOrCalculateWithPostCompute(
classId,
{
val foundClass = findClass(classId, content)
if (foundClass == null || foundClass.annotations.any {
it.classId?.asSingleFqName() == JvmAnnotationNames.METADATA_FQ_NAME
}
) {
null to null
} else {
FirRegularClassSymbol(classId) to foundClass
}
},
) { firSymbol, foundClass ->
convertJavaClassToFir(firSymbol, foundClass)
}
}
private class ValueParametersForAnnotationConstructor {
val valueParameters: MutableList = mutableListOf()
var valueParameterForValue: FirJavaValueParameter? = null
}
private fun convertJavaClassToFir(classSymbol: FirRegularClassSymbol, javaClass: JavaClass?): FirJavaClass? {
if (javaClass == null) return null
val classId = classSymbol.classId
val javaTypeParameterStack = JavaTypeParameterStack()
val outerClassId = classId.outerClassId
val parentClassSymbol = if (outerClassId != null) {
getClassLikeSymbolByFqName(outerClassId)
} else null
if (parentClassSymbol != null) {
val parentStack = parentClassTypeParameterStackCache[parentClassSymbol]
?: (parentClassSymbol.fir as? FirJavaClass)?.javaTypeParameterStack
if (parentStack != null) {
javaTypeParameterStack.addStack(parentStack)
}
}
val firJavaClass = buildJavaClass {
source = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = [email protected]
symbol = classSymbol
name = javaClass.name
visibility = javaClass.visibility
modality = javaClass.modality
classKind = javaClass.classKind
this.isTopLevel = outerClassId == null
isStatic = javaClass.isStatic
this.javaTypeParameterStack = javaTypeParameterStack
parentClassTypeParameterStackCache[classSymbol] = javaTypeParameterStack
existingNestedClassifierNames += javaClass.innerClassNames
scopeProvider = [email protected]
val classTypeParameters = javaClass.typeParameters.convertTypeParameters(javaTypeParameterStack)
typeParameters += classTypeParameters
if (!isStatic && parentClassSymbol != null) {
typeParameters += parentClassSymbol.fir.typeParameters.map {
buildOuterClassTypeParameterRef { symbol = it.symbol }
}
}
val dispatchReceiver = classId.defaultType(typeParameters.map { it.symbol } )
status = FirResolvedDeclarationStatusImpl(
javaClass.visibility,
javaClass.modality
).apply {
this.isInner = !isTopLevel && [email protected]
isCompanion = false
isData = false
isInline = false
isFun = classKind == ClassKind.INTERFACE
}
// TODO: may be we can process fields & methods later.
// However, they should be built up to override resolve stage
for (javaField in javaClass.fields) {
declarations += convertJavaFieldToFir(javaField, classId, javaTypeParameterStack, dispatchReceiver)
}
val valueParametersForAnnotationConstructor = ValueParametersForAnnotationConstructor()
val classIsAnnotation = classKind == ClassKind.ANNOTATION_CLASS
for (javaMethod in javaClass.methods) {
if (javaMethod.isObjectMethodInInterface()) continue
declarations += convertJavaMethodToFir(
javaMethod,
classId,
javaTypeParameterStack,
classIsAnnotation,
valueParametersForAnnotationConstructor,
dispatchReceiver
)
}
val javaClassDeclaredConstructors = javaClass.constructors
val constructorId = CallableId(classId.packageFqName, classId.relativeClassName, classId.shortClassName)
if (javaClassDeclaredConstructors.isEmpty()
&& javaClass.classKind == ClassKind.CLASS
&& javaClass.hasDefaultConstructor()
) {
declarations += convertJavaConstructorToFir(
javaConstructor = null,
constructorId,
javaClass,
ownerClassBuilder = this,
classTypeParameters,
javaTypeParameterStack
)
}
for (javaConstructor in javaClassDeclaredConstructors) {
declarations += convertJavaConstructorToFir(
javaConstructor,
constructorId,
javaClass,
ownerClassBuilder = this,
classTypeParameters,
javaTypeParameterStack,
)
}
if (classKind == ClassKind.ENUM_CLASS) {
generateValuesFunction(
session,
classId.packageFqName,
classId.relativeClassName
)
generateValueOfFunction(session, classId.packageFqName, classId.relativeClassName)
}
if (classIsAnnotation) {
declarations +=
buildConstructorForAnnotationClass(constructorId, this, valueParametersForAnnotationConstructor)
}
parentClassTypeParameterStackCache.remove(classSymbol)
}
firJavaClass.replaceSuperTypeRefs(
javaClass.supertypes.map { supertype ->
supertype.toFirResolvedTypeRef(
[email protected], javaTypeParameterStack, isForSupertypes = true, forTypeParameterBounds = false
)
}
)
firJavaClass.addAnnotationsFrom([email protected], javaClass, javaTypeParameterStack)
return firJavaClass
}
private fun convertJavaFieldToFir(
javaField: JavaField,
classId: ClassId,
javaTypeParameterStack: JavaTypeParameterStack,
dispatchReceiver: ConeClassLikeType
): FirDeclaration {
val fieldName = javaField.name
val fieldId = CallableId(classId.packageFqName, classId.relativeClassName, fieldName)
val returnType = javaField.type
return when {
javaField.isEnumEntry -> buildEnumEntry {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = [email protected]
symbol = FirVariableSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
returnTypeRef = returnType.toFirJavaTypeRef([email protected], javaTypeParameterStack)
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
origin = FirDeclarationOrigin.Java
addAnnotationsFrom([email protected], javaField, javaTypeParameterStack)
}.apply {
containingClassAttr = ConeClassLikeLookupTagImpl(classId)
}
else -> buildJavaField {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = [email protected]
symbol = FirFieldSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
visibility = javaField.visibility
modality = javaField.modality
returnTypeRef = returnType.toFirJavaTypeRef([email protected], javaTypeParameterStack)
isVar = !javaField.isFinal
isStatic = javaField.isStatic
addAnnotationsFrom([email protected], javaField, javaTypeParameterStack)
initializer = convertJavaInitializerToFir(javaField.initializerValue)
if (!javaField.isStatic) {
dispatchReceiverType = dispatchReceiver
}
}.apply {
if (javaField.isStatic) {
containingClassAttr = ConeClassLikeLookupTagImpl(classId)
}
}
}
}
private fun convertJavaInitializerToFir(value: Any?): FirExpression? {
// NB: null should be converted to null
return value?.createConstantIfAny(session)
}
private fun convertJavaMethodToFir(
javaMethod: JavaMethod,
classId: ClassId,
javaTypeParameterStack: JavaTypeParameterStack,
classIsAnnotation: Boolean,
valueParametersForAnnotationConstructor: ValueParametersForAnnotationConstructor,
dispatchReceiver: ConeClassLikeType
): FirJavaMethod {
val methodName = javaMethod.name
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
val methodSymbol = FirNamedFunctionSymbol(methodId)
val returnType = javaMethod.returnType
val firJavaMethod = buildJavaMethod {
session = [email protected]
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
symbol = methodSymbol
name = methodName
visibility = javaMethod.visibility
modality = javaMethod.modality
returnTypeRef = returnType.toFirJavaTypeRef([email protected], javaTypeParameterStack)
isStatic = javaMethod.isStatic
typeParameters += javaMethod.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom([email protected], javaMethod, javaTypeParameterStack)
for ((index, valueParameter) in javaMethod.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
[email protected], index, javaTypeParameterStack,
)
}
status = FirResolvedDeclarationStatusImpl(
javaMethod.visibility,
javaMethod.modality
).apply {
isStatic = javaMethod.isStatic
isExpect = false
isActual = false
isOverride = false
// Approximation: all Java methods with name that allows to use it in operator form are considered operators
// We need here more detailed checks (see modifierChecks.kt)
isOperator = name in ALL_JAVA_OPERATION_NAMES || OperatorNameConventions.COMPONENT_REGEX.matches(name.asString())
isInfix = false
isInline = false
isTailRec = false
isExternal = false
isSuspend = false
}
if (!javaMethod.isStatic) {
dispatchReceiverType = dispatchReceiver
}
}.apply {
if (javaMethod.isStatic) {
containingClassAttr = ConeClassLikeLookupTagImpl(classId)
}
}
if (classIsAnnotation) {
val parameterForAnnotationConstructor = buildJavaValueParameter {
session = [email protected]
returnTypeRef = firJavaMethod.returnTypeRef
name = methodName
if (javaMethod.hasAnnotationParameterDefaultValue) {
defaultValue = buildExpressionStub()
}
isVararg = returnType is JavaArrayType && methodName == VALUE_METHOD_NAME
}
if (methodName == VALUE_METHOD_NAME) {
valueParametersForAnnotationConstructor.valueParameterForValue = parameterForAnnotationConstructor
} else {
valueParametersForAnnotationConstructor.valueParameters += parameterForAnnotationConstructor
}
}
return firJavaMethod
}
private fun convertJavaConstructorToFir(
javaConstructor: JavaConstructor?,
constructorId: CallableId,
javaClass: JavaClass,
ownerClassBuilder: FirJavaClassBuilder,
classTypeParameters: List,
javaTypeParameterStack: JavaTypeParameterStack,
): FirJavaConstructor {
val constructorSymbol = FirConstructorSymbol(constructorId)
return buildJavaConstructor {
source = (javaConstructor as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = [email protected]
symbol = constructorSymbol
isInner = javaClass.outerClass != null && !javaClass.isStatic
val isThisInner = this.isInner
val visibility = javaConstructor?.visibility ?: ownerClassBuilder.visibility
status = FirResolvedDeclarationStatusImpl(
visibility,
Modality.FINAL
).apply {
isExpect = false
isActual = false
isOverride = false
isInner = isThisInner
}
this.visibility = visibility
isPrimary = javaConstructor == null
returnTypeRef = buildResolvedTypeRef {
type = ownerClassBuilder.buildSelfTypeRef()
}
typeParameters += classTypeParameters.map { buildConstructedClassTypeParameterRef { symbol = it.symbol } }
if (javaConstructor != null) {
this.typeParameters += javaConstructor.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom([email protected], javaConstructor, javaTypeParameterStack)
for ((index, valueParameter) in javaConstructor.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
[email protected], index, javaTypeParameterStack,
)
}
}
}.apply {
containingClassAttr = ownerClassBuilder.symbol.toLookupTag()
}
}
private fun buildConstructorForAnnotationClass(
constructorId: CallableId,
ownerClassBuilder: FirJavaClassBuilder,
valueParametersForAnnotationConstructor: ValueParametersForAnnotationConstructor
): FirJavaConstructor {
return buildJavaConstructor {
session = [email protected]
symbol = FirConstructorSymbol(constructorId)
status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
returnTypeRef = buildResolvedTypeRef {
type = ownerClassBuilder.buildSelfTypeRef()
}
valueParameters.addIfNotNull(valueParametersForAnnotationConstructor.valueParameterForValue)
valueParameters += valueParametersForAnnotationConstructor.valueParameters
visibility = Visibilities.Public
isInner = false
isPrimary = true
}.apply {
containingClassAttr = ownerClassBuilder.symbol.toLookupTag()
}
}
private fun FirJavaClassBuilder.buildSelfTypeRef(): ConeKotlinType = symbol.constructType(
typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), isNullable = false)
}.toTypedArray(),
isNullable = false,
)
override fun getPackage(fqName: FqName): FqName? {
return packageCache.lookupCacheOrCalculate(fqName) {
try {
val facade = KotlinJavaPsiFacade.getInstance(project)
val javaPackage = facade.findPackage(fqName.asString(), searchScope) ?: return@lookupCacheOrCalculate null
FqName(javaPackage.qualifiedName)
} catch (e: ProcessCanceledException) {
return@lookupCacheOrCalculate null
}
}
}
private val knownClassNamesInPackage = mutableMapOf?>()
private fun hasTopLevelClassOf(classId: ClassId): Boolean {
val knownNames = knownClassNamesInPackage.getOrPut(classId.packageFqName) {
facade.knownClassNamesInPackage(classId.packageFqName, searchScope)
} ?: return true
return classId.relativeClassName.topLevelName() in knownNames
}
}
fun FqName.topLevelName() =
asString().substringBefore(".")
© 2015 - 2025 Weber Informatics LLC | Privacy Policy