1 /* 2 * 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.google.turbine.binder.bound.TypeBoundClass 20 import com.google.turbine.model.Const 21 import com.google.turbine.model.Const.ArrayInitValue 22 import com.google.turbine.model.Const.Kind 23 import com.google.turbine.model.Const.Value 24 import com.google.turbine.model.TurbineConstantTypeKind 25 import com.google.turbine.tree.Tree 26 import com.google.turbine.tree.Tree.ArrayInit 27 import com.google.turbine.tree.Tree.ConstVarName 28 import com.google.turbine.tree.Tree.Expression 29 30 /** 31 * A representation of a value in Turbine. 32 * 33 * A value could be: 34 * * A literal constant, e.g. `"string"`, or `3.4`. 35 * * An enum constant, e.g. `RetentionPolicy.CLASS`. 36 * * A class literal. 37 * * A constant field. 38 * * An array of one of the above types. 39 * 40 * They can be used as constant field values and annotation attribute values, including default 41 * values. 42 * 43 * It consists of two parts. 44 * * [const] - this is the constant value and has been evaluated by Turbine. 45 * * [expr] - the optional source representation of the value. This is `null` when the value is 46 * obtained from a binary file, e.g. the value of an annotation attribute of an annotation on a 47 * class loaded from the class path. 48 * 49 * The model needs information from both so this encapsulates them together to make them easier to 50 * use and provide a convenient place for code that manipulate them. 51 */ 52 internal class TurbineValue( 53 /** The constant object representing the annotation value. */ 54 val const: Const, 55 56 /** An optional [Expression] that might provide additional context for value extraction. */ 57 val expr: Expression?, 58 59 /** If available, then can be used to resolve [ConstVarName] to [TypeBoundClass.FieldInfo]. */ 60 val fieldResolver: TurbineFieldResolver? = null, 61 ) { 62 /** 63 * Get the source representation of this value suitable for use when writing a method's default 64 * value. 65 */ getSourceForMethodDefaultnull66 fun getSourceForMethodDefault(): String { 67 // Check for field references first. 68 if (expr != null) { 69 when (expr.kind()) { 70 Tree.Kind.CONST_VAR_NAME -> { 71 // If the const is an enum then use that as it will be fully qualified but the 72 // ConstVarName will not. 73 if (const.kind() != Kind.ENUM_CONSTANT && fieldResolver != null) { 74 expr as ConstVarName 75 val fieldInfo = fieldResolver.resolveField(expr) 76 val fieldSymbol = fieldInfo?.sym() 77 if (fieldSymbol != null) { 78 return "${fieldSymbol.owner().qualifiedName}.${fieldSymbol.name()}" 79 } 80 } 81 } 82 // Fall back to using the const. 83 else -> {} 84 } 85 } 86 87 return when (const.kind()) { 88 Kind.PRIMITIVE -> { 89 when ((const as Value).constantTypeKind()) { 90 TurbineConstantTypeKind.FLOAT -> { 91 val value = (const as Const.FloatValue).value() 92 when { 93 value == Float.POSITIVE_INFINITY -> "java.lang.Float.POSITIVE_INFINITY" 94 value == Float.NEGATIVE_INFINITY -> "java.lang.Float.NEGATIVE_INFINITY" 95 else -> value.toString() + "f" 96 } 97 } 98 TurbineConstantTypeKind.DOUBLE -> { 99 val value = (const as Const.DoubleValue).value() 100 when { 101 value == Double.POSITIVE_INFINITY -> 102 "java.lang.Double.POSITIVE_INFINITY" 103 value == Double.NEGATIVE_INFINITY -> 104 "java.lang.Double.NEGATIVE_INFINITY" 105 else -> const.toString() 106 } 107 } 108 TurbineConstantTypeKind.BYTE -> const.getValue().toString() 109 else -> const.toString() 110 } 111 } 112 Kind.ARRAY -> { 113 const as ArrayInitValue 114 // This is case where defined type is array type but default value is 115 // single non-array element 116 // For e.g. char[] letter() default 'a'; 117 if (const.elements().count() == 1 && expr != null && expr !is ArrayInit) { 118 TurbineValue(const.elements().single(), expr).getSourceForMethodDefault() 119 } else const.underlyingValue.toString() 120 } 121 Kind.CLASS_LITERAL -> "${const.underlyingValue}.class" 122 else -> const.underlyingValue.toString() 123 } 124 } 125 126 /** 127 * Get the source representation of this value suitable for use when writing an annotation 128 * attribute's value. 129 */ getSourceForAnnotationValuenull130 fun getSourceForAnnotationValue(): String { 131 return when (const.kind()) { 132 Kind.PRIMITIVE -> { 133 when ((const as Value).constantTypeKind()) { 134 TurbineConstantTypeKind.INT -> { 135 val value = (const as Const.IntValue).value() 136 if (value < 0 || (expr != null && expr.kind() == Tree.Kind.TYPE_CAST)) 137 "0x" + value.toUInt().toString(16) // Hex Value 138 else value.toString() 139 } 140 TurbineConstantTypeKind.SHORT -> { 141 val value = (const as Const.ShortValue).value() 142 if (value < 0) "0x" + value.toUInt().toString(16) else value.toString() 143 } 144 TurbineConstantTypeKind.FLOAT -> { 145 val value = (const as Const.FloatValue).value() 146 when { 147 value == Float.POSITIVE_INFINITY -> "java.lang.Float.POSITIVE_INFINITY" 148 value == Float.NEGATIVE_INFINITY -> "java.lang.Float.NEGATIVE_INFINITY" 149 value < 0 -> value.toString() + "F" // Handling negative values 150 else -> value.toString() + "f" // Handling positive values 151 } 152 } 153 TurbineConstantTypeKind.DOUBLE -> { 154 val value = (const as Const.DoubleValue).value() 155 when { 156 value == Double.POSITIVE_INFINITY -> 157 "java.lang.Double.POSITIVE_INFINITY" 158 value == Double.NEGATIVE_INFINITY -> 159 "java.lang.Double.NEGATIVE_INFINITY" 160 else -> const.toString() 161 } 162 } 163 TurbineConstantTypeKind.BYTE -> const.getValue().toString() 164 else -> const.toString() 165 } 166 } 167 Kind.ARRAY -> { 168 const as ArrayInitValue 169 val values = 170 if (expr != null) 171 const.elements().zip((expr as ArrayInit).exprs(), ::TurbineValue) 172 else const.elements().map { TurbineValue(it, null) } 173 values.joinToString(prefix = "{", postfix = "}") { 174 it.getSourceForAnnotationValue() 175 } 176 } 177 Kind.ENUM_CONSTANT -> const.underlyingValue.toString() 178 Kind.CLASS_LITERAL -> { 179 expr?.toString() ?: "${const.underlyingValue}.class" 180 } 181 else -> const.toString() 182 } 183 } 184 } 185