@@ -7,6 +7,7 @@ import com.github.codeql.utils.versions.functionN
77import com.github.codeql.utils.versions.isUnderscoreParameter
88import com.semmle.extractor.java.OdasaOutput
99import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
10+ import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
1011import org.jetbrains.kotlin.backend.common.lower.parents
1112import org.jetbrains.kotlin.backend.common.pop
1213import org.jetbrains.kotlin.backend.jvm.ir.getAnnotationRetention
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.IrStatement
2223import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
2324import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
2425import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
26+ import org.jetbrains.kotlin.ir.builders.declarations.*
2527import org.jetbrains.kotlin.ir.declarations.*
2628import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
2729import org.jetbrains.kotlin.ir.expressions.*
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
3941import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
4042import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
4143import org.jetbrains.kotlin.name.FqName
44+ import org.jetbrains.kotlin.name.Name
4245import org.jetbrains.kotlin.types.Variance
4346import org.jetbrains.kotlin.util.OperatorNameConventions
4447import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -468,8 +471,65 @@ open class KotlinFileExtractor(
468471 extractDeclInitializers(c.declarations, false ) { Pair (blockId, obinitId) }
469472 }
470473
474+ // Adapted from RepeatedAnnotationLowering.kt
475+ private fun groupRepeatableAnnotations (annotations : List <IrConstructorCall >): List <IrConstructorCall > {
476+ if (annotations.size < 2 ) return annotations
477+
478+ val annotationsByClass = annotations.groupByTo(mutableMapOf ()) { it.symbol.owner.constructedClass }
479+ if (annotationsByClass.values.none { it.size > 1 }) return annotations
480+
481+ val result = mutableListOf<IrConstructorCall >()
482+ for (annotation in annotations) {
483+ val annotationClass = annotation.symbol.owner.constructedClass
484+ val grouped = annotationsByClass.remove(annotationClass) ? : continue
485+ if (grouped.size < 2 ) {
486+ result.add(grouped.single())
487+ continue
488+ }
489+
490+ val containerClass = getOrCreateContainerClass(annotationClass)
491+ if (containerClass != null )
492+ result.add(wrapAnnotationEntriesInContainer(annotationClass, containerClass, grouped))
493+ else
494+ logger.warnElement(" Failed to find an annotation container class" , annotationClass)
495+ }
496+ return result
497+ }
498+
499+ // Adapted from RepeatedAnnotationLowering.kt
500+ private fun getOrCreateContainerClass (annotationClass : IrClass ): IrClass ? {
501+ val metaAnnotations = annotationClass.annotations
502+ val jvmRepeatable = metaAnnotations.find { it.isAnnotation(JvmAnnotationNames .REPEATABLE_ANNOTATION ) }
503+ return if (jvmRepeatable != null ) {
504+ ((jvmRepeatable.getValueArgument(0 ) as ? IrClassReference )?.symbol as ? IrClassSymbol )?.owner
505+ } else {
506+ getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass)
507+ }
508+ }
509+
510+ // Adapted from RepeatedAnnotationLowering.kt
511+ private fun wrapAnnotationEntriesInContainer (
512+ annotationClass : IrClass ,
513+ containerClass : IrClass ,
514+ entries : List <IrConstructorCall >,
515+ ): IrConstructorCall {
516+ val annotationType = annotationClass.typeWith()
517+ return IrConstructorCallImpl .fromSymbolOwner(containerClass.defaultType, containerClass.primaryConstructor!! .symbol).apply {
518+ putValueArgument(
519+ 0 ,
520+ IrVarargImpl (
521+ UNDEFINED_OFFSET , UNDEFINED_OFFSET ,
522+ pluginContext.irBuiltIns.arrayClass.typeWith(annotationType),
523+ annotationType,
524+ entries
525+ )
526+ )
527+ }
528+ }
529+
471530 private fun extractAnnotations (annotations : List <IrConstructorCall >, parent : Label <out DbExprparent >) {
472- for ((idx, constructorCall: IrConstructorCall ) in annotations.sortedBy { v -> v.type.classFqName?.asString() }.withIndex()) {
531+ val groupedAnnotations = groupRepeatableAnnotations(annotations)
532+ for ((idx, constructorCall: IrConstructorCall ) in groupedAnnotations.sortedBy { v -> v.type.classFqName?.asString() }.withIndex()) {
473533 extractAnnotation(constructorCall, parent, idx)
474534 }
475535 }
@@ -699,19 +759,73 @@ open class KotlinFileExtractor(
699759 }
700760
701761 private val javaAnnotationRepeatable by lazy { referenceExternalClass(" java.lang.annotation.Repeatable" ) }
762+ private val kotlinAnnotationRepeatableContainer by lazy { referenceExternalClass(" kotlin.jvm.internal.RepeatableContainer" ) }
763+
764+ // Taken from JvmCachedDeclarations.kt
765+ private fun getOrCreateSyntheticRepeatableAnnotationContainer (annotationClass : IrClass ) =
766+ globalExtensionState.syntheticRepeatableAnnotationContainers.getOrPut(annotationClass) {
767+ val containerClass = pluginContext.irFactory.buildClass {
768+ kind = ClassKind .ANNOTATION_CLASS
769+ name = Name .identifier(JvmAbi .REPEATABLE_ANNOTATION_CONTAINER_NAME )
770+ }.apply {
771+ createImplicitParameterDeclarationWithWrappedDescriptor()
772+ parent = annotationClass
773+ superTypes = listOf (pluginContext.irBuiltIns.annotationType)
774+ }
775+
776+ val propertyName = Name .identifier(" value" )
777+ val propertyType = pluginContext.irBuiltIns.arrayClass.typeWith(annotationClass.typeWith())
778+
779+ containerClass.addConstructor {
780+ isPrimary = true
781+ }.apply {
782+ addValueParameter(propertyName, propertyType)
783+ }
784+
785+ containerClass.addProperty {
786+ name = propertyName
787+ }.apply property@{
788+ backingField = pluginContext.irFactory.buildField {
789+ name = propertyName
790+ type = propertyType
791+ }.apply {
792+ parent = containerClass
793+ correspondingPropertySymbol = this @property.symbol
794+ }
795+ addDefaultGetter(containerClass, pluginContext.irBuiltIns)
796+ }
797+
798+ val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.let { it.constructors.single() }
799+
800+ containerClass.annotations = annotationClass.annotations
801+ .filter {
802+ it.isAnnotationWithEqualFqName(StandardNames .FqNames .retention) ||
803+ it.isAnnotationWithEqualFqName(StandardNames .FqNames .target)
804+ }
805+ .map { it.deepCopyWithSymbols(containerClass) } +
806+ listOfNotNull(
807+ repeatableContainerAnnotation?.let {
808+ IrConstructorCallImpl .fromSymbolOwner(
809+ UNDEFINED_OFFSET , UNDEFINED_OFFSET , it.returnType, it.symbol, 0
810+ )
811+ }
812+ )
702813
703- // Taken from AdditionalClassAnnotationLowering.kt
814+ containerClass
815+ }
816+
817+ // Adapted from AdditionalClassAnnotationLowering.kt
704818 private fun generateRepeatableAnnotation (irClass : IrClass ): IrConstructorCall ? {
705819 if (! irClass.hasAnnotation(StandardNames .FqNames .repeatable) ||
706820 irClass.hasAnnotation(JvmAnnotationNames .REPEATABLE_ANNOTATION )
707821 ) return null
708822
709823 val repeatableConstructor = javaAnnotationRepeatable?.declarations?.firstIsInstanceOrNull<IrConstructor >() ? : return null
710824
711- val containerClass =
712- irClass.declarations.singleOrNull {
713- it is IrClass && it.name.asString() == JvmAbi . REPEATABLE_ANNOTATION_CONTAINER_NAME
714- } as IrClass ? ? : return null
825+ val containerClass = getOrCreateSyntheticRepeatableAnnotationContainer(irClass)
826+ // Whenever a repeatable annotation with a Kotlin-synthesised container is extracted, extract the synthetic container to the same trap file.
827+ extractClassSource(containerClass, extractDeclarations = true , extractStaticInitializer = true , extractPrivateMembers = true , extractFunctionBodies = true )
828+
715829 val containerReference = IrClassReferenceImpl (
716830 UNDEFINED_OFFSET , UNDEFINED_OFFSET , pluginContext.irBuiltIns.kClassClass.typeWith(containerClass.defaultType),
717831 containerClass.symbol, containerClass.defaultType
0 commit comments