org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationLoader.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.SpecialJvmAnnotations
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.*
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf.propertySignature
import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind
import org.jetbrains.kotlin.serialization.deserialization.AnnotationLoader
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
abstract class AbstractBinaryClassAnnotationLoader>(
protected val kotlinClassFinder: KotlinClassFinder
) : AnnotationLoader {
abstract val jvmMetadataVersion: JvmMetadataVersion
protected abstract fun getAnnotationsContainer(binaryClass: KotlinJvmBinaryClass): S
protected abstract fun loadAnnotation(
annotationClassId: ClassId,
source: SourceElement,
result: MutableList
): KotlinJvmBinaryClass.AnnotationArgumentVisitor?
abstract override fun loadAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): A
protected fun loadAnnotationIfNotSpecial(
annotationClassId: ClassId,
source: SourceElement,
result: MutableList
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
if (annotationClassId in SpecialJvmAnnotations.SPECIAL_ANNOTATIONS) return null
return loadAnnotation(annotationClassId, source, result)
}
private fun ProtoContainer.Class.toBinaryClass(): KotlinJvmBinaryClass? =
(source as? KotlinJvmBinarySourceElement)?.binaryClass
protected open fun getCachedFileContent(kotlinClass: KotlinJvmBinaryClass): ByteArray? = null
override fun loadClassAnnotations(container: ProtoContainer.Class): List {
val kotlinClass = container.toBinaryClass() ?: error("Class for loading annotations is not found: ${container.debugFqName()}")
val result = ArrayList(1)
kotlinClass.loadClassAnnotations(object : KotlinJvmBinaryClass.AnnotationVisitor {
override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
return loadAnnotationIfNotSpecial(classId, source, result)
}
override fun visitEnd() {
}
}, getCachedFileContent(kotlinClass))
return result
}
override fun loadCallableAnnotations(container: ProtoContainer, proto: MessageLite, kind: AnnotatedCallableKind): List {
if (kind == AnnotatedCallableKind.PROPERTY) {
return loadPropertyAnnotations(container, proto as ProtoBuf.Property, PropertyRelatedElement.PROPERTY)
}
val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, kind) ?: return emptyList()
return findClassAndLoadMemberAnnotations(container, signature)
}
override fun loadPropertyBackingFieldAnnotations(container: ProtoContainer, proto: ProtoBuf.Property): List =
loadPropertyAnnotations(container, proto, PropertyRelatedElement.BACKING_FIELD)
override fun loadPropertyDelegateFieldAnnotations(container: ProtoContainer, proto: ProtoBuf.Property): List =
loadPropertyAnnotations(container, proto, PropertyRelatedElement.DELEGATE_FIELD)
private enum class PropertyRelatedElement {
PROPERTY,
BACKING_FIELD,
DELEGATE_FIELD,
}
private fun loadPropertyAnnotations(container: ProtoContainer, proto: ProtoBuf.Property, element: PropertyRelatedElement): List {
val isConst = Flags.IS_CONST.get(proto.flags)
val isMovedFromInterfaceCompanion = JvmProtoBufUtil.isMovedFromInterfaceCompanion(proto)
if (element == PropertyRelatedElement.PROPERTY) {
val syntheticFunctionSignature =
getPropertySignature(proto, container.nameResolver, container.typeTable, synthetic = true) ?: return emptyList()
return findClassAndLoadMemberAnnotations(
container, syntheticFunctionSignature, property = true, isConst = isConst,
isMovedFromInterfaceCompanion = isMovedFromInterfaceCompanion
)
}
val fieldSignature =
getPropertySignature(proto, container.nameResolver, container.typeTable, field = true) ?: return emptyList()
// TODO: check delegate presence in some other way
val isDelegated = JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX in fieldSignature.signature
if (isDelegated != (element == PropertyRelatedElement.DELEGATE_FIELD)) return emptyList()
return findClassAndLoadMemberAnnotations(
container, fieldSignature, property = true, field = true, isConst = isConst,
isMovedFromInterfaceCompanion = isMovedFromInterfaceCompanion
)
}
override fun loadEnumEntryAnnotations(container: ProtoContainer, proto: ProtoBuf.EnumEntry): List {
val signature = MemberSignature.fromFieldNameAndDesc(
container.nameResolver.getString(proto.name),
ClassMapperLite.mapClass((container as ProtoContainer.Class).classId.asString())
)
return findClassAndLoadMemberAnnotations(container, signature)
}
private fun findClassAndLoadMemberAnnotations(
container: ProtoContainer,
signature: MemberSignature,
property: Boolean = false,
field: Boolean = false,
isConst: Boolean? = null,
isMovedFromInterfaceCompanion: Boolean = false
): List {
val kotlinClass =
findClassWithAnnotationsAndInitializers(
container,
getSpecialCaseContainerClass(
container,
property,
field,
isConst,
isMovedFromInterfaceCompanion,
kotlinClassFinder, jvmMetadataVersion
)
)
?: return listOf()
return getAnnotationsContainer(kotlinClass).memberAnnotations[signature] ?: listOf()
}
override fun loadValueParameterAnnotations(
container: ProtoContainer,
callableProto: MessageLite,
kind: AnnotatedCallableKind,
parameterIndex: Int,
proto: ProtoBuf.ValueParameter
): List {
val methodSignature = getCallableSignature(callableProto, container.nameResolver, container.typeTable, kind)
if (methodSignature != null) {
val index = parameterIndex + computeJvmParameterIndexShift(container, callableProto)
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, index)
return findClassAndLoadMemberAnnotations(container, paramSignature)
}
return listOf()
}
private fun computeJvmParameterIndexShift(container: ProtoContainer, message: MessageLite): Int {
return when (message) {
is ProtoBuf.Function -> if (message.hasReceiver()) 1 else 0
is ProtoBuf.Property -> if (message.hasReceiver()) 1 else 0
is ProtoBuf.Constructor -> when {
(container as ProtoContainer.Class).kind == ProtoBuf.Class.Kind.ENUM_CLASS -> 2
container.isInner -> 1
else -> 0
}
else -> throw UnsupportedOperationException("Unsupported message: ${message::class.java}")
}
}
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
proto: MessageLite,
kind: AnnotatedCallableKind
): List {
val methodSignature = getCallableSignature(proto, container.nameResolver, container.typeTable, kind)
if (methodSignature != null) {
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, 0)
return findClassAndLoadMemberAnnotations(container, paramSignature)
}
return emptyList()
}
override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List {
return proto.getExtension(JvmProtoBuf.typeAnnotation).map { loadAnnotation(it, nameResolver) }
}
override fun loadTypeParameterAnnotations(proto: ProtoBuf.TypeParameter, nameResolver: NameResolver): List {
return proto.getExtension(JvmProtoBuf.typeParameterAnnotation).map { loadAnnotation(it, nameResolver) }
}
protected fun findClassWithAnnotationsAndInitializers(
container: ProtoContainer, specialCase: KotlinJvmBinaryClass?
): KotlinJvmBinaryClass? {
return when {
specialCase != null -> specialCase
container is ProtoContainer.Class -> container.toBinaryClass()
else -> null
}
}
protected fun getCallableSignature(
proto: MessageLite,
nameResolver: NameResolver,
typeTable: TypeTable,
kind: AnnotatedCallableKind,
requireHasFieldFlagForField: Boolean = false
): MemberSignature? {
return when (proto) {
is ProtoBuf.Constructor -> {
MemberSignature.fromJvmMemberSignature(
JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable) ?: return null
)
}
is ProtoBuf.Function -> {
MemberSignature.fromJvmMemberSignature(JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable) ?: return null)
}
is ProtoBuf.Property -> {
val signature = proto.getExtensionOrNull(propertySignature) ?: return null
when (kind) {
AnnotatedCallableKind.PROPERTY_GETTER ->
if (signature.hasGetter()) MemberSignature.fromMethod(nameResolver, signature.getter) else null
AnnotatedCallableKind.PROPERTY_SETTER ->
if (signature.hasSetter()) MemberSignature.fromMethod(nameResolver, signature.setter) else null
AnnotatedCallableKind.PROPERTY ->
getPropertySignature(proto, nameResolver, typeTable, true, true, requireHasFieldFlagForField)
else -> null
}
}
else -> null
}
}
protected fun isImplicitRepeatableContainer(classId: ClassId): Boolean {
if (classId.outerClassId == null ||
classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
) return false
val klass = kotlinClassFinder.findKotlinClass(classId, jvmMetadataVersion)
return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass)
}
abstract class AnnotationsContainer {
abstract val memberAnnotations: Map>
}
companion object {
fun getSpecialCaseContainerClass(
container: ProtoContainer,
property: Boolean,
field: Boolean,
isConst: Boolean?,
isMovedFromInterfaceCompanion: Boolean,
kotlinClassFinder: KotlinClassFinder,
jvmMetadataVersion: JvmMetadataVersion
): KotlinJvmBinaryClass? {
if (property) {
checkNotNull(isConst) { "isConst should not be null for property (container=$container)" }
if (container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.INTERFACE) {
return kotlinClassFinder.findKotlinClass(
container.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)),
jvmMetadataVersion
)
}
if (isConst && container is ProtoContainer.Package) {
// Const properties in multifile classes are generated into the facade class
val facadeClassName = (container.source as? JvmPackagePartSource)?.facadeClassName
if (facadeClassName != null) {
// Converting '/' to '.' is fine here because the facade class has a top level ClassId
return kotlinClassFinder.findKotlinClass(
ClassId.topLevel(FqName(facadeClassName.internalName.replace('/', '.'))),
jvmMetadataVersion
)
}
}
}
if (field && container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
val outerClass = container.outerClass
if (outerClass != null &&
(outerClass.kind == ProtoBuf.Class.Kind.CLASS || outerClass.kind == ProtoBuf.Class.Kind.ENUM_CLASS ||
(isMovedFromInterfaceCompanion &&
(outerClass.kind == ProtoBuf.Class.Kind.INTERFACE ||
outerClass.kind == ProtoBuf.Class.Kind.ANNOTATION_CLASS)))
) {
// Backing fields of properties of a companion object in a class are generated in the outer class
return (outerClass.source as? KotlinJvmBinarySourceElement)?.binaryClass
}
}
if (container is ProtoContainer.Package && container.source is JvmPackagePartSource) {
val jvmPackagePartSource = container.source as JvmPackagePartSource
return jvmPackagePartSource.knownJvmBinaryClass
?: kotlinClassFinder.findKotlinClass(jvmPackagePartSource.classId, jvmMetadataVersion)
}
return null
}
}
}
class AnnotationsContainerWithConstants(
override val memberAnnotations: Map>,
val propertyConstants: Map,
val annotationParametersDefaultValues: Map
) : AbstractBinaryClassAnnotationLoader.AnnotationsContainer()
fun getPropertySignature(
proto: ProtoBuf.Property,
nameResolver: NameResolver,
typeTable: TypeTable,
field: Boolean = false,
synthetic: Boolean = false,
requireHasFieldFlagForField: Boolean = true
): MemberSignature? {
val signature = proto.getExtensionOrNull(propertySignature) ?: return null
if (field) {
val fieldSignature =
JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable, requireHasFieldFlagForField) ?: return null
return MemberSignature.fromJvmMemberSignature(fieldSignature)
} else if (synthetic && signature.hasSyntheticMethod()) {
return MemberSignature.fromMethod(nameResolver, signature.syntheticMethod)
}
return null
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy