All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kotlin.reflect.jvm.internal.impl.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kotlin.reflect.jvm.internal.impl.load.kotlin

import kotlin.reflect.jvm.internal.impl.descriptors.SourceElement
import kotlin.reflect.jvm.internal.impl.descriptors.annotations.AnnotationUseSiteTarget
import kotlin.reflect.jvm.internal.impl.load.java.JvmAbi
import kotlin.reflect.jvm.internal.impl.load.java.JvmAnnotationNames
import kotlin.reflect.jvm.internal.impl.name.ClassId
import kotlin.reflect.jvm.internal.impl.name.FqName
import kotlin.reflect.jvm.internal.impl.name.Name
import kotlin.reflect.jvm.internal.impl.protobuf.MessageLite
import kotlin.reflect.jvm.internal.impl.serialization.Flags
import kotlin.reflect.jvm.internal.impl.serialization.ProtoBuf
import kotlin.reflect.jvm.internal.impl.serialization.deserialization.*
import kotlin.reflect.jvm.internal.impl.serialization.jvm.ClassMapperLite
import kotlin.reflect.jvm.internal.impl.serialization.jvm.JvmProtoBuf
import kotlin.reflect.jvm.internal.impl.serialization.jvm.JvmProtoBuf.propertySignature
import kotlin.reflect.jvm.internal.impl.serialization.jvm.JvmProtoBufUtil
import kotlin.reflect.jvm.internal.impl.storage.StorageManager
import kotlin.reflect.jvm.internal.impl.types.KotlinType
import java.util.*

abstract class AbstractBinaryClassAnnotationAndConstantLoader(
        storageManager: StorageManager,
        private val kotlinClassFinder: KotlinClassFinder
) : AnnotationAndConstantLoader {
    private val storage = storageManager.createMemoizedFunction> {
        kotlinClass ->
        loadAnnotationsAndInitializers(kotlinClass)
    }

    protected abstract fun loadConstant(desc: String, initializer: Any): C?

    protected abstract fun loadAnnotation(
            annotationClassId: ClassId,
            source: SourceElement,
            result: MutableList
    ): KotlinJvmBinaryClass.AnnotationArgumentVisitor?

    protected abstract fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): A

    private fun loadAnnotationIfNotSpecial(
            annotationClassId: ClassId,
            source: SourceElement,
            result: MutableList
    ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
        if (annotationClassId in SPECIAL_ANNOTATIONS) return null

        return loadAnnotation(annotationClassId, source, result)
    }

    private fun ProtoContainer.Class.toBinaryClass(): KotlinJvmBinaryClass? =
            (source as? KotlinJvmBinarySourceElement)?.binaryClass

    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() {
            }
        })

        return result
    }

    override fun loadCallableAnnotations(container: ProtoContainer, proto: MessageLite, kind: AnnotatedCallableKind): List {
        if (kind == AnnotatedCallableKind.PROPERTY) {
            proto as ProtoBuf.Property

            val syntheticFunctionSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, synthetic = true)
            val fieldSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, field = true)

            val isConst = Flags.IS_CONST.get(proto.flags)

            val propertyAnnotations = syntheticFunctionSignature?.let { sig ->
                findClassAndLoadMemberAnnotations(container, sig, property = true, isConst = isConst)
            }.orEmpty()

            val fieldAnnotations = fieldSignature?.let { sig ->
                findClassAndLoadMemberAnnotations(container, sig, property = true, field = true, isConst = isConst)
            }.orEmpty()

            // TODO: check delegate presence in some other way
            return loadPropertyAnnotations(propertyAnnotations, fieldAnnotations,
                                           if (fieldSignature?.signature?.contains(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX) ?: false) {
                                               AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD
                                           }
                                           else {
                                               AnnotationUseSiteTarget.FIELD
                                           })
        }

        val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, kind) ?: return emptyList()
        return transformAnnotations(findClassAndLoadMemberAnnotations(container, signature))
    }

    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)
        )
        return findClassAndLoadMemberAnnotations(container, signature)
    }

    protected abstract fun loadPropertyAnnotations(propertyAnnotations: List, fieldAnnotations: List,
                                                   fieldUseSiteTarget: AnnotationUseSiteTarget): List

    protected abstract fun transformAnnotations(annotations: List): List

    private fun findClassAndLoadMemberAnnotations(
            container: ProtoContainer, signature: MemberSignature,
            property: Boolean = false, field: Boolean = false, isConst: Boolean? = null
    ): List {
        val kotlinClass =
                findClassWithAnnotationsAndInitializers(container, getSpecialCaseContainerClass(container, property, field, isConst))
                ?: 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.javaClass}")
        }
    }

    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 loadPropertyConstant(container: ProtoContainer, proto: ProtoBuf.Property, expectedType: KotlinType): C? {
        val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, AnnotatedCallableKind.PROPERTY)
                        ?: return null

        val specialCase = getSpecialCaseContainerClass(container, property = true, field = true, isConst = Flags.IS_CONST.get(proto.flags))
        val kotlinClass = findClassWithAnnotationsAndInitializers(container, specialCase) ?: return null

        return storage(kotlinClass).propertyConstants[signature]
    }

    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?
    ): 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)) {
                // 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) {
            return kotlinClassFinder.findKotlinClass((container.source as JvmPackagePartSource).classId)
        }
        return null
    }

    private fun loadAnnotationsAndInitializers(kotlinClass: KotlinJvmBinaryClass): Storage {
        val memberAnnotations = HashMap>()
        val propertyConstants = HashMap()

        kotlinClass.visitMembers(object : KotlinJvmBinaryClass.MemberVisitor {
            override fun visitMethod(name: Name, desc: String): KotlinJvmBinaryClass.MethodAnnotationVisitor? {
                return AnnotationVisitorForMethod(MemberSignature.fromMethodNameAndDesc(name.asString(), desc))
            }

            override fun visitField(name: Name, desc: String, initializer: Any?): KotlinJvmBinaryClass.AnnotationVisitor? {
                val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), desc)

                if (initializer != null) {
                    val constant = loadConstant(desc, initializer)
                    if (constant != null) {
                        propertyConstants[signature] = constant
                    }
                }
                return MemberAnnotationVisitor(signature)
            }

            inner class AnnotationVisitorForMethod(signature: MemberSignature) : MemberAnnotationVisitor(signature), KotlinJvmBinaryClass.MethodAnnotationVisitor {

                override fun visitParameterAnnotation(
                        index: Int, classId: ClassId, source: SourceElement
                ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
                    val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index)
                    var result = memberAnnotations[paramSignature]
                    if (result == null) {
                        result = ArrayList()
                        memberAnnotations[paramSignature] = result
                    }
                    return loadAnnotationIfNotSpecial(classId, source, result)
                }
            }

            open inner class MemberAnnotationVisitor(protected val signature: MemberSignature) : KotlinJvmBinaryClass.AnnotationVisitor {
                private val result = ArrayList()

                override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
                    return loadAnnotationIfNotSpecial(classId, source, result)
                }

                override fun visitEnd() {
                    if (result.isNotEmpty()) {
                        memberAnnotations[signature] = result
                    }
                }
            }
        })

        return Storage(memberAnnotations, propertyConstants)
    }

    private fun getPropertySignature(
            proto: ProtoBuf.Property,
            nameResolver: NameResolver,
            typeTable: TypeTable,
            field: Boolean = false,
            synthetic: Boolean = false
    ): MemberSignature? {
        val signature =
                if (proto.hasExtension(propertySignature)) proto.getExtension(propertySignature)
                else return null

        if (field) {
            val (name, desc) = JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable) ?: return null
            return MemberSignature.fromFieldNameAndDesc(name, desc)
        }
        else if (synthetic && signature.hasSyntheticMethod()) {
            return MemberSignature.fromMethod(nameResolver, signature.syntheticMethod)
        }

        return null
    }

    private fun getCallableSignature(
            proto: MessageLite,
            nameResolver: NameResolver,
            typeTable: TypeTable,
            kind: AnnotatedCallableKind
    ): MemberSignature? {
        return when {
            proto is ProtoBuf.Constructor -> {
                MemberSignature.fromMethodNameAndDesc(JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable) ?: return null)
            }
            proto is ProtoBuf.Function -> {
                MemberSignature.fromMethodNameAndDesc(JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable) ?: return null)
            }
            proto is ProtoBuf.Property && proto.hasExtension(propertySignature) -> {
                val signature = proto.getExtension(propertySignature)
                when (kind) {
                    AnnotatedCallableKind.PROPERTY_GETTER -> MemberSignature.fromMethod(nameResolver, signature.getter)
                    AnnotatedCallableKind.PROPERTY_SETTER -> MemberSignature.fromMethod(nameResolver, signature.setter)
                    AnnotatedCallableKind.PROPERTY -> getPropertySignature(proto, nameResolver, typeTable, true, true)
                    else -> null
                }
            }
            else -> null
        }
    }

    private class Storage(
            val memberAnnotations: Map>,
            val propertyConstants: Map
    )

    private companion object {
        val SPECIAL_ANNOTATIONS = listOf(
                JvmAnnotationNames.METADATA_FQ_NAME,
                JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION,
                JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION,
                FqName("java.lang.annotation.Target"),
                FqName("java.lang.annotation.Retention"),
                FqName("java.lang.annotation.Documented")
        ).map(ClassId::topLevel).toSet()
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy