Skip to content

Commit 9b3c07c

Browse files
committed
Add annotation class meta-annotations per classes' expected JVM lowering
1 parent f7d2644 commit 9b3c07c

File tree

1 file changed

+193
-5
lines changed

1 file changed

+193
-5
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 193 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,29 @@ import com.semmle.extractor.java.OdasaOutput
99
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
1010
import org.jetbrains.kotlin.backend.common.lower.parents
1111
import org.jetbrains.kotlin.backend.common.pop
12+
import org.jetbrains.kotlin.backend.jvm.ir.getAnnotationRetention
13+
import org.jetbrains.kotlin.builtins.StandardNames
1214
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
1315
import org.jetbrains.kotlin.config.JvmAnalysisFlags
1416
import org.jetbrains.kotlin.descriptors.*
17+
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
18+
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
1519
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
1620
import org.jetbrains.kotlin.ir.IrElement
1721
import org.jetbrains.kotlin.ir.IrStatement
1822
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
23+
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
1924
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
2025
import org.jetbrains.kotlin.ir.declarations.*
2126
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
2227
import org.jetbrains.kotlin.ir.expressions.*
23-
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
24-
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
28+
import org.jetbrains.kotlin.ir.expressions.impl.*
2529
import org.jetbrains.kotlin.ir.symbols.*
2630
import org.jetbrains.kotlin.ir.types.*
31+
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
2732
import org.jetbrains.kotlin.ir.util.*
33+
import org.jetbrains.kotlin.load.java.JvmAbi
34+
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
2835
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
2936
import org.jetbrains.kotlin.load.java.structure.JavaClass
3037
import org.jetbrains.kotlin.load.java.structure.JavaMethod
@@ -34,7 +41,9 @@ import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
3441
import org.jetbrains.kotlin.name.FqName
3542
import org.jetbrains.kotlin.types.Variance
3643
import org.jetbrains.kotlin.util.OperatorNameConventions
44+
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
3745
import java.io.Closeable
46+
import java.lang.annotation.ElementType
3847
import java.util.*
3948
import kotlin.collections.ArrayList
4049

@@ -458,12 +467,16 @@ open class KotlinFileExtractor(
458467
extractDeclInitializers(c.declarations, false) { Pair(blockId, obinitId) }
459468
}
460469

461-
private fun extractAnnotations(c: IrAnnotationContainer, parent: Label<out DbExprparent>) {
462-
for ((idx, constructorCall: IrConstructorCall) in c.annotations.sortedBy { v -> v.type.classFqName?.asString() }.withIndex()) {
470+
private fun extractAnnotations(annotations: List<IrConstructorCall>, parent: Label<out DbExprparent>) {
471+
for ((idx, constructorCall: IrConstructorCall) in annotations.sortedBy { v -> v.type.classFqName?.asString() }.withIndex()) {
463472
extractAnnotation(constructorCall, parent, idx)
464473
}
465474
}
466475

476+
private fun extractAnnotations(c: IrAnnotationContainer, parent: Label<out DbExprparent>) {
477+
extractAnnotations(c.annotations, parent)
478+
}
479+
467480
private fun extractAnnotation(
468481
constructorCall: IrConstructorCall,
469482
parent: Label<out DbExprparent>,
@@ -559,6 +572,175 @@ open class KotlinFileExtractor(
559572
}
560573
}
561574

575+
private val javaAnnotationTargetElementType by lazy { referenceExternalClass("java.lang.annotation.ElementType") }
576+
577+
private val javaAnnotationTarget by lazy { referenceExternalClass("java.lang.annotation.Target") }
578+
579+
// Taken from AdditionalClassAnnotationLowering.kt
580+
private fun getApplicableTargetSet(c: IrClass): Set<KotlinTarget>? {
581+
val targetEntry = c.getAnnotation(StandardNames.FqNames.target) ?: return null
582+
return loadAnnotationTargets(targetEntry)
583+
}
584+
585+
// Taken from AdditionalClassAnnotationLowering.kt
586+
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
587+
val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null
588+
return valueArgument.elements.filterIsInstance<IrGetEnumValue>().mapNotNull {
589+
KotlinTarget.valueOrNull(it.symbol.owner.name.asString())
590+
}.toSet()
591+
}
592+
593+
594+
private fun findEnumEntry(c: IrClass, name: String) =
595+
c.declarations.filterIsInstance<IrEnumEntry>().find { it.name.asString() == name }
596+
597+
// Adapted from JvmSymbols.kt
598+
private val jvm6TargetMap by lazy {
599+
javaAnnotationTargetElementType?.let {
600+
val etMethod = findEnumEntry(it, "METHOD")
601+
mapOf(
602+
KotlinTarget.CLASS to findEnumEntry(it, "TYPE"),
603+
KotlinTarget.ANNOTATION_CLASS to findEnumEntry(it, "ANNOTATION_TYPE"),
604+
KotlinTarget.CONSTRUCTOR to findEnumEntry(it, "CONSTRUCTOR"),
605+
KotlinTarget.LOCAL_VARIABLE to findEnumEntry(it, "LOCAL_VARIABLE"),
606+
KotlinTarget.FUNCTION to etMethod,
607+
KotlinTarget.PROPERTY_GETTER to etMethod,
608+
KotlinTarget.PROPERTY_SETTER to etMethod,
609+
KotlinTarget.FIELD to findEnumEntry(it, "FIELD"),
610+
KotlinTarget.VALUE_PARAMETER to findEnumEntry(it, "PARAMETER")
611+
)
612+
}
613+
}
614+
615+
// Adapted from JvmSymbols.kt
616+
private val jvm8TargetMap by lazy {
617+
javaAnnotationTargetElementType?.let {
618+
jvm6TargetMap?.let { j6Map ->
619+
j6Map + mapOf(
620+
KotlinTarget.TYPE_PARAMETER to findEnumEntry(it, "TYPE_PARAMETER"),
621+
KotlinTarget.TYPE to findEnumEntry(it, "TYPE_USE")
622+
)
623+
}
624+
}
625+
}
626+
627+
// TODO: find out if we can spot when we're building for JVM <= 7 and omit the Java 8-only targets in that case.
628+
private fun getAnnotationTargetMap() = jvm8TargetMap
629+
630+
// Adapted from AdditionalClassAnnotationLowering.kt
631+
private fun generateTargetAnnotation(c: IrClass): IrConstructorCall? {
632+
if (c.hasAnnotation(JvmAnnotationNames.TARGET_ANNOTATION))
633+
return null
634+
val elementType = javaAnnotationTargetElementType ?: return null
635+
val targetType = javaAnnotationTarget ?: return null
636+
val targetConstructor = targetType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
637+
val targets = getApplicableTargetSet(c) ?: return null
638+
val annotationTargetMap = getAnnotationTargetMap() ?: return null
639+
640+
val javaTargets = targets.mapNotNullTo(HashSet()) { annotationTargetMap[it] }.sortedBy {
641+
ElementType.valueOf(it.symbol.owner.name.asString())
642+
}
643+
val vararg = IrVarargImpl(
644+
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
645+
type = pluginContext.irBuiltIns.arrayClass.typeWith(elementType.defaultType),
646+
varargElementType = elementType.defaultType
647+
)
648+
for (target in javaTargets) {
649+
vararg.elements.add(
650+
IrGetEnumValueImpl(
651+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, elementType.defaultType, target.symbol
652+
)
653+
)
654+
}
655+
656+
return IrConstructorCallImpl.fromSymbolOwner(
657+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0
658+
).apply {
659+
putValueArgument(0, vararg)
660+
}
661+
}
662+
663+
private val javaAnnotationRetention by lazy { referenceExternalClass("java.lang.annotation.Retention") }
664+
private val javaAnnotationRetentionPolicy by lazy { referenceExternalClass("java.lang.annotation.RetentionPolicy") }
665+
private val javaAnnotationRetentionPolicyRuntime by lazy { javaAnnotationRetentionPolicy?.let { findEnumEntry(it, "RUNTIME") } }
666+
667+
private val annotationRetentionMap by lazy {
668+
javaAnnotationRetentionPolicy?.let {
669+
mapOf(
670+
KotlinRetention.SOURCE to findEnumEntry(it, "SOURCE"),
671+
KotlinRetention.BINARY to findEnumEntry(it, "CLASS"),
672+
KotlinRetention.RUNTIME to javaAnnotationRetentionPolicyRuntime
673+
)
674+
}
675+
}
676+
677+
// Taken from AdditionalClassAnnotationLowering.kt
678+
private fun generateRetentionAnnotation(irClass: IrClass): IrConstructorCall? {
679+
if (irClass.hasAnnotation(JvmAnnotationNames.RETENTION_ANNOTATION))
680+
return null
681+
val retentionMap = annotationRetentionMap ?: return null
682+
val kotlinRetentionPolicy = irClass.getAnnotationRetention()
683+
val javaRetentionPolicy = kotlinRetentionPolicy?.let { retentionMap[it] } ?: javaAnnotationRetentionPolicyRuntime ?: return null
684+
val retentionPolicyType = javaAnnotationRetentionPolicy ?: return null
685+
val retentionType = javaAnnotationRetention ?: return null
686+
val targetConstructor = retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
687+
688+
return IrConstructorCallImpl.fromSymbolOwner(
689+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0
690+
).apply {
691+
putValueArgument(
692+
0,
693+
IrGetEnumValueImpl(
694+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, retentionPolicyType.defaultType, javaRetentionPolicy.symbol
695+
)
696+
)
697+
}
698+
}
699+
700+
private val javaAnnotationRepeatable by lazy { referenceExternalClass("java.lang.annotation.Repeatable") }
701+
702+
// Taken from AdditionalClassAnnotationLowering.kt
703+
private fun generateRepeatableAnnotation(irClass: IrClass): IrConstructorCall? {
704+
if (!irClass.hasAnnotation(StandardNames.FqNames.repeatable) ||
705+
irClass.hasAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION)
706+
) return null
707+
708+
val repeatableConstructor = javaAnnotationRepeatable?.declarations?.firstIsInstanceOrNull<IrConstructor>() ?: return null
709+
710+
val containerClass =
711+
irClass.declarations.singleOrNull {
712+
it is IrClass && it.name.asString() == JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
713+
} as IrClass? ?: return null
714+
val containerReference = IrClassReferenceImpl(
715+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, pluginContext.irBuiltIns.kClassClass.typeWith(containerClass.defaultType),
716+
containerClass.symbol, containerClass.defaultType
717+
)
718+
return IrConstructorCallImpl.fromSymbolOwner(
719+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, repeatableConstructor.returnType, repeatableConstructor.symbol, 0
720+
).apply {
721+
putValueArgument(0, containerReference)
722+
}
723+
}
724+
725+
private val javaAnnotationDocumented by lazy { referenceExternalClass("java.lang.annotation.Documented") }
726+
727+
// Taken from AdditionalClassAnnotationLowering.kt
728+
private fun generateDocumentedAnnotation(irClass: IrClass): IrConstructorCall? {
729+
if (!irClass.hasAnnotation(StandardNames.FqNames.mustBeDocumented) ||
730+
irClass.hasAnnotation(JvmAnnotationNames.DOCUMENTED_ANNOTATION)
731+
) return null
732+
733+
val documentedConstructor = javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>() ?: return null
734+
735+
return IrConstructorCallImpl.fromSymbolOwner(
736+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, documentedConstructor.returnType, documentedConstructor.symbol, 0
737+
)
738+
}
739+
740+
private fun generateJavaMetaAnnotations(c: IrClass) =
741+
// This is essentially AdditionalClassAnnotationLowering adapted to run outside the backend.
742+
listOfNotNull(generateTargetAnnotation(c), generateRetentionAnnotation(c), generateRepeatableAnnotation(c), generateDocumentedAnnotation(c))
743+
562744
fun extractClassSource(c: IrClass, extractDeclarations: Boolean, extractStaticInitializer: Boolean, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean): Label<out DbClassorinterface> {
563745
with("class source", c) {
564746
DeclarationStackAdjuster(c).use {
@@ -640,7 +822,13 @@ open class KotlinFileExtractor(
640822

641823
linesOfCode?.linesOfCodeInDeclaration(c, id)
642824

643-
extractAnnotations(c, id)
825+
val additionalAnnotations =
826+
if (c.kind == ClassKind.ANNOTATION_CLASS && c.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB)
827+
generateJavaMetaAnnotations(c)
828+
else
829+
listOf()
830+
831+
extractAnnotations(c.annotations + additionalAnnotations, id)
644832

645833
if (extractFunctionBodies && !c.isAnonymousObject && !c.isLocal)
646834
externalClassExtractor.writeStubTrapFile(c)

0 commit comments

Comments
 (0)