1 /*
<lambda>null2  * Copyright (C) 2024 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.ClassTypeItem
20 import com.android.tools.metalava.model.PrimitiveTypeItem
21 import com.android.tools.metalava.model.ReferenceTypeItem
22 import com.android.tools.metalava.model.TypeArgumentTypeItem
23 import com.android.tools.metalava.model.TypeItem
24 import com.android.tools.metalava.model.TypeModifiers
25 import com.android.tools.metalava.model.TypeNullability
26 import com.android.tools.metalava.model.TypeParameterScope
27 import com.android.tools.metalava.model.type.ContextNullability
28 import com.android.tools.metalava.model.type.DefaultArrayTypeItem
29 import com.android.tools.metalava.model.type.DefaultClassTypeItem
30 import com.android.tools.metalava.model.type.DefaultPrimitiveTypeItem
31 import com.android.tools.metalava.model.type.DefaultTypeItemFactory
32 import com.android.tools.metalava.model.type.DefaultTypeModifiers
33 import com.android.tools.metalava.model.type.DefaultVariableTypeItem
34 import com.android.tools.metalava.model.type.DefaultWildcardTypeItem
35 import com.google.turbine.model.TurbineConstantTypeKind
36 import com.google.turbine.type.AnnoInfo
37 import com.google.turbine.type.Type
38 import javax.lang.model.element.Element
39 import javax.lang.model.element.TypeElement
40 import javax.lang.model.type.TypeKind
41 
42 /** Creates [TypeItem]s from [Type]s. */
43 internal class TurbineTypeItemFactory(
44     private val codebase: TurbineBasedCodebase,
45     private val initializer: TurbineCodebaseInitialiser,
46     typeParameterScope: TypeParameterScope,
47 ) : DefaultTypeItemFactory<Type, TurbineTypeItemFactory>(typeParameterScope) {
48 
49     override fun self() = this
50 
51     override fun createNestedFactory(scope: TypeParameterScope) =
52         TurbineTypeItemFactory(codebase, initializer, scope)
53 
54     override fun getType(
55         underlyingType: Type,
56         contextNullability: ContextNullability,
57         isVarArg: Boolean,
58     ) = createType(underlyingType, isVarArg, contextNullability)
59 
60     private fun createModifiers(
61         annos: List<AnnoInfo>,
62         contextNullability: ContextNullability,
63     ): TypeModifiers {
64         val typeAnnotations = initializer.createAnnotations(annos)
65         // Compute the nullability, factoring in any context nullability and type annotations.
66         // Turbine does not support kotlin so the kotlin nullability is always null.
67         val nullability = contextNullability.compute(null, typeAnnotations)
68         return DefaultTypeModifiers.create(typeAnnotations.toMutableList(), nullability)
69     }
70 
71     internal fun createType(
72         type: Type,
73         isVarArg: Boolean,
74         contextNullability: ContextNullability = ContextNullability.none,
75     ): TypeItem {
76         return when (val kind = type.tyKind()) {
77             Type.TyKind.PRIM_TY -> {
78                 type as Type.PrimTy
79                 // Primitives are always non-null.
80                 val modifiers = createModifiers(type.annos(), ContextNullability.forceNonNull)
81                 when (type.primkind()) {
82                     TurbineConstantTypeKind.BOOLEAN ->
83                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.BOOLEAN)
84                     TurbineConstantTypeKind.BYTE ->
85                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.BYTE)
86                     TurbineConstantTypeKind.CHAR ->
87                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.CHAR)
88                     TurbineConstantTypeKind.DOUBLE ->
89                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.DOUBLE)
90                     TurbineConstantTypeKind.FLOAT ->
91                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.FLOAT)
92                     TurbineConstantTypeKind.INT ->
93                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.INT)
94                     TurbineConstantTypeKind.LONG ->
95                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.LONG)
96                     TurbineConstantTypeKind.SHORT ->
97                         DefaultPrimitiveTypeItem(modifiers, PrimitiveTypeItem.Primitive.SHORT)
98                     else ->
99                         throw IllegalStateException("Invalid primitive type in API surface: $type")
100                 }
101             }
102             Type.TyKind.ARRAY_TY -> {
103                 createArrayType(type as Type.ArrayTy, isVarArg, contextNullability)
104             }
105             Type.TyKind.CLASS_TY -> {
106                 type as Type.ClassTy
107                 var outerClass: ClassTypeItem? = null
108                 // A ClassTy is represented by list of SimpleClassTY each representing an inner
109                 // class. e.g. , Outer.Inner.Inner1 will be represented by three simple classes
110                 // Outer, Outer.Inner and Outer.Inner.Inner1
111                 val iterator = type.classes().iterator()
112                 while (iterator.hasNext()) {
113                     val simpleClass = iterator.next()
114 
115                     // Select the ContextNullability. If there is another SimpleClassTy after this
116                     // then this is an outer class which can never be null, so force it to be
117                     // non-null. Otherwise, this is the inner class so use the supplied
118                     // ContextNullability.
119                     val actualContextNullability =
120                         if (iterator.hasNext()) {
121                             // For all outer class types, set the nullability to non-null.
122                             ContextNullability.forceNonNull
123                         } else {
124                             // Use the supplied ContextNullability.
125                             contextNullability
126                         }
127 
128                     outerClass =
129                         createInnerClassType(simpleClass, outerClass, actualContextNullability)
130                 }
131                 outerClass!!
132             }
133             Type.TyKind.TY_VAR -> {
134                 type as Type.TyVar
135                 val modifiers = createModifiers(type.annos(), contextNullability)
136                 val typeParameter = typeParameterScope.getTypeParameter(type.sym().name())
137                 DefaultVariableTypeItem(modifiers, typeParameter)
138             }
139             Type.TyKind.WILD_TY -> {
140                 type as Type.WildTy
141                 // Wildcards themselves don't have a defined nullability.
142                 val modifiers =
143                     createModifiers(type.annotations(), ContextNullability.forceUndefined)
144                 when (type.boundKind()) {
145                     Type.WildTy.BoundKind.UPPER -> {
146                         val upperBound = createWildcardBound(type.bound())
147                         DefaultWildcardTypeItem(modifiers, upperBound, null)
148                     }
149                     Type.WildTy.BoundKind.LOWER -> {
150                         // LowerBounded types have java.lang.Object as upper bound
151                         val upperBound = createWildcardBound(Type.ClassTy.OBJECT)
152                         val lowerBound = createWildcardBound(type.bound())
153                         DefaultWildcardTypeItem(modifiers, upperBound, lowerBound)
154                     }
155                     Type.WildTy.BoundKind.NONE -> {
156                         // Unbounded types have java.lang.Object as upper bound
157                         val upperBound = createWildcardBound(Type.ClassTy.OBJECT)
158                         DefaultWildcardTypeItem(modifiers, upperBound, null)
159                     }
160                     else ->
161                         throw IllegalStateException("Invalid wildcard type in API surface: $type")
162                 }
163             }
164             Type.TyKind.VOID_TY ->
165                 DefaultPrimitiveTypeItem(
166                     // Primitives are always non-null.
167                     createModifiers(emptyList(), ContextNullability.forceNonNull),
168                     PrimitiveTypeItem.Primitive.VOID
169                 )
170             Type.TyKind.NONE_TY ->
171                 DefaultPrimitiveTypeItem(
172                     // Primitives are always non-null.
173                     DefaultTypeModifiers.create(emptyList(), TypeNullability.NONNULL),
174                     PrimitiveTypeItem.Primitive.VOID
175                 )
176             Type.TyKind.ERROR_TY -> {
177                 // This is case of unresolved superclass or implemented interface
178                 type as Type.ErrorTy
179                 DefaultClassTypeItem(
180                     codebase,
181                     DefaultTypeModifiers.create(emptyList(), TypeNullability.UNDEFINED),
182                     type.name(),
183                     emptyList(),
184                     null,
185                 )
186             }
187             else -> throw IllegalStateException("Invalid type in API surface: $kind")
188         }
189     }
190 
191     private fun createWildcardBound(type: Type) = getGeneralType(type) as ReferenceTypeItem
192 
193     private fun createArrayType(
194         type: Type.ArrayTy,
195         isVarArg: Boolean,
196         contextNullability: ContextNullability,
197     ): TypeItem {
198         // For Turbine's ArrayTy, due to a bug in Turbine, the annotations for multidimensional
199         // arrays are in the wrong order so this works around the issue.
200 
201         // First, traverse from the outermost array to the innermost component type and add the
202         // [AnnoInfo]s to the list. Ending up with the innermost component type. Due to the bug the
203         // list contains [AnnoInfo]s from the innermost component type to the outermost types.
204         val annosList = mutableListOf<List<AnnoInfo>>()
205         var curr: Type = type
206         while (curr.tyKind() == Type.TyKind.ARRAY_TY) {
207             curr as Type.ArrayTy
208             annosList.add(curr.annos())
209             curr = curr.elementType()
210         }
211 
212         // Then, get the type for the innermost component, it has the correct annotations. Pass
213         // in the [ContextNullability.forComponentType] just in case this is the return type of an
214         // annotation method, or in other words the type of an annotation attribute.
215         val componentType = getType(curr, contextNullability.forComponentType())
216 
217         // Finally, traverse over the annotations from the innermost component type to the outermost
218         // array and construct a [DefaultArrayTypeItem] around the inner component type using its
219         // `List<AnnoInfo>`. The last `List<AnnoInfo>` is for the outermost array, and it needs to
220         // be tagged with the [isVarArg] value and [contextNullability].
221         val lastIndex = annosList.size - 1
222         return annosList.foldIndexed(componentType) { index, typeItem, annos ->
223             val (arrayContextNullability, arrayVarArg) =
224                 if (index == lastIndex) {
225                     // Outermost array. Should be called with correct value of isVarArg and
226                     // the contextual nullability.
227                     Pair(contextNullability, isVarArg)
228                 } else {
229                     Pair(ContextNullability.none, false)
230                 }
231 
232             val modifiers = createModifiers(annos, arrayContextNullability)
233             DefaultArrayTypeItem(modifiers, typeItem, arrayVarArg)
234         }
235     }
236 
237     /**
238      * Retrieves the `ClassTypeItem` representation of the outer class associated with a given
239      * nested class type. Intended for types that are not explicitly mentioned within the source
240      * code.
241      *
242      * @param type The `Type.ClassTy.SimpleClassTy` object representing the nested class.
243      * @return The `ClassTypeItem` representing the outer class.
244      */
245     private fun getOuterClassType(type: Type.ClassTy.SimpleClassTy): ClassTypeItem {
246         val className = initializer.getQualifiedName(type.sym().binaryName())
247         val classTypeElement = initializer.getTypeElement(className)!!
248         return createOuterClassType(classTypeElement.enclosingElement!!)!!
249     }
250 
251     /**
252      * Constructs a `ClassTypeItem` representation from a type element. Intended for types that are
253      * not explicitly mentioned within the source code.
254      *
255      * @param element The `Element` object representing the type.
256      * @return The corresponding `ClassTypeItem`, or null if the `element` does not represent a
257      *   declared type.
258      */
259     private fun createOuterClassType(element: Element): ClassTypeItem? {
260         if (element.asType().kind != TypeKind.DECLARED) return null
261 
262         val outerClassElement = element.enclosingElement!!
263         val outerClassTypeItem = createOuterClassType(outerClassElement)
264 
265         element as TypeElement
266 
267         // Since this type was never part of source , it won't have any annotation or arguments
268         val modifiers = DefaultTypeModifiers.create(emptyList(), TypeNullability.NONNULL)
269         val classTypeItem =
270             DefaultClassTypeItem(
271                 codebase,
272                 modifiers,
273                 element.qualifiedName.toString(), // Assuming qualifiedName is available on element
274                 emptyList(),
275                 outerClassTypeItem
276             )
277         return classTypeItem
278     }
279 
280     private fun createInnerClassType(
281         type: Type.ClassTy.SimpleClassTy,
282         outerClass: ClassTypeItem?,
283         contextNullability: ContextNullability,
284     ): ClassTypeItem {
285         val outerClassItem =
286             if (type.sym().binaryName().contains("$") && outerClass == null) {
287                 getOuterClassType(type)
288             } else {
289                 outerClass
290             }
291 
292         val modifiers = createModifiers(type.annos(), contextNullability)
293         val qualifiedName = initializer.getQualifiedName(type.sym().binaryName())
294         val parameters = type.targs().map { getGeneralType(it) as TypeArgumentTypeItem }
295         return DefaultClassTypeItem(codebase, modifiers, qualifiedName, parameters, outerClassItem)
296     }
297 }
298