
org.jetbrains.kotlin.fir.backend.Fir2IrAnnotationsFromPluginRegistrar.kt Maven / Gradle / Ivy
/*
* 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.fir.backend
import org.jetbrains.kotlin.backend.common.extensions.IrAnnotationsFromPluginRegistrar
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.packageFqName
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.getContainingFile
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
import org.jetbrains.kotlin.fir.serialization.FirAdditionalMetadataAnnotationsProvider
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.toLookupTag
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.nameWithPackage
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class Fir2IrAnnotationsFromPluginRegistrar(private val components: Fir2IrComponents) : IrAnnotationsFromPluginRegistrar() {
private val generatedIrDeclarationsByFileByOffset = mutableMapOf, MutableList>>()
private fun IrConstructorCall.hasOnlySupportedAnnotationArgumentTypes(): Boolean {
for (i in 0 until valueArgumentsCount) {
if (getValueArgument(i) !is IrConst<*>) {
return false
}
}
return true
}
override fun addMetadataVisibleAnnotationsToElement(declaration: IrDeclaration, annotations: List) {
require(annotations.all { it.typeArgumentsCount == 0 && it.hasOnlySupportedAnnotationArgumentTypes() }) {
"Saving annotations with arguments from IR to metadata is only supported for basic constants. See KT-58968"
}
annotations.forEach {
require(it.symbol.owner.constructedClass.isAnnotationClass) { "${it.render()} is not an annotation constructor call" }
}
val fileFqName = declaration.file.nameWithPackage
val fileStorage = generatedIrDeclarationsByFileByOffset.getOrPut(fileFqName) { mutableMapOf() }
val storage = fileStorage.getOrPut(declaration.startOffset to declaration.endOffset) { mutableListOf() }
storage += annotations
declaration.annotations += annotations
}
fun createMetadataAnnotationsProvider(): FirAdditionalMetadataAnnotationsProvider {
return Provider()
}
private inner class Provider : FirAdditionalMetadataAnnotationsProvider() {
override fun findGeneratedAnnotationsFor(declaration: FirDeclaration): List {
val irAnnotations = extractGeneratedIrDeclarations(declaration).takeUnless { it.isEmpty() } ?: return emptyList()
return irAnnotations.map { it.toFirAnnotation() }
}
override fun hasGeneratedAnnotationsFor(declaration: FirDeclaration): Boolean {
return extractGeneratedIrDeclarations(declaration).isNotEmpty()
}
private fun extractGeneratedIrDeclarations(declaration: FirDeclaration): List {
val firFile = declaration.containingFile() ?: return emptyList()
val fileFqName = firFile.packageFqName.child(Name.identifier(firFile.name)).asString()
val source = declaration.source ?: return emptyList()
val fileStorage = generatedIrDeclarationsByFileByOffset[fileFqName] ?: return emptyList()
return fileStorage[(source.startOffsetSkippingComments() ?: source.startOffset) to source.endOffset] ?: emptyList()
}
private fun FirDeclaration.containingFile(): FirFile? {
if (this is FirFile) return this
// In MPP scenario containing session of declaration may differ from the main session of the module
// (if this declaration came from some common module), so in order to get the proper containing file,
// we need to use the original session of the declaration
val containingSession = moduleData.session
val topmostParent = topmostParent(containingSession)
return containingSession.firProvider.getContainingFile(topmostParent.symbol)
}
private fun FirDeclaration.topmostParent(session: FirSession): FirDeclaration {
return when (this) {
is FirClassLikeDeclaration -> runIf(!classId.isLocal) { classId.topmostParentClassId.toSymbol(session)?.fir }
is FirTypeParameter -> containingDeclarationSymbol.fir.topmostParent(session)
is FirValueParameter -> containingFunctionSymbol.fir.topmostParent(session)
is FirCallableDeclaration -> symbol.callableId.classId
?.takeIf { !it.isLocal }
?.topmostParentClassId
?.toSymbol(session)
?.fir
else -> error("Unsupported declaration type: $this")
} ?: this
}
private val ClassId.topmostParentClassId: ClassId
get() = parentClassId?.topmostParentClassId ?: this
private fun IrConstructorCall.toFirAnnotation(): FirAnnotation {
val annotationClassId = this.symbol.owner.constructedClass.classId!!
return buildAnnotation {
annotationTypeRef = annotationClassId
.toLookupTag()
.constructClassType(typeArguments = emptyArray(), isNullable = false)
.toFirResolvedTypeRef()
argumentMapping = buildAnnotationArgumentMapping {
for (i in 0 until [email protected]) {
val name = [email protected][i].name
val argument = [email protected](i) as IrConst<*>
this.mapping[name] = when (argument.kind) {
IrConstKind.Boolean -> buildConstExpression(
source = null,
ConstantValueKind.Boolean,
argument.value as Boolean,
setType = true
)
IrConstKind.Byte -> buildConstExpression(
source = null,
ConstantValueKind.Byte,
argument.value as Byte,
setType = true
)
IrConstKind.Char -> buildConstExpression(
source = null,
ConstantValueKind.Char,
argument.value as Char,
setType = true
)
IrConstKind.Double -> buildConstExpression(
source = null,
ConstantValueKind.Double,
argument.value as Double,
setType = true
)
IrConstKind.Float -> buildConstExpression(
source = null,
ConstantValueKind.Float,
argument.value as Float,
setType = true
)
IrConstKind.Int -> buildConstExpression(
source = null,
ConstantValueKind.Int,
argument.value as Int,
setType = true
)
IrConstKind.Long -> buildConstExpression(
source = null,
ConstantValueKind.Long,
argument.value as Long,
setType = true
)
IrConstKind.Null -> buildConstExpression(
source = null,
ConstantValueKind.Null,
value = null,
setType = true
)
IrConstKind.Short -> buildConstExpression(
source = null,
ConstantValueKind.Short,
argument.value as Short,
setType = false
)
IrConstKind.String -> buildConstExpression(
source = null,
ConstantValueKind.String,
argument.value as String,
setType = false
)
}
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy