1 /* <lambda>null2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.model.turbine 18 19 import com.android.tools.metalava.model.ANNOTATION_ATTR_VALUE 20 import com.android.tools.metalava.model.AnnotationAttribute 21 import com.android.tools.metalava.model.AnnotationAttributeValue 22 import com.android.tools.metalava.model.AnnotationItem 23 import com.android.tools.metalava.model.Codebase 24 import com.android.tools.metalava.model.DefaultAnnotationArrayAttributeValue 25 import com.android.tools.metalava.model.DefaultAnnotationAttribute 26 import com.android.tools.metalava.model.DefaultAnnotationItem 27 import com.android.tools.metalava.model.DefaultAnnotationSingleAttributeValue 28 import com.android.tools.metalava.reporter.FileLocation 29 import com.google.common.collect.ImmutableList 30 import com.google.common.collect.ImmutableMap 31 import com.google.turbine.model.Const 32 import com.google.turbine.model.Const.ArrayInitValue 33 import com.google.turbine.model.Const.Kind 34 import com.google.turbine.tree.Tree 35 import com.google.turbine.tree.Tree.ArrayInit 36 import com.google.turbine.tree.Tree.Assign 37 import com.google.turbine.tree.Tree.Expression 38 import com.google.turbine.tree.Tree.Literal 39 import com.google.turbine.type.AnnoInfo 40 41 /** 42 * Factory for creating [AnnotationItem]s from [AnnoInfo]s. 43 * 44 * @param codebase the [Codebase] to which the [AnnotationItem] will belong. 45 * @param sourceFileCache provides mapping from [AnnoInfo.source] to location. 46 */ 47 internal class TurbineAnnotationFactory( 48 private val codebase: Codebase, 49 private val sourceFileCache: TurbineSourceFileCache, 50 ) { 51 /** Creates a list of AnnotationItems from given list of Turbine Annotations */ 52 internal fun createAnnotations(annotations: List<AnnoInfo>): List<AnnotationItem> { 53 return annotations.mapNotNull { createAnnotation(it) } 54 } 55 56 /** Create an [AnnotationItem] from an [AnnoInfo]. */ 57 private fun createAnnotation(annotation: AnnoInfo): AnnotationItem? { 58 // Get the source representation of the annotation. This will be null for an annotation 59 // loaded from a class file. 60 val tree: Tree.Anno? = annotation.tree() 61 // An annotation that has no definition in scope has a null sym, in that case fall back 62 // to use the name used in the source. The sym can only be null in sources, so if sym is 63 // null then tree cannot be null. 64 val qualifiedName = annotation.sym()?.qualifiedName ?: tree!!.name().dotSeparatedName 65 66 val fileLocation = 67 annotation 68 .source() 69 ?.let { sourceFile -> sourceFileCache.turbineSourceFile(sourceFile) } 70 ?.let { sourceFile -> TurbineFileLocation.forTree(sourceFile, tree) } 71 ?: FileLocation.UNKNOWN 72 73 return DefaultAnnotationItem.create(codebase, fileLocation, qualifiedName) { 74 getAnnotationAttributes(annotation.values(), tree?.args()) 75 } 76 } 77 78 /** Creates a list of AnnotationAttribute from the map of name-value attribute pairs */ 79 private fun getAnnotationAttributes( 80 attrs: ImmutableMap<String, Const>, 81 exprs: ImmutableList<Expression>? 82 ): List<AnnotationAttribute> { 83 val attributes = mutableListOf<AnnotationAttribute>() 84 if (exprs != null) { 85 for (exp in exprs) { 86 when (exp.kind()) { 87 Tree.Kind.ASSIGN -> { 88 exp as Assign 89 val name = exp.name().value() 90 val assignExp = exp.expr() 91 attributes.add( 92 DefaultAnnotationAttribute( 93 name, 94 createAttrValue(attrs[name]!!, assignExp) 95 ) 96 ) 97 } 98 else -> { 99 val name = ANNOTATION_ATTR_VALUE 100 val value = 101 attrs[name] 102 ?: (exp as? Literal)?.value() 103 ?: error( 104 "Cannot find value for default 'value' attribute from $exp" 105 ) 106 attributes.add( 107 DefaultAnnotationAttribute(name, createAttrValue(value, exp)) 108 ) 109 } 110 } 111 } 112 } else { 113 for ((name, value) in attrs) { 114 attributes.add(DefaultAnnotationAttribute(name, createAttrValue(value, null))) 115 } 116 } 117 return attributes 118 } 119 120 private fun createAttrValue(const: Const, expr: Expression?): AnnotationAttributeValue { 121 if (const.kind() == Kind.ARRAY) { 122 const as ArrayInitValue 123 if (const.elements().count() == 1 && expr != null && expr !is ArrayInit) { 124 // This is case where defined type is array type but provided attribute value is 125 // single non-array element 126 // For e.g. @Anno(5) where Anno is @interface Anno {int [] value()} 127 val constLiteral = const.elements().single() 128 return DefaultAnnotationSingleAttributeValue( 129 { TurbineValue(constLiteral, expr).getSourceForAnnotationValue() }, 130 { constLiteral.underlyingValue } 131 ) 132 } 133 return DefaultAnnotationArrayAttributeValue( 134 { TurbineValue(const, expr).getSourceForAnnotationValue() }, 135 { const.elements().map { createAttrValue(it, null) } } 136 ) 137 } 138 return DefaultAnnotationSingleAttributeValue( 139 { TurbineValue(const, expr).getSourceForAnnotationValue() }, 140 { const.underlyingValue } 141 ) 142 } 143 } 144