• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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