From 78db89b46b79d7d28444a6c37aeca9bf48813b82 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Wed, 15 Dec 2021 21:33:05 +0300 Subject: [PATCH] Extract AbstractBinaryClassAnnotationLoader to be used without FE1.0 --- ...tBinaryClassAnnotationAndConstantLoader.kt | 324 +----------------- .../AbstractBinaryClassAnnotationLoader.kt | 321 +++++++++++++++++ 2 files changed, 340 insertions(+), 305 deletions(-) create mode 100644 core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationLoader.kt diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index fdffe8b653e..4ce2cbd48eb 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -8,17 +8,12 @@ package org.jetbrains.kotlin.load.kotlin import org.jetbrains.kotlin.SpecialJvmAnnotations import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader.AnnotationsContainerWithConstants 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.deserialization.Flags 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.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind @@ -29,192 +24,27 @@ import org.jetbrains.kotlin.types.KotlinType abstract class AbstractBinaryClassAnnotationAndConstantLoader( storageManager: StorageManager, - protected val kotlinClassFinder: KotlinClassFinder -) : AnnotationAndConstantLoader { - private val storage = storageManager.createMemoizedFunction> { kotlinClass -> - loadAnnotationsAndInitializers(kotlinClass) - } + kotlinClassFinder: KotlinClassFinder +) : AnnotationAndConstantLoader, + AbstractBinaryClassAnnotationLoader>(kotlinClassFinder) { + + private val storage = + storageManager.createMemoizedFunction> { kotlinClass -> + loadAnnotationsAndInitializers(kotlinClass) + } + + override fun getAnnotationsContainer(binaryClass: KotlinJvmBinaryClass): AnnotationsContainerWithConstants = storage(binaryClass) protected abstract fun loadConstant(desc: String, initializer: Any): C? protected abstract fun transformToUnsignedConstant(constant: C): C? - protected abstract fun loadAnnotation( - annotationClassId: ClassId, - source: SourceElement, - result: MutableList - ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? - - protected abstract fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): A - protected abstract fun loadAnnotationMethodDefaultValue( annotationClass: KotlinJvmBinaryClass, methodSignature: MemberSignature, visitResult: (C) -> Unit ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? - private 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 - ) - ) - ?: return listOf() - - return storage(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 { loadTypeAnnotation(it, nameResolver) } - } - - override fun loadTypeParameterAnnotations(proto: ProtoBuf.TypeParameter, nameResolver: NameResolver): List { - return proto.getExtension(JvmProtoBuf.typeParameterAnnotation).map { loadTypeAnnotation(it, nameResolver) } - } - override fun loadAnnotationDefaultValue( container: ProtoContainer, proto: ProtoBuf.Property, @@ -237,7 +67,7 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader( proto: ProtoBuf.Property, annotatedCallableKind: AnnotatedCallableKind, expectedType: KotlinType, - loader: Storage.(MemberSignature) -> C? + loader: AnnotationsContainerWithConstants.(MemberSignature) -> C? ): C? { val specialCase = getSpecialCaseContainerClass( container, @@ -260,63 +90,7 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader( return if (UnsignedTypes.isUnsignedType(expectedType)) transformToUnsignedConstant(result) else result } - private fun findClassWithAnnotationsAndInitializers( - container: ProtoContainer, specialCase: KotlinJvmBinaryClass? - ): KotlinJvmBinaryClass? { - return when { - specialCase != null -> specialCase - container is ProtoContainer.Class -> container.toBinaryClass() - else -> null - } - } - - // TODO: do not use KotlinClassFinder#findKotlinClass here because it traverses the file system in the compiler - // Introduce an API in KotlinJvmBinaryClass to find a class nearby instead - private fun getSpecialCaseContainerClass( - container: ProtoContainer, - property: Boolean, - field: Boolean, - isConst: Boolean?, - isMovedFromInterfaceCompanion: Boolean - ): 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)) - ) - } - 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('/', '.')))) - } - } - } - 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.toBinaryClass() - } - } - if (container is ProtoContainer.Package && container.source is JvmPackagePartSource) { - val jvmPackagePartSource = container.source as JvmPackagePartSource - - return jvmPackagePartSource.knownJvmBinaryClass - ?: kotlinClassFinder.findKotlinClass(jvmPackagePartSource.classId) - } - return null - } - - private fun loadAnnotationsAndInitializers(kotlinClass: KotlinJvmBinaryClass): Storage { + private fun loadAnnotationsAndInitializers(kotlinClass: KotlinJvmBinaryClass): AnnotationsContainerWithConstants { val memberAnnotations = HashMap>() val propertyConstants = HashMap() val annotationParametersDefaultValues = HashMap() @@ -375,61 +149,9 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader( } }, getCachedFileContent(kotlinClass)) - return Storage(memberAnnotations, propertyConstants, annotationParametersDefaultValues) + return AnnotationsContainerWithConstants(memberAnnotations, propertyConstants, annotationParametersDefaultValues) } - private 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 - } - - private 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 isRepeatableWithImplicitContainer(annotationClassId: ClassId, arguments: Map>): Boolean { if (annotationClassId != SpecialJvmAnnotations.JAVA_LANG_ANNOTATION_REPEATABLE) return false @@ -439,18 +161,10 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader( return isImplicitRepeatableContainer(normalClass.classId) } - 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) - return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass) - } - - private class Storage( - val memberAnnotations: Map>, + class AnnotationsContainerWithConstants( + override val memberAnnotations: Map>, val propertyConstants: Map, val annotationParametersDefaultValues: Map - ) + ) : AbstractBinaryClassAnnotationLoader.AnnotationsContainer() } + diff --git a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationLoader.kt b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationLoader.kt new file mode 100644 index 00000000000..297489c6ca0 --- /dev/null +++ b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationLoader.kt @@ -0,0 +1,321 @@ +/* + * 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.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 { + protected abstract fun getAnnotationsContainer(binaryClass: KotlinJvmBinaryClass): S + + protected abstract fun loadAnnotation( + annotationClassId: ClassId, + source: SourceElement, + result: MutableList + ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? + + protected abstract fun loadTypeAnnotation(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 + ) + ) + ?: 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 { loadTypeAnnotation(it, nameResolver) } + } + + override fun loadTypeParameterAnnotations(proto: ProtoBuf.TypeParameter, nameResolver: NameResolver): List { + return proto.getExtension(JvmProtoBuf.typeParameterAnnotation).map { loadTypeAnnotation(it, nameResolver) } + } + + protected fun findClassWithAnnotationsAndInitializers( + container: ProtoContainer, specialCase: KotlinJvmBinaryClass? + ): KotlinJvmBinaryClass? { + return when { + specialCase != null -> specialCase + container is ProtoContainer.Class -> container.toBinaryClass() + else -> null + } + } + + // TODO: do not use KotlinClassFinder#findKotlinClass here because it traverses the file system in the compiler + // Introduce an API in KotlinJvmBinaryClass to find a class nearby instead + protected fun getSpecialCaseContainerClass( + container: ProtoContainer, + property: Boolean, + field: Boolean, + isConst: Boolean?, + isMovedFromInterfaceCompanion: Boolean + ): 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)) + ) + } + 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('/', '.')))) + } + } + } + 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.toBinaryClass() + } + } + if (container is ProtoContainer.Package && container.source is JvmPackagePartSource) { + val jvmPackagePartSource = container.source as JvmPackagePartSource + + return jvmPackagePartSource.knownJvmBinaryClass + ?: kotlinClassFinder.findKotlinClass(jvmPackagePartSource.classId) + } + return null + } + + protected 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 + } + + 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) + return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass) + } + + abstract class AnnotationsContainer { + abstract val memberAnnotations: Map> + } +}