Skip to content

Commit 81353f8

Browse files
committed
Make annotation extraction compatible with all supported Kotlin versions
1 parent 757fa60 commit 81353f8

File tree

5 files changed

+115
-16
lines changed

5 files changed

+115
-16
lines changed

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

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@ package com.github.codeql
22

33
import com.github.codeql.comments.CommentExtractor
44
import com.github.codeql.utils.*
5-
import com.github.codeql.utils.versions.allOverriddenIncludingSelf
6-
import com.github.codeql.utils.versions.functionN
7-
import com.github.codeql.utils.versions.isUnderscoreParameter
5+
import com.github.codeql.utils.versions.*
86
import com.semmle.extractor.java.OdasaOutput
97
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
10-
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
118
import org.jetbrains.kotlin.backend.common.lower.parents
129
import org.jetbrains.kotlin.backend.common.pop
13-
import org.jetbrains.kotlin.backend.jvm.ir.getAnnotationRetention
1410
import org.jetbrains.kotlin.builtins.StandardNames
1511
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
1612
import org.jetbrains.kotlin.config.JvmAnalysisFlags
13+
import org.jetbrains.kotlin.config.JvmTarget
1714
import org.jetbrains.kotlin.descriptors.*
1815
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
1916
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
@@ -31,8 +28,32 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
3128
import org.jetbrains.kotlin.ir.symbols.*
3229
import org.jetbrains.kotlin.ir.types.*
3330
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
34-
import org.jetbrains.kotlin.ir.util.*
35-
import org.jetbrains.kotlin.load.java.JvmAbi
31+
import org.jetbrains.kotlin.ir.util.companionObject
32+
import org.jetbrains.kotlin.ir.util.constructedClass
33+
import org.jetbrains.kotlin.ir.util.constructors
34+
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
35+
import org.jetbrains.kotlin.ir.util.defaultType
36+
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
37+
import org.jetbrains.kotlin.ir.util.getAnnotation
38+
import org.jetbrains.kotlin.ir.util.hasAnnotation
39+
import org.jetbrains.kotlin.ir.util.hasEqualFqName
40+
import org.jetbrains.kotlin.ir.util.hasInterfaceParent
41+
import org.jetbrains.kotlin.ir.util.isAnonymousObject
42+
import org.jetbrains.kotlin.ir.util.isFakeOverride
43+
import org.jetbrains.kotlin.ir.util.isFunctionOrKFunction
44+
import org.jetbrains.kotlin.ir.util.isInterface
45+
import org.jetbrains.kotlin.ir.util.isLocal
46+
import org.jetbrains.kotlin.ir.util.isNonCompanionObject
47+
import org.jetbrains.kotlin.ir.util.isSuspend
48+
import org.jetbrains.kotlin.ir.util.isSuspendFunctionOrKFunction
49+
import org.jetbrains.kotlin.ir.util.isVararg
50+
import org.jetbrains.kotlin.ir.util.kotlinFqName
51+
import org.jetbrains.kotlin.ir.util.packageFqName
52+
import org.jetbrains.kotlin.ir.util.parentAsClass
53+
import org.jetbrains.kotlin.ir.util.parentClassOrNull
54+
import org.jetbrains.kotlin.ir.util.primaryConstructor
55+
import org.jetbrains.kotlin.ir.util.render
56+
import org.jetbrains.kotlin.ir.util.target
3657
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
3758
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
3859
import org.jetbrains.kotlin.load.java.structure.JavaClass
@@ -471,16 +492,24 @@ open class KotlinFileExtractor(
471492
extractDeclInitializers(c.declarations, false) { Pair(blockId, obinitId) }
472493
}
473494

495+
// Taken from AdditionalIrUtils.kt (not available in Kotlin < 1.6)
496+
private val IrConstructorCall.annotationClass
497+
get() = this.symbol.owner.constructedClass
498+
499+
// Taken from AdditionalIrUtils.kt (not available in Kotlin < 1.6)
500+
private fun IrConstructorCall.isAnnotationWithEqualFqName(fqName: FqName): Boolean =
501+
annotationClass.hasEqualFqName(fqName)
502+
474503
// Adapted from RepeatedAnnotationLowering.kt
475504
private fun groupRepeatableAnnotations(annotations: List<IrConstructorCall>): List<IrConstructorCall> {
476505
if (annotations.size < 2) return annotations
477506

478-
val annotationsByClass = annotations.groupByTo(mutableMapOf()) { it.symbol.owner.constructedClass }
507+
val annotationsByClass = annotations.groupByTo(mutableMapOf()) { it.annotationClass }
479508
if (annotationsByClass.values.none { it.size > 1 }) return annotations
480509

481510
val result = mutableListOf<IrConstructorCall>()
482511
for (annotation in annotations) {
483-
val annotationClass = annotation.symbol.owner.constructedClass
512+
val annotationClass = annotation.annotationClass
484513
val grouped = annotationsByClass.remove(annotationClass) ?: continue
485514
if (grouped.size < 2) {
486515
result.add(grouped.single())
@@ -501,7 +530,7 @@ open class KotlinFileExtractor(
501530
// Adapted from RepeatedAnnotationLowering.kt
502531
private fun getOrCreateContainerClass(annotationClass: IrClass): IrClass? {
503532
val metaAnnotations = annotationClass.annotations
504-
val jvmRepeatable = metaAnnotations.find { it.isAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION) }
533+
val jvmRepeatable = metaAnnotations.find { it.symbol.owner.parentAsClass.fqNameWhenAvailable == JvmAnnotationNames.REPEATABLE_ANNOTATION }
505534
return if (jvmRepeatable != null) {
506535
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)?.owner
507536
} else {
@@ -701,8 +730,11 @@ open class KotlinFileExtractor(
701730
}
702731
}
703732

704-
// TODO: find out if we can spot when we're building for JVM <= 7 and omit the Java 8-only targets in that case.
705-
private fun getAnnotationTargetMap() = jvm8TargetMap
733+
private fun getAnnotationTargetMap() =
734+
if (pluginContext.platform?.any { it.targetPlatformVersion == JvmTarget.JVM_1_6 } == true)
735+
jvm6TargetMap
736+
else
737+
jvm8TargetMap
706738

707739
// Adapted from AdditionalClassAnnotationLowering.kt
708740
private fun generateTargetAnnotation(c: IrClass): IrConstructorCall? {
@@ -751,6 +783,15 @@ open class KotlinFileExtractor(
751783
}
752784
}
753785

786+
// Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20)
787+
private fun IrClass.getAnnotationRetention(): KotlinRetention? {
788+
val retentionArgument =
789+
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0)
790+
as? IrGetEnumValue ?: return null
791+
val retentionArgumentValue = retentionArgument.symbol.owner
792+
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
793+
}
794+
754795
// Taken from AdditionalClassAnnotationLowering.kt
755796
private fun generateRetentionAnnotation(irClass: IrClass): IrConstructorCall? {
756797
if (irClass.hasAnnotation(JvmAnnotationNames.RETENTION_ANNOTATION))
@@ -777,16 +818,46 @@ open class KotlinFileExtractor(
777818
private val javaAnnotationRepeatable by lazy { referenceExternalClass("java.lang.annotation.Repeatable") }
778819
private val kotlinAnnotationRepeatableContainer by lazy { referenceExternalClass("kotlin.jvm.internal.RepeatableContainer") }
779820

821+
// Taken from declarationBuilders.kt (not available in Kotlin < 1.6):
822+
private fun addDefaultGetter(p: IrProperty, parentClass: IrClass) {
823+
val field = p.backingField!!
824+
p.addGetter {
825+
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
826+
returnType = field.type
827+
}.apply {
828+
dispatchReceiverParameter = copyParameterToFunction(parentClass.thisReceiver!!, this)
829+
body = factory.createBlockBody(
830+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
831+
IrReturnImpl(
832+
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
833+
pluginContext.irBuiltIns.nothingType,
834+
symbol,
835+
IrGetFieldImpl(
836+
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
837+
field.symbol,
838+
field.type,
839+
IrGetValueImpl(
840+
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
841+
dispatchReceiverParameter!!.type,
842+
dispatchReceiverParameter!!.symbol
843+
)
844+
)
845+
)
846+
)
847+
)
848+
}
849+
}
850+
780851
// Taken from JvmCachedDeclarations.kt
781852
private fun getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass: IrClass) =
782853
globalExtensionState.syntheticRepeatableAnnotationContainers.getOrPut(annotationClass) {
783854
val containerClass = pluginContext.irFactory.buildClass {
784855
kind = ClassKind.ANNOTATION_CLASS
785-
name = Name.identifier(JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME)
856+
name = Name.identifier("Container")
786857
}.apply {
787858
createImplicitParameterDeclarationWithWrappedDescriptor()
788859
parent = annotationClass
789-
superTypes = listOf(pluginContext.irBuiltIns.annotationType)
860+
superTypes = listOf(getAnnotationType(pluginContext))
790861
}
791862

792863
val propertyName = Name.identifier("value")
@@ -795,7 +866,7 @@ open class KotlinFileExtractor(
795866
containerClass.addConstructor {
796867
isPrimary = true
797868
}.apply {
798-
addValueParameter(propertyName, propertyType)
869+
addValueParameter(propertyName.identifier, propertyType)
799870
}
800871

801872
containerClass.addProperty {
@@ -808,7 +879,7 @@ open class KotlinFileExtractor(
808879
parent = containerClass
809880
correspondingPropertySymbol = this@property.symbol
810881
}
811-
addDefaultGetter(containerClass, pluginContext.irBuiltIns)
882+
addDefaultGetter(this, containerClass)
812883
}
813884

814885
val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.let { it.constructors.single() }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
4+
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
5+
6+
@OptIn(ObsoleteDescriptorBasedAPI::class)
7+
fun getAnnotationType(context: IrPluginContext) =
8+
context.typeTranslator.translateType(context.builtIns.annotationType)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.ir.declarations.IrFunction
4+
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
5+
import org.jetbrains.kotlin.backend.common.ir.copyTo
6+
7+
fun copyParameterToFunction(p: IrValueParameter, f: IrFunction) = p.copyTo(f)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
4+
5+
fun getAnnotationType(context: IrPluginContext) =
6+
context.irBuiltIns.annotationType
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.ir.declarations.IrFunction
4+
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
5+
import org.jetbrains.kotlin.ir.util.copyTo
6+
7+
fun copyParameterToFunction(p: IrValueParameter, f: IrFunction) = p.copyTo(f)

0 commit comments

Comments
 (0)