mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
Do not load Java @Repeatable for Kotlin-repeatable annotations
#KT-48131 Fixed
This commit is contained in:
@@ -6840,6 +6840,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("repeatableAnnotation.kt")
|
||||
public void testRepeatableAnnotation() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClass.kt")
|
||||
public void testSealedClass() throws Exception {
|
||||
|
||||
@@ -9,28 +9,30 @@ import org.jetbrains.kotlin.SpecialJvmAnnotations
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirClassReferenceExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.buildUnaryArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.java.createConstantOrError
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredPropertySymbols
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.constructClassType
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.constants.ClassLiteralValue
|
||||
|
||||
internal class AnnotationsLoader(private val session: FirSession) {
|
||||
internal class AnnotationsLoader(private val session: FirSession, private val kotlinClassFinder: KotlinClassFinder) {
|
||||
private fun loadAnnotation(
|
||||
annotationClassId: ClassId, result: MutableList<FirAnnotationCall>,
|
||||
): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
|
||||
@@ -141,6 +143,11 @@ internal class AnnotationsLoader(private val session: FirSession) {
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
// Do not load the @java.lang.annotation.Repeatable annotation instance generated automatically by the compiler for
|
||||
// Kotlin-repeatable annotation classes. Otherwise the reference to the implicit nested "Container" class cannot be
|
||||
// resolved, since that class is only generated in the backend, and is not visible to the frontend.
|
||||
if (isRepeatableWithImplicitContainer(lookupTag, argumentMap)) return
|
||||
|
||||
result += buildAnnotationCall {
|
||||
annotationTypeRef = lookupTag.toDefaultResolvedTypeRef()
|
||||
argumentList = buildArgumentList {
|
||||
@@ -162,6 +169,21 @@ internal class AnnotationsLoader(private val session: FirSession) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isRepeatableWithImplicitContainer(lookupTag: ConeClassLikeLookupTag, argumentMap: Map<Name, FirExpression>): Boolean {
|
||||
if (lookupTag.classId != SpecialJvmAnnotations.JAVA_LANG_ANNOTATION_REPEATABLE) return false
|
||||
|
||||
val getClassCall = argumentMap[Name.identifier("value")] as? FirGetClassCall ?: return false
|
||||
val classReference = getClassCall.argument as? FirClassReferenceExpression ?: return false
|
||||
val containerType = classReference.classTypeRef.coneType as? ConeClassLikeType ?: return false
|
||||
val classId = containerType.lookupTag.classId
|
||||
if (classId.outerClassId == null ||
|
||||
classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
|
||||
) return false
|
||||
|
||||
val klass = kotlinClassFinder.findKotlinClass(classId)
|
||||
return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass)
|
||||
}
|
||||
|
||||
internal fun loadAnnotationIfNotSpecial(
|
||||
annotationClassId: ClassId, result: MutableList<FirAnnotationCall>,
|
||||
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
|
||||
|
||||
@@ -33,14 +33,14 @@ class JvmBinaryAnnotationDeserializer(
|
||||
byteContent: ByteArray?
|
||||
) : AbstractAnnotationDeserializer(session) {
|
||||
private val annotationInfo by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
session.loadMemberAnnotations(kotlinBinaryClass, byteContent)
|
||||
session.loadMemberAnnotations(kotlinBinaryClass, byteContent, kotlinClassFinder)
|
||||
}
|
||||
|
||||
private val annotationInfoForDefaultImpls by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val defaultImplsClassId = kotlinBinaryClass.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
|
||||
val (defaultImplsClass, defaultImplsByteContent) = kotlinClassFinder.findKotlinClassOrContent(defaultImplsClassId) as? KotlinClassFinder.Result.KotlinClass
|
||||
?: return@lazy null
|
||||
session.loadMemberAnnotations(defaultImplsClass, defaultImplsByteContent)
|
||||
session.loadMemberAnnotations(defaultImplsClass, defaultImplsByteContent, kotlinClassFinder)
|
||||
}
|
||||
|
||||
override fun inheritAnnotationInfo(parent: AbstractAnnotationDeserializer) {
|
||||
@@ -288,9 +288,13 @@ class JvmBinaryAnnotationDeserializer(
|
||||
private data class MemberAnnotations(val memberAnnotations: MutableMap<MemberSignature, MutableList<FirAnnotationCall>>)
|
||||
|
||||
// TODO: better to be in KotlinDeserializedJvmSymbolsProvider?
|
||||
private fun FirSession.loadMemberAnnotations(kotlinBinaryClass: KotlinJvmBinaryClass, byteContent: ByteArray?): MemberAnnotations {
|
||||
private fun FirSession.loadMemberAnnotations(
|
||||
kotlinBinaryClass: KotlinJvmBinaryClass,
|
||||
byteContent: ByteArray?,
|
||||
kotlinClassFinder: KotlinClassFinder,
|
||||
): MemberAnnotations {
|
||||
val memberAnnotations = hashMapOf<MemberSignature, MutableList<FirAnnotationCall>>()
|
||||
val annotationsLoader = AnnotationsLoader(this)
|
||||
val annotationsLoader = AnnotationsLoader(this, kotlinClassFinder)
|
||||
|
||||
kotlinBinaryClass.visitMembers(object : KotlinJvmBinaryClass.MemberVisitor {
|
||||
override fun visitMethod(name: Name, desc: String): KotlinJvmBinaryClass.MethodAnnotationVisitor {
|
||||
|
||||
@@ -9,15 +9,17 @@ import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
|
||||
import org.jetbrains.kotlin.fir.caches.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.getDeprecationInfos
|
||||
import org.jetbrains.kotlin.fir.deserialization.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.java.topLevelName
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.load.java.JavaClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.*
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
@@ -44,7 +46,7 @@ class KotlinDeserializedJvmSymbolsProvider(
|
||||
javaClassFinder: JavaClassFinder,
|
||||
) : AbstractFirDeserializedSymbolsProvider(session, moduleDataProvider, kotlinScopeProvider) {
|
||||
private val knownNameInPackageCache = KnownNameInPackageCache(session, javaClassFinder)
|
||||
private val annotationsLoader = AnnotationsLoader(session)
|
||||
private val annotationsLoader = AnnotationsLoader(session, kotlinClassFinder)
|
||||
|
||||
override fun computePackagePartsInfos(packageFqName: FqName): List<PackagePartsCacheData> {
|
||||
return packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName ->
|
||||
|
||||
23
compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt
vendored
Normal file
23
compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_MULTI_MODULE_IR_AGAINST_OLD
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
// JVM_TARGET: 1.8
|
||||
// MODULE: lib
|
||||
// FILE: A.kt
|
||||
|
||||
@Repeatable
|
||||
annotation class A(val value: String)
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: box.kt
|
||||
|
||||
class C {
|
||||
@A("O") @A("K")
|
||||
fun f() {}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = C::class.java.getDeclaredMethod("f").getAnnotationsByType(A::class.java)
|
||||
return a[0].value + a[1].value
|
||||
}
|
||||
@@ -6840,6 +6840,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("repeatableAnnotation.kt")
|
||||
public void testRepeatableAnnotation() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClass.kt")
|
||||
public void testSealedClass() throws Exception {
|
||||
|
||||
@@ -397,6 +397,12 @@ public class JvmIrAgainstOldBoxTestGenerated extends AbstractJvmIrAgainstOldBoxT
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("repeatableAnnotation.kt")
|
||||
public void testRepeatableAnnotation() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClass.kt")
|
||||
public void testSealedClass() throws Exception {
|
||||
|
||||
@@ -47,6 +47,7 @@ object JvmAbi {
|
||||
const val IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS = "-impl"
|
||||
|
||||
const val REPEATABLE_ANNOTATION_CONTAINER_NAME = "Container"
|
||||
val REPEATABLE_ANNOTATION_CONTAINER_META_ANNOTATION = ClassId.fromString("kotlin/jvm/internal/RepeatableContainer")
|
||||
|
||||
/**
|
||||
* @param baseName JVM name of the property getter since Kotlin 1.4, or Kotlin name of the property otherwise.
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.util.*
|
||||
|
||||
abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any>(
|
||||
storageManager: StorageManager,
|
||||
private val kotlinClassFinder: KotlinClassFinder
|
||||
protected val kotlinClassFinder: KotlinClassFinder
|
||||
) : AnnotationAndConstantLoader<A, C> {
|
||||
private val storage = storageManager.createMemoizedFunction<KotlinJvmBinaryClass, Storage<A, C>> { kotlinClass ->
|
||||
loadAnnotationsAndInitializers(kotlinClass)
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.SpecialJvmAnnotations
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
@@ -29,7 +32,6 @@ import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import java.util.*
|
||||
|
||||
class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
private val module: ModuleDescriptor,
|
||||
@@ -143,7 +145,13 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
result.add(AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source))
|
||||
val annotation = AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source)
|
||||
// Do not load the @java.lang.annotation.Repeatable annotation instance generated automatically by the compiler for
|
||||
// Kotlin-repeatable annotation classes. Otherwise the reference to the implicit nested "Container" class cannot be
|
||||
// resolved, since that class is only generated in the backend, and is not visible to the frontend.
|
||||
if (!isRepeatableWithImplicitContainer(annotation)) {
|
||||
result.add(annotation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createConstant(name: Name?, value: Any?): ConstantValue<*> {
|
||||
@@ -156,4 +164,18 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
private fun resolveClass(classId: ClassId): ClassDescriptor {
|
||||
return module.findNonGenericClassAcrossDependencies(classId, notFoundClasses)
|
||||
}
|
||||
|
||||
private fun isRepeatableWithImplicitContainer(annotation: AnnotationDescriptor): Boolean {
|
||||
if (annotation.fqName != JvmAnnotationNames.REPEATABLE_ANNOTATION) return false
|
||||
|
||||
val containerKClassValue = annotation.allValueArguments[Name.identifier("value")] as? KClassValue ?: return false
|
||||
val normalClass = containerKClassValue.value as? KClassValue.Value.NormalClass ?: return false
|
||||
val classId = normalClass.classId
|
||||
if (classId.outerClassId == null ||
|
||||
classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
|
||||
) return false
|
||||
|
||||
val klass = kotlinClassFinder.findKotlinClass(classId)
|
||||
return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
object SpecialJvmAnnotations {
|
||||
val SPECIAL_ANNOTATIONS: Set<ClassId> = listOf(
|
||||
@@ -18,4 +20,22 @@ object SpecialJvmAnnotations {
|
||||
JvmAnnotationNames.RETENTION_ANNOTATION,
|
||||
JvmAnnotationNames.DOCUMENTED_ANNOTATION
|
||||
).mapTo(mutableSetOf(), ClassId::topLevel)
|
||||
|
||||
val JAVA_LANG_ANNOTATION_REPEATABLE = ClassId.topLevel(JvmAnnotationNames.REPEATABLE_ANNOTATION)
|
||||
|
||||
fun isAnnotatedWithContainerMetaAnnotation(klass: KotlinJvmBinaryClass): Boolean {
|
||||
var result = false
|
||||
klass.loadClassAnnotations(object : KotlinJvmBinaryClass.AnnotationVisitor {
|
||||
override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
|
||||
if (classId == JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_META_ANNOTATION) {
|
||||
result = true
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
}
|
||||
}, null)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user