@@ -7,6 +7,7 @@ import com.github.codeql.utils.versions.functionN
7
7
import com.github.codeql.utils.versions.isUnderscoreParameter
8
8
import com.semmle.extractor.java.OdasaOutput
9
9
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
10
+ import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
10
11
import org.jetbrains.kotlin.backend.common.lower.parents
11
12
import org.jetbrains.kotlin.backend.common.pop
12
13
import org.jetbrains.kotlin.backend.jvm.ir.getAnnotationRetention
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.IrStatement
22
23
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
23
24
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
24
25
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
26
+ import org.jetbrains.kotlin.ir.builders.declarations.*
25
27
import org.jetbrains.kotlin.ir.declarations.*
26
28
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
27
29
import org.jetbrains.kotlin.ir.expressions.*
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
39
41
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
40
42
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
41
43
import org.jetbrains.kotlin.name.FqName
44
+ import org.jetbrains.kotlin.name.Name
42
45
import org.jetbrains.kotlin.types.Variance
43
46
import org.jetbrains.kotlin.util.OperatorNameConventions
44
47
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -468,8 +471,65 @@ open class KotlinFileExtractor(
468
471
extractDeclInitializers(c.declarations, false ) { Pair (blockId, obinitId) }
469
472
}
470
473
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
+
471
530
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()) {
473
533
extractAnnotation(constructorCall, parent, idx)
474
534
}
475
535
}
@@ -699,19 +759,73 @@ open class KotlinFileExtractor(
699
759
}
700
760
701
761
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
+ )
702
813
703
- // Taken from AdditionalClassAnnotationLowering.kt
814
+ containerClass
815
+ }
816
+
817
+ // Adapted from AdditionalClassAnnotationLowering.kt
704
818
private fun generateRepeatableAnnotation (irClass : IrClass ): IrConstructorCall ? {
705
819
if (! irClass.hasAnnotation(StandardNames .FqNames .repeatable) ||
706
820
irClass.hasAnnotation(JvmAnnotationNames .REPEATABLE_ANNOTATION )
707
821
) return null
708
822
709
823
val repeatableConstructor = javaAnnotationRepeatable?.declarations?.firstIsInstanceOrNull<IrConstructor >() ? : return null
710
824
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
+
715
829
val containerReference = IrClassReferenceImpl (
716
830
UNDEFINED_OFFSET , UNDEFINED_OFFSET , pluginContext.irBuiltIns.kClassClass.typeWith(containerClass.defaultType),
717
831
containerClass.symbol, containerClass.defaultType
0 commit comments