[cls] include annotation arguments in cls stubs

^ KTIJ-24666
this would allow to build fir based on stubs,
do not keep ProtoBuf in memory and
search in decompiled code by stubs
This commit is contained in:
Anna Kozlova
2023-04-13 12:15:23 +02:00
parent f0af7c4228
commit 7ee648a4f5
19 changed files with 452 additions and 168 deletions
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2023 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.analysis.decompiler.stub
import org.jetbrains.kotlin.constant.ConstantValue
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
data class AnnotationWithArgs(val classId: ClassId, val args: Map<Name, ConstantValue<*>>)
@@ -114,7 +114,7 @@ abstract class CallableClsStubBuilder(
}
abstract val receiverType: ProtoBuf.Type?
abstract val receiverAnnotations: List<ClassIdWithTarget>
abstract val receiverAnnotations: List<AnnotationWithTarget>
abstract val returnType: ProtoBuf.Type?
abstract val contextReceiverTypes: List<ProtoBuf.Type>
@@ -149,11 +149,11 @@ private class FunctionClsStubBuilder(
override val receiverType: ProtoBuf.Type?
get() = functionProto.receiverType(c.typeTable)
override val receiverAnnotations: List<ClassIdWithTarget>
override val receiverAnnotations: List<AnnotationWithTarget>
get() {
return c.components.annotationLoader
.loadExtensionReceiverParameterAnnotations(protoContainer, functionProto, AnnotatedCallableKind.FUNCTION)
.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.RECEIVER) }
.map { AnnotationWithTarget(it, AnnotationUseSiteTarget.RECEIVER) }
}
override val returnType: ProtoBuf.Type
@@ -176,10 +176,10 @@ private class FunctionClsStubBuilder(
// If function is marked as having no annotations, we don't create stubs for it
if (!Flags.HAS_ANNOTATIONS.get(functionProto.flags)) return
val annotationIds = c.components.annotationLoader.loadCallableAnnotations(
val annotations = c.components.annotationLoader.loadCallableAnnotations(
protoContainer, functionProto, AnnotatedCallableKind.FUNCTION
)
createAnnotationStubs(annotationIds, modifierListStubImpl)
createAnnotationStubs(annotations, modifierListStubImpl)
}
override fun doCreateCallableStub(parent: StubElement<out PsiElement>): StubElement<out PsiElement> {
@@ -217,10 +217,10 @@ private class PropertyClsStubBuilder(
override val receiverType: ProtoBuf.Type?
get() = propertyProto.receiverType(c.typeTable)
override val receiverAnnotations: List<ClassIdWithTarget>
override val receiverAnnotations: List<AnnotationWithTarget>
get() = c.components.annotationLoader
.loadExtensionReceiverParameterAnnotations(protoContainer, propertyProto, AnnotatedCallableKind.PROPERTY_GETTER)
.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.RECEIVER) }
.map { AnnotationWithTarget(it, AnnotationUseSiteTarget.RECEIVER) }
override val returnType: ProtoBuf.Type
get() = propertyProto.returnType(c.typeTable)
@@ -250,9 +250,9 @@ private class PropertyClsStubBuilder(
val delegateFieldAnnotations =
c.components.annotationLoader.loadPropertyDelegateFieldAnnotations(protoContainer, propertyProto)
val allAnnotations =
propertyAnnotations.map { ClassIdWithTarget(it, null) } +
backingFieldAnnotations.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.FIELD) } +
delegateFieldAnnotations.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) }
propertyAnnotations.map { AnnotationWithTarget(it, null) } +
backingFieldAnnotations.map { AnnotationWithTarget(it, AnnotationUseSiteTarget.FIELD) } +
delegateFieldAnnotations.map { AnnotationWithTarget(it, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) }
createTargetedAnnotationStubs(allAnnotations, modifierListStubImpl)
}
@@ -360,74 +360,11 @@ private class PropertyClsStubBuilder(
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? = null
override fun visitAnnotationMemberDefaultValue(): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
class AnnotationMemberDefaultValueVisitor : KotlinJvmBinaryClass.AnnotationArgumentVisitor {
private val args = mutableMapOf<Name, ConstantValue<*>>()
private fun nameOrSpecial(name: Name?): Name {
return name ?: Name.special("<no_name>")
}
override fun visit(name: Name?, value: Any?) {
args[nameOrSpecial(name)] = createConstantValue(value)
}
override fun visitClassLiteral(name: Name?, value: ClassLiteralValue) {
args[nameOrSpecial(name)] = createConstantValue(KClassData(value.classId, value.arrayNestedness))
}
override fun visitEnum(name: Name?, enumClassId: ClassId, enumEntryName: Name) {
args[nameOrSpecial(name)] = createConstantValue(EnumData(enumClassId, enumEntryName))
}
override fun visitAnnotation(
name: Name?,
classId: ClassId
): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
val visitor = AnnotationMemberDefaultValueVisitor()
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
args[nameOrSpecial(name)] = createConstantValue(AnnotationData(classId, visitor.args))
}
}
}
override fun visitArray(name: Name?): KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
return object : KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
private val elements = mutableListOf<Any>()
override fun visit(value: Any?) {
elements.addIfNotNull(value)
}
override fun visitEnum(enumClassId: ClassId, enumEntryName: Name) {
elements.add(EnumData(enumClassId, enumEntryName))
}
override fun visitClassLiteral(value: ClassLiteralValue) {
elements.add(KClassData(value.classId, value.arrayNestedness))
}
override fun visitAnnotation(classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
val visitor = AnnotationMemberDefaultValueVisitor()
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
elements.addIfNotNull(AnnotationData(classId, visitor.args))
}
}
}
override fun visitEnd() {
args[nameOrSpecial(name)] = createConstantValue(elements.toTypedArray())
}
}
}
return object : AnnotationMemberDefaultValueVisitor() {
override fun visitEnd() {
constantInitializer = args.values.firstOrNull()
}
}
return AnnotationMemberDefaultValueVisitor()
}
override fun visitAnnotation(
@@ -482,7 +419,7 @@ private class ConstructorClsStubBuilder(
override val receiverType: ProtoBuf.Type?
get() = null
override val receiverAnnotations: List<ClassIdWithTarget>
override val receiverAnnotations: List<AnnotationWithTarget>
get() = emptyList()
override val returnType: ProtoBuf.Type?
@@ -519,3 +456,69 @@ private class ConstructorClsStubBuilder(
KotlinConstructorStubImpl(parent, KtStubElementTypes.PRIMARY_CONSTRUCTOR, name, hasBody = false, isDelegatedCallToThis = false)
}
}
open class AnnotationMemberDefaultValueVisitor : KotlinJvmBinaryClass.AnnotationArgumentVisitor {
protected val args = mutableMapOf<Name, ConstantValue<*>>()
private fun nameOrSpecial(name: Name?): Name {
return name ?: Name.special("<no_name>")
}
override fun visit(name: Name?, value: Any?) {
val constantValue = createConstantValue(value)
args[nameOrSpecial(name)] = constantValue
}
override fun visitClassLiteral(name: Name?, value: ClassLiteralValue) {
args[nameOrSpecial(name)] = createConstantValue(KClassData(value.classId, value.arrayNestedness))
}
override fun visitEnum(name: Name?, enumClassId: ClassId, enumEntryName: Name) {
args[nameOrSpecial(name)] = createConstantValue(EnumData(enumClassId, enumEntryName))
}
override fun visitAnnotation(
name: Name?,
classId: ClassId
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val visitor = AnnotationMemberDefaultValueVisitor()
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
args[nameOrSpecial(name)] = createConstantValue(AnnotationData(classId, visitor.args))
}
}
}
override fun visitArray(name: Name?): KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor? {
return object : KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
private val elements = mutableListOf<Any>()
override fun visit(value: Any?) {
elements.addIfNotNull(value)
}
override fun visitEnum(enumClassId: ClassId, enumEntryName: Name) {
elements.add(EnumData(enumClassId, enumEntryName))
}
override fun visitClassLiteral(value: ClassLiteralValue) {
elements.add(KClassData(value.classId, value.arrayNestedness))
}
override fun visitAnnotation(classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
val visitor = AnnotationMemberDefaultValueVisitor()
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
elements.addIfNotNull(AnnotationData(classId, visitor.args))
}
}
}
override fun visitEnd() {
args[nameOrSpecial(name)] = createConstantValue(elements.toTypedArray())
}
}
}
override fun visitEnd() {}
}
@@ -18,11 +18,11 @@ import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
import org.jetbrains.kotlin.serialization.deserialization.getName
data class ClassIdWithTarget(val classId: ClassId, val target: AnnotationUseSiteTarget?)
data class AnnotationWithTarget(val annotationWithArgs: AnnotationWithArgs, val target: AnnotationUseSiteTarget?)
class ClsStubBuilderComponents(
val classDataFinder: ClassDataFinder,
val annotationLoader: AnnotationLoader<ClassId>,
val annotationLoader: AnnotationLoader<AnnotationWithArgs>,
val virtualFileForDebug: VirtualFile,
val serializationProtocol: SerializerExtensionProtocol,
val classFinder: KotlinClassFinder? = null,
@@ -37,7 +37,7 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
fun createTypeReferenceStub(
parent: StubElement<out PsiElement>,
type: Type,
additionalAnnotations: () -> List<ClassIdWithTarget> = { emptyList() }
additionalAnnotations: () -> List<AnnotationWithTarget> = { emptyList() }
) {
val abbreviatedType = type.abbreviatedType(c.typeTable)
if (abbreviatedType != null) {
@@ -47,11 +47,11 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
val typeReference = KotlinPlaceHolderStubImpl<KtTypeReference>(parent, KtStubElementTypes.TYPE_REFERENCE)
val annotations = c.components.annotationLoader.loadTypeAnnotations(type, c.nameResolver).filterNot {
val isTopLevelClass = !it.isNestedClass
isTopLevelClass && it.asSingleFqName() in ANNOTATIONS_NOT_LOADED_FOR_TYPES
val isTopLevelClass = !it.classId.isNestedClass
isTopLevelClass && it.classId.asSingleFqName() in ANNOTATIONS_NOT_LOADED_FOR_TYPES
}
val allAnnotations = additionalAnnotations() + annotations.map { ClassIdWithTarget(it, null) }
val allAnnotations = additionalAnnotations() + annotations.map { AnnotationWithTarget(it, null) }
when {
type.hasClassName() || type.hasTypeAliasName() ->
@@ -71,7 +71,7 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
else
parent
private fun createTypeParameterStub(parent: KotlinStubBaseImpl<*>, type: Type, name: Name, annotations: List<ClassIdWithTarget>) {
private fun createTypeParameterStub(parent: KotlinStubBaseImpl<*>, type: Type, name: Name, annotations: List<AnnotationWithTarget>) {
createTypeAnnotationStubs(parent, type, annotations)
val upperBoundType = if (type.hasFlexibleTypeCapabilitiesId()) {
createKotlinTypeBean(type.flexibleUpperBound(c.typeTable)!!)
@@ -95,7 +95,7 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
KotlinNameReferenceExpressionStubImpl(userType, StandardNames.FqNames.any.shortName().ref())
}
private fun createClassReferenceTypeStub(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List<ClassIdWithTarget>) {
private fun createClassReferenceTypeStub(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List<AnnotationWithTarget>) {
if (type.hasFlexibleTypeCapabilitiesId()) {
val id = c.nameResolver.getString(type.flexibleTypeCapabilitiesId)
@@ -113,11 +113,11 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
val shouldBuildAsFunctionType = isBuiltinFunctionClass(classId) && type.argumentList.none { it.projection == Projection.STAR }
if (shouldBuildAsFunctionType) {
val (extensionAnnotations, notExtensionAnnotations) = annotations.partition {
it.classId.asSingleFqName() == StandardNames.FqNames.extensionFunctionType
it.annotationWithArgs.classId.asSingleFqName() == StandardNames.FqNames.extensionFunctionType
}
val (contextReceiverAnnotations, otherAnnotations) = notExtensionAnnotations.partition {
it.classId.asSingleFqName() == StandardNames.FqNames.contextFunctionTypeParams
it.annotationWithArgs.classId.asSingleFqName() == StandardNames.FqNames.contextFunctionTypeParams
}
val isExtension = extensionAnnotations.isNotEmpty()
@@ -194,7 +194,7 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
} else lowerBound
}
private fun createTypeAnnotationStubs(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List<ClassIdWithTarget>) {
private fun createTypeAnnotationStubs(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List<AnnotationWithTarget>) {
val typeModifiers = getTypeModifiersAsWritten(type)
if (annotations.isEmpty() && typeModifiers.isEmpty()) return
val typeModifiersMask = ModifierMaskUtils.computeMask { it in typeModifiers }
@@ -192,22 +192,23 @@ fun createEmptyModifierListStub(parent: KotlinStubBaseImpl<*>): KotlinModifierLi
)
}
fun createAnnotationStubs(annotationIds: List<ClassId>, parent: KotlinStubBaseImpl<*>) {
return createTargetedAnnotationStubs(annotationIds.map { ClassIdWithTarget(it, null) }, parent)
fun createAnnotationStubs(annotations: List<AnnotationWithArgs>, parent: KotlinStubBaseImpl<*>) {
return createTargetedAnnotationStubs(annotations.map { AnnotationWithTarget(it, null) }, parent)
}
fun createTargetedAnnotationStubs(
annotationIds: List<ClassIdWithTarget>,
annotations: List<AnnotationWithTarget>,
parent: KotlinStubBaseImpl<*>
) {
if (annotationIds.isEmpty()) return
if (annotations.isEmpty()) return
annotationIds.forEach { annotation ->
val (annotationClassId, target) = annotation
annotations.forEach { annotation ->
val (annotationWithArgs, target) = annotation
val annotationEntryStubImpl = KotlinAnnotationEntryStubImpl(
parent,
shortName = annotationClassId.shortClassName.ref(),
hasValueArguments = false
shortName = annotationWithArgs.classId.shortClassName.ref(),
hasValueArguments = false,
annotationWithArgs.args
)
if (target != null) {
KotlinAnnotationUseSiteTargetStubImpl(annotationEntryStubImpl, StringRef.fromString(target.name)!!)
@@ -215,7 +216,7 @@ fun createTargetedAnnotationStubs(
val constructorCallee =
KotlinPlaceHolderStubImpl<KtConstructorCalleeExpression>(annotationEntryStubImpl, KtStubElementTypes.CONSTRUCTOR_CALLEE)
val typeReference = KotlinPlaceHolderStubImpl<KtTypeReference>(constructorCallee, KtStubElementTypes.TYPE_REFERENCE)
createStubForTypeName(annotationClassId, typeReference)
createStubForTypeName(annotationWithArgs.classId, typeReference)
}
}
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.metadata.deserialization.underlyingType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.stubs.impl.KotlinTypeAliasStubImpl
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.serialization.deserialization.getName
fun createTypeAliasStub(
@@ -42,7 +41,12 @@ fun createTypeAliasStub(
}
if (Flags.HAS_ANNOTATIONS.get(typeAliasProto.flags)) {
createAnnotationStubs(typeAliasProto.annotationList.map { c.nameResolver.getClassId(it.id) }, modifierList)
createAnnotationStubs(
typeAliasProto.annotationList.map {
c.components.annotationLoader.loadAnnotation(it, c.nameResolver)
},
modifierList
)
}
val typeAliasUnderlyingType = typeAliasProto.underlyingType(c.typeTable)