1 /*
<lambda>null2  * Copyright (C) 2023 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.ArrayTypeItem
24 import com.android.tools.metalava.model.BoundsTypeItem
25 import com.android.tools.metalava.model.ClassItem
26 import com.android.tools.metalava.model.ClassKind
27 import com.android.tools.metalava.model.ClassTypeItem
28 import com.android.tools.metalava.model.DefaultAnnotationArrayAttributeValue
29 import com.android.tools.metalava.model.DefaultAnnotationAttribute
30 import com.android.tools.metalava.model.DefaultAnnotationItem
31 import com.android.tools.metalava.model.DefaultAnnotationSingleAttributeValue
32 import com.android.tools.metalava.model.DefaultTypeParameterList
33 import com.android.tools.metalava.model.ExceptionTypeItem
34 import com.android.tools.metalava.model.MethodItem
35 import com.android.tools.metalava.model.TypeItem
36 import com.android.tools.metalava.model.TypeParameterList
37 import com.android.tools.metalava.model.TypeParameterScope
38 import com.android.tools.metalava.model.type.MethodFingerprint
39 import com.android.tools.metalava.reporter.FileLocation
40 import com.google.common.collect.ImmutableList
41 import com.google.common.collect.ImmutableMap
42 import com.google.turbine.binder.Binder
43 import com.google.turbine.binder.Binder.BindingResult
44 import com.google.turbine.binder.ClassPathBinder
45 import com.google.turbine.binder.Processing.ProcessorInfo
46 import com.google.turbine.binder.bound.EnumConstantValue
47 import com.google.turbine.binder.bound.SourceTypeBoundClass
48 import com.google.turbine.binder.bound.TurbineClassValue
49 import com.google.turbine.binder.bound.TypeBoundClass
50 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo
51 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo
52 import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo
53 import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo
54 import com.google.turbine.binder.bytecode.BytecodeBoundClass
55 import com.google.turbine.binder.env.CompoundEnv
56 import com.google.turbine.binder.env.SimpleEnv
57 import com.google.turbine.binder.lookup.LookupKey
58 import com.google.turbine.binder.lookup.TopLevelIndex
59 import com.google.turbine.binder.sym.ClassSymbol
60 import com.google.turbine.binder.sym.TyVarSymbol
61 import com.google.turbine.diag.TurbineLog
62 import com.google.turbine.model.Const
63 import com.google.turbine.model.Const.ArrayInitValue
64 import com.google.turbine.model.Const.Kind
65 import com.google.turbine.model.Const.Value
66 import com.google.turbine.model.TurbineConstantTypeKind as PrimKind
67 import com.google.turbine.model.TurbineFlag
68 import com.google.turbine.model.TurbineTyKind
69 import com.google.turbine.processing.ModelFactory
70 import com.google.turbine.processing.TurbineElements
71 import com.google.turbine.processing.TurbineTypes
72 import com.google.turbine.tree.Tree
73 import com.google.turbine.tree.Tree.ArrayInit
74 import com.google.turbine.tree.Tree.Assign
75 import com.google.turbine.tree.Tree.CompUnit
76 import com.google.turbine.tree.Tree.Expression
77 import com.google.turbine.tree.Tree.Ident
78 import com.google.turbine.tree.Tree.Literal
79 import com.google.turbine.tree.Tree.MethDecl
80 import com.google.turbine.tree.Tree.TyDecl
81 import com.google.turbine.type.AnnoInfo
82 import com.google.turbine.type.Type
83 import java.io.File
84 import java.util.Optional
85 import javax.lang.model.SourceVersion
86 import javax.lang.model.element.TypeElement
87 
88 /**
89  * This initializer acts as an adapter between codebase and the output from Turbine parser.
90  *
91  * This is used for populating all the classes,packages and other items from the data present in the
92  * parsed Tree
93  */
94 internal open class TurbineCodebaseInitialiser(
95     val units: List<CompUnit>,
96     val codebase: TurbineBasedCodebase,
97     val classpath: List<File>,
98 ) {
99     /** The output from Turbine Binder */
100     private lateinit var bindingResult: BindingResult
101 
102     /** Map between ClassSymbols and TurbineClass for classes present in source */
103     private lateinit var sourceClassMap: ImmutableMap<ClassSymbol, SourceTypeBoundClass>
104 
105     /** Map between ClassSymbols and TurbineClass for classes present in classPath */
106     private lateinit var envClassMap: CompoundEnv<ClassSymbol, BytecodeBoundClass>
107 
108     private lateinit var index: TopLevelIndex
109 
110     /** Map between Class declaration and the corresponding source CompUnit */
111     private val classSourceMap: MutableMap<TyDecl, CompUnit> = mutableMapOf<TyDecl, CompUnit>()
112 
113     private val globalTypeItemFactory =
114         TurbineTypeItemFactory(codebase, this, TypeParameterScope.empty)
115 
116     /**
117      * Data Type: TurbineElements (An implementation of javax.lang.model.util.Elements)
118      *
119      * Usage: Enables lookup of TypeElement objects by name.
120      */
121     private lateinit var turbineElements: TurbineElements
122 
123     /**
124      * Binds the units with the help of Turbine's binder.
125      *
126      * Then creates the packages, classes and their members, as well as sets up various class
127      * hierarchies using the binder's output
128      */
129     fun initialize() {
130         // Bind the units
131         try {
132             val procInfo =
133                 ProcessorInfo.create(
134                     ImmutableList.of(),
135                     null,
136                     ImmutableMap.of(),
137                     SourceVersion.latest()
138                 )
139 
140             // Any non-fatal error (like unresolved symbols) will be captured in this log and will
141             // be ignored.
142             val log = TurbineLog()
143 
144             bindingResult =
145                 Binder.bind(
146                     log,
147                     ImmutableList.copyOf(units),
148                     ClassPathBinder.bindClasspath(classpath.map { it.toPath() }),
149                     procInfo,
150                     ClassPathBinder.bindClasspath(listOf()),
151                     Optional.empty()
152                 )!!
153             sourceClassMap = bindingResult.units()
154             envClassMap = bindingResult.classPathEnv()
155             index = bindingResult.tli()
156         } catch (e: Throwable) {
157             throw e
158         }
159         // maps class symbols to their source-based definitions
160         val sourceEnv = SimpleEnv<ClassSymbol, SourceTypeBoundClass>(sourceClassMap)
161         // maps class symbols to their classpath-based definitions
162         val classpathEnv: CompoundEnv<ClassSymbol, TypeBoundClass> = CompoundEnv.of(envClassMap)
163         // provides a unified view of both source and classpath classes
164         val combinedEnv = classpathEnv.append(sourceEnv)
165 
166         // used to create language model elements for code analysis
167         val factory = ModelFactory(combinedEnv, ClassLoader.getSystemClassLoader(), index)
168         // provides type-related operations within the Turbine compiler context
169         val turbineTypes = TurbineTypes(factory)
170         // provides access to code elements (packages, types, members) for analysis.
171         turbineElements = TurbineElements(factory, turbineTypes)
172 
173         createAllPackages()
174         createAllClasses()
175     }
176 
177     private fun createAllPackages() {
178         // Root package
179         findOrCreatePackage("", null, "")
180 
181         for (unit in units) {
182             var doc = ""
183             var sourceFile: TurbineSourceFile? = null
184             // No class declarations. Will be a case of package-info file
185             if (unit.decls().isEmpty()) {
186                 val source = unit.source().source()
187                 sourceFile = TurbineSourceFile(codebase, unit)
188                 doc = getHeaderComments(source)
189             }
190             findOrCreatePackage(getPackageName(unit), sourceFile, doc)
191             unit.decls().forEach { decl -> classSourceMap.put(decl, unit) }
192         }
193     }
194 
195     /**
196      * Searches for the package with supplied name in the codebase's package map and if not found
197      * creates the corresponding TurbinePackageItem and adds it to the package map.
198      */
199     private fun findOrCreatePackage(
200         name: String,
201         sourceFile: TurbineSourceFile?,
202         document: String
203     ): TurbinePackageItem {
204         val pkgItem = codebase.findPackage(name)
205         if (pkgItem != null) {
206             val turbinePkgItem = pkgItem as TurbinePackageItem
207             // Update originallyHidden status based on the documentation.
208             if (document.isNotEmpty()) {
209                 turbinePkgItem.updateOriginallyHiddenStatus(document)
210             }
211             // The hidden status will be updated automatically based on originallyHidden
212             return turbinePkgItem
213         } else {
214             val modifiers = TurbineModifierItem.create(codebase, 0, null, false)
215             val fileLocation = TurbineFileLocation.forTree(sourceFile)
216             val turbinePkgItem =
217                 TurbinePackageItem.create(codebase, fileLocation, name, modifiers, document)
218             codebase.addPackage(turbinePkgItem)
219             return turbinePkgItem
220         }
221     }
222 
223     private fun createAllClasses() {
224         for ((classSymbol, sourceBoundClass) in sourceClassMap) {
225 
226             // Turbine considers package-info as class and creates one for empty packages which is
227             // not consistent with Psi
228             if (classSymbol.simpleName() == "package-info") {
229                 continue
230             }
231 
232             // Ignore inner classes, they will be created when the outer class is created.
233             if (sourceBoundClass.owner() != null) {
234                 continue
235             }
236 
237             createTopLevelClassAndContents(classSymbol)
238         }
239 
240         codebase.resolveSuperTypes()
241     }
242 
243     val ClassSymbol.isTopClass
244         get() = !binaryName().contains('$')
245 
246     /**
247      * Create top level classes, their inner classes and all the other members.
248      *
249      * All the classes are registered by name and so can be found by [findOrCreateClass].
250      */
251     private fun createTopLevelClassAndContents(classSymbol: ClassSymbol) {
252         if (!classSymbol.isTopClass) error("$classSymbol is not a top level class")
253         createClass(classSymbol, null, globalTypeItemFactory)
254     }
255 
256     /** Tries to create a class if not already present in codebase's classmap */
257     internal fun findOrCreateClass(name: String): TurbineClassItem? {
258         var classItem = codebase.findClass(name)
259 
260         if (classItem == null) {
261             // This will get the symbol for the top class even if the class name is for an inner
262             // class.
263             val topClassSym = getClassSymbol(name)
264 
265             // Create the top level class, if needed, along with any inner classes and register them
266             // all by name.
267             topClassSym?.let {
268                 // It is possible that the top level class has already been created but just did not
269                 // contain the requested inner class so check to make sure it exists before creating
270                 // it.
271                 val topClassName = getQualifiedName(topClassSym.binaryName())
272                 codebase.findClass(topClassName)
273                     ?: let {
274                         // Create tand register he top level class and its inner classes.
275                         createTopLevelClassAndContents(topClassSym)
276 
277                         // Now try and find the actual class that was requested by name. If it
278                         // exists it
279                         // should have been created in the previous call.
280                         classItem = codebase.findClass(name)
281                     }
282             }
283         }
284 
285         return classItem
286     }
287 
288     private fun createClass(
289         sym: ClassSymbol,
290         containingClassItem: TurbineClassItem?,
291         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
292     ): TurbineClassItem {
293 
294         var cls: TypeBoundClass? = sourceClassMap[sym]
295         cls = if (cls != null) cls else envClassMap.get(sym)!!
296         val decl = (cls as? SourceTypeBoundClass)?.decl()
297 
298         val isTopClass = cls.owner() == null
299         val isFromClassPath = !(cls is SourceTypeBoundClass)
300 
301         // Get the package item
302         val pkgName = sym.packageName().replace('/', '.')
303         val pkgItem = findOrCreatePackage(pkgName, null, "")
304 
305         // Create class
306         val qualifiedName = getQualifiedName(sym.binaryName())
307         val simpleName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)
308         val fullName = sym.simpleName().replace('$', '.')
309         val annotations = createAnnotations(cls.annotations())
310         val documentation = javadoc(decl)
311         val modifierItem =
312             TurbineModifierItem.create(
313                 codebase,
314                 cls.access(),
315                 annotations,
316                 isDeprecated(documentation)
317             )
318         val (typeParameters, classTypeItemFactory) =
319             createTypeParameters(
320                 cls.typeParameterTypes(),
321                 enclosingClassTypeItemFactory,
322                 "class $qualifiedName",
323             )
324         // Create the sourcefile
325         val sourceFile =
326             if (isTopClass && !isFromClassPath) {
327                 classSourceMap[(cls as SourceTypeBoundClass).decl()]?.let {
328                     TurbineSourceFile(codebase, it)
329                 }
330             } else null
331         val fileLocation =
332             when {
333                 sourceFile != null -> TurbineFileLocation.forTree(sourceFile, decl)
334                 containingClassItem != null ->
335                     TurbineFileLocation.forTree(containingClassItem, decl)
336                 else -> FileLocation.UNKNOWN
337             }
338         val classItem =
339             TurbineClassItem(
340                 codebase,
341                 fileLocation,
342                 simpleName,
343                 fullName,
344                 qualifiedName,
345                 sym,
346                 modifierItem,
347                 getClassKind(cls.kind()),
348                 typeParameters,
349                 getCommentedDoc(documentation),
350                 sourceFile,
351             )
352         classItem.containingClass = containingClassItem
353         modifierItem.setSynchronized(false) // A class can not be synchronized in java
354 
355         // Setup the SuperClass
356         if (!classItem.isInterface()) {
357             val superClassType = cls.superClassType()
358             val superClassTypeItem =
359                 if (superClassType == null) null
360                 else classTypeItemFactory.getSuperClassType(superClassType)
361             classItem.setSuperClassType(superClassTypeItem)
362         }
363 
364         // Set interface types
365         classItem.setInterfaceTypes(
366             cls.interfaceTypes().map { classTypeItemFactory.getInterfaceType(it) }
367         )
368 
369         // Create fields
370         createFields(classItem, cls.fields(), classTypeItemFactory)
371 
372         // Create methods
373         createMethods(classItem, cls.methods(), classTypeItemFactory)
374 
375         // Create constructors
376         createConstructors(classItem, cls.methods(), classTypeItemFactory)
377 
378         // Add to the codebase
379         codebase.registerClass(classItem, isTopClass)
380 
381         // Add the class to corresponding PackageItem
382         if (isTopClass) {
383             classItem.containingPackage = pkgItem
384             pkgItem.addTopClass(classItem)
385         }
386 
387         // Do not emit to signature file if it is from classpath
388         if (isFromClassPath) {
389             pkgItem.emit = false
390             classItem.emit = false
391         }
392 
393         // Create InnerClasses.
394         val children = cls.children()
395         createInnerClasses(classItem, children.values.asList(), classTypeItemFactory)
396 
397         return classItem
398     }
399 
400     fun getClassKind(type: TurbineTyKind): ClassKind {
401         return when (type) {
402             TurbineTyKind.INTERFACE -> ClassKind.INTERFACE
403             TurbineTyKind.ENUM -> ClassKind.ENUM
404             TurbineTyKind.ANNOTATION -> ClassKind.ANNOTATION_TYPE
405             else -> ClassKind.CLASS
406         }
407     }
408 
409     /** Creates a list of AnnotationItems from given list of Turbine Annotations */
410     internal fun createAnnotations(annotations: List<AnnoInfo>): List<AnnotationItem> {
411         return annotations.map { createAnnotation(it) }
412     }
413 
414     private fun createAnnotation(annotation: AnnoInfo): AnnotationItem {
415         val simpleName = annotation.tree()?.let { extractNameFromIdent(it.name()) }
416         val clsSym = annotation.sym()
417         val qualifiedName =
418             if (clsSym == null) simpleName!! else getQualifiedName(clsSym.binaryName())
419 
420         return DefaultAnnotationItem(codebase, qualifiedName) {
421             getAnnotationAttributes(annotation.values(), annotation.tree()?.args())
422         }
423     }
424 
425     /** Creates a list of AnnotationAttribute from the map of name-value attribute pairs */
426     private fun getAnnotationAttributes(
427         attrs: ImmutableMap<String, Const>,
428         exprs: ImmutableList<Expression>?
429     ): List<AnnotationAttribute> {
430         val attributes = mutableListOf<AnnotationAttribute>()
431         if (exprs != null) {
432             for (exp in exprs) {
433                 when (exp.kind()) {
434                     Tree.Kind.ASSIGN -> {
435                         exp as Assign
436                         val name = exp.name().value()
437                         val assignExp = exp.expr()
438                         attributes.add(
439                             DefaultAnnotationAttribute(
440                                 name,
441                                 createAttrValue(attrs[name]!!, assignExp)
442                             )
443                         )
444                     }
445                     else -> {
446                         val name = ANNOTATION_ATTR_VALUE
447                         attributes.add(
448                             DefaultAnnotationAttribute(name, createAttrValue(attrs[name]!!, exp))
449                         )
450                     }
451                 }
452             }
453         } else {
454             for ((name, value) in attrs) {
455                 attributes.add(DefaultAnnotationAttribute(name, createAttrValue(value, null)))
456             }
457         }
458         return attributes
459     }
460 
461     private fun createAttrValue(const: Const, expr: Expression?): AnnotationAttributeValue {
462         if (const.kind() == Kind.ARRAY) {
463             const as ArrayInitValue
464             if (const.elements().count() == 1 && expr != null && !(expr is ArrayInit)) {
465                 // This is case where defined type is array type but provided attribute value is
466                 // single non-array element
467                 // For e.g. @Anno(5) where Anno is @interfacce Anno {int [] value()}
468                 val constLiteral = const.elements().single()
469                 return DefaultAnnotationSingleAttributeValue(
470                     { getSource(constLiteral, expr) },
471                     { getValue(constLiteral) }
472                 )
473             }
474             return DefaultAnnotationArrayAttributeValue(
475                 { getSource(const, expr) },
476                 { const.elements().map { createAttrValue(it, null) } }
477             )
478         }
479         return DefaultAnnotationSingleAttributeValue(
480             { getSource(const, expr) },
481             { getValue(const) }
482         )
483     }
484 
485     private fun getSource(const: Const, expr: Expression?): String {
486         return when (const.kind()) {
487             Kind.PRIMITIVE -> {
488                 when ((const as Value).constantTypeKind()) {
489                     PrimKind.INT -> {
490                         val value = (const as Const.IntValue).value()
491                         if (value < 0 || (expr != null && expr.kind() == Tree.Kind.TYPE_CAST))
492                             "0x" + value.toUInt().toString(16) // Hex Value
493                         else value.toString()
494                     }
495                     PrimKind.SHORT -> {
496                         val value = (const as Const.ShortValue).value()
497                         if (value < 0) "0x" + value.toUInt().toString(16) else value.toString()
498                     }
499                     PrimKind.FLOAT -> {
500                         val value = (const as Const.FloatValue).value()
501                         when {
502                             value == Float.POSITIVE_INFINITY -> "java.lang.Float.POSITIVE_INFINITY"
503                             value == Float.NEGATIVE_INFINITY -> "java.lang.Float.NEGATIVE_INFINITY"
504                             value < 0 -> value.toString() + "F" // Handling negative values
505                             else -> value.toString() + "f" // Handling positive values
506                         }
507                     }
508                     PrimKind.DOUBLE -> {
509                         val value = (const as Const.DoubleValue).value()
510                         when {
511                             value == Double.POSITIVE_INFINITY ->
512                                 "java.lang.Double.POSITIVE_INFINITY"
513                             value == Double.NEGATIVE_INFINITY ->
514                                 "java.lang.Double.NEGATIVE_INFINITY"
515                             else -> const.toString()
516                         }
517                     }
518                     PrimKind.BYTE -> const.getValue().toString()
519                     else -> const.toString()
520                 }
521             }
522             Kind.ARRAY -> {
523                 const as ArrayInitValue
524                 val pairs =
525                     if (expr != null) const.elements().zip((expr as ArrayInit).exprs())
526                     else const.elements().map { Pair(it, null) }
527                 buildString {
528                         append("{")
529                         pairs.joinTo(this, ", ") { getSource(it.first, it.second) }
530                         append("}")
531                     }
532                     .toString()
533             }
534             Kind.ENUM_CONSTANT -> getValue(const).toString()
535             Kind.CLASS_LITERAL -> {
536                 if (expr != null) expr.toString() else getValue(const).toString()
537             }
538             else -> const.toString()
539         }
540     }
541 
542     private fun getValue(const: Const): Any? {
543         when (const.kind()) {
544             Kind.PRIMITIVE -> {
545                 val value = const as Value
546                 return value.getValue()
547             }
548             // For cases like AnyClass.class, return the qualified name of AnyClass
549             Kind.CLASS_LITERAL -> {
550                 val value = const as TurbineClassValue
551                 return value.type().toString()
552             }
553             Kind.ENUM_CONSTANT -> {
554                 val value = const as EnumConstantValue
555                 val temp =
556                     getQualifiedName(value.sym().owner().binaryName()) + "." + value.toString()
557                 return temp
558             }
559             else -> {
560                 return const.toString()
561             }
562         }
563     }
564 
565     private fun createTypeParameters(
566         tyParams: ImmutableMap<TyVarSymbol, TyVarInfo>,
567         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
568         description: String,
569     ): Pair<TypeParameterList, TurbineTypeItemFactory> {
570 
571         if (tyParams.isEmpty()) return Pair(TypeParameterList.NONE, enclosingClassTypeItemFactory)
572 
573         // Create a list of [TypeParameterItem]s from turbine specific classes.
574         val (typeParameters, typeItemFactory) =
575             DefaultTypeParameterList.createTypeParameterItemsAndFactory(
576                 enclosingClassTypeItemFactory,
577                 description,
578                 tyParams.toList(),
579                 { (sym, tyParam) -> createTypeParameter(sym, tyParam) },
580                 { typeItemFactory, item, (_, tParam) ->
581                     createTypeParameterBounds(tParam, typeItemFactory).also { item.bounds = it }
582                 },
583             )
584 
585         return Pair(DefaultTypeParameterList(typeParameters), typeItemFactory)
586     }
587 
588     /**
589      * Create the [TurbineTypeParameterItem] without any bounds and register it so that any uses of
590      * it within the type bounds, e.g. `<E extends Enum<E>>`, or from other type parameters within
591      * the same [TypeParameterList] can be resolved.
592      */
593     private fun createTypeParameter(sym: TyVarSymbol, param: TyVarInfo): TurbineTypeParameterItem {
594         val modifiers =
595             TurbineModifierItem.create(codebase, 0, createAnnotations(param.annotations()), false)
596         val typeParamItem = TurbineTypeParameterItem(codebase, modifiers, name = sym.name())
597         return typeParamItem
598     }
599 
600     /** Create the bounds of a [TurbineTypeParameterItem]. */
601     private fun createTypeParameterBounds(
602         param: TyVarInfo,
603         typeItemFactory: TurbineTypeItemFactory,
604     ): List<BoundsTypeItem> {
605         val typeBounds = mutableListOf<BoundsTypeItem>()
606         val upperBounds = param.upperBound()
607 
608         upperBounds.bounds().mapTo(typeBounds) { typeItemFactory.getBoundsType(it) }
609         param.lowerBound()?.let { typeBounds.add(typeItemFactory.getBoundsType(it)) }
610 
611         return typeBounds.toList()
612     }
613 
614     /** This method sets up the inner class hierarchy. */
615     private fun createInnerClasses(
616         classItem: TurbineClassItem,
617         innerClasses: ImmutableList<ClassSymbol>,
618         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
619     ) {
620         classItem.innerClasses =
621             innerClasses.map { cls -> createClass(cls, classItem, enclosingClassTypeItemFactory) }
622     }
623 
624     /** This methods creates and sets the fields of a class */
625     private fun createFields(
626         classItem: TurbineClassItem,
627         fields: ImmutableList<FieldInfo>,
628         typeItemFactory: TurbineTypeItemFactory,
629     ) {
630         classItem.fields =
631             fields.map { field ->
632                 val annotations = createAnnotations(field.annotations())
633                 val flags = field.access()
634                 val decl = field.decl()
635                 val fieldModifierItem =
636                     TurbineModifierItem.create(
637                         codebase,
638                         flags,
639                         annotations,
640                         isDeprecated(javadoc(decl))
641                     )
642                 val isEnumConstant = (flags and TurbineFlag.ACC_ENUM) != 0
643                 val fieldValue = createInitialValue(field)
644                 val type =
645                     typeItemFactory.getFieldType(
646                         underlyingType = field.type(),
647                         itemAnnotations = annotations,
648                         isEnumConstant = isEnumConstant,
649                         isFinal = fieldModifierItem.isFinal(),
650                         isInitialValueNonNull = {
651                             // The initial value is non-null if the value is a literal which is not
652                             // null.
653                             fieldValue.initialValue(false) != null
654                         }
655                     )
656 
657                 val documentation = javadoc(decl)
658                 val fieldItem =
659                     TurbineFieldItem(
660                         codebase,
661                         TurbineFileLocation.forTree(classItem, decl),
662                         field.name(),
663                         classItem,
664                         type,
665                         fieldModifierItem,
666                         getCommentedDoc(documentation),
667                         isEnumConstant,
668                         fieldValue,
669                     )
670                 fieldItem
671             }
672     }
673 
674     private fun createMethods(
675         classItem: TurbineClassItem,
676         methods: List<MethodInfo>,
677         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
678     ) {
679         val methodItems =
680             methods
681                 .filter { it.sym().name() != "<init>" }
682                 .map { method ->
683                     val annotations = createAnnotations(method.annotations())
684                     val decl: MethDecl? = method.decl()
685                     val methodModifierItem =
686                         TurbineModifierItem.create(
687                             codebase,
688                             method.access(),
689                             annotations,
690                             isDeprecated(javadoc(decl))
691                         )
692                     val (typeParams, methodTypeItemFactory) =
693                         createTypeParameters(
694                             method.tyParams(),
695                             enclosingClassTypeItemFactory,
696                             method.name(),
697                         )
698                     val documentation = javadoc(decl)
699                     val defaultValueExpr = getAnnotationDefaultExpression(method)
700                     val defaultValue =
701                         if (method.defaultValue() != null)
702                             extractAnnotationDefaultValue(method.defaultValue()!!, defaultValueExpr)
703                         else ""
704 
705                     val parameters = method.parameters()
706                     val fingerprint = MethodFingerprint(method.name(), parameters.size)
707                     val isAnnotationElement =
708                         classItem.isAnnotationType() && !methodModifierItem.isStatic()
709                     val returnType =
710                         methodTypeItemFactory.getMethodReturnType(
711                             underlyingReturnType = method.returnType(),
712                             itemAnnotations = methodModifierItem.annotations(),
713                             fingerprint = fingerprint,
714                             isAnnotationElement = isAnnotationElement,
715                         )
716 
717                     val methodItem =
718                         TurbineMethodItem(
719                             codebase,
720                             TurbineFileLocation.forTree(classItem, decl),
721                             method.sym(),
722                             classItem,
723                             returnType,
724                             methodModifierItem,
725                             typeParams,
726                             getCommentedDoc(documentation),
727                             defaultValue,
728                         )
729                     createParameters(
730                         methodItem,
731                         decl?.params(),
732                         parameters,
733                         methodTypeItemFactory,
734                     )
735                     methodItem.throwableTypes =
736                         getThrowsList(method.exceptions(), methodTypeItemFactory)
737                     methodItem
738                 }
739         // Ignore default enum methods
740         classItem.methods =
741             methodItems.filter { !isDefaultEnumMethod(classItem, it) }.toMutableList()
742     }
743 
744     private fun createParameters(
745         methodItem: TurbineMethodItem,
746         parameterDecls: List<Tree.VarDecl>?,
747         parameters: List<ParamInfo>,
748         typeItemFactory: TurbineTypeItemFactory,
749     ) {
750         val fingerprint = MethodFingerprint(methodItem.name(), parameters.size)
751         // Some parameters in [parameters] are implicit parameters that do not have a corresponding
752         // entry in the [parameterDecls] list. The number of implicit parameters is the total
753         // number of [parameters] minus the number of declared parameters [parameterDecls]. The
754         // implicit parameters are always at the beginning so the offset from the declared parameter
755         // in [parameterDecls] to the corresponding parameter in [parameters] is simply the number
756         // of the implicit parameters.
757         val declaredParameterOffset = parameters.size - (parameterDecls?.size ?: 0)
758         methodItem.parameters =
759             parameters.mapIndexed { idx, parameter ->
760                 val annotations = createAnnotations(parameter.annotations())
761                 val parameterModifierItem =
762                     TurbineModifierItem.create(codebase, parameter.access(), annotations, false)
763                 val type =
764                     typeItemFactory.getMethodParameterType(
765                         underlyingParameterType = parameter.type(),
766                         itemAnnotations = annotations,
767                         fingerprint = fingerprint,
768                         parameterIndex = idx,
769                         isVarArg = parameterModifierItem.isVarArg(),
770                     )
771                 // Get the [Tree.VarDecl] corresponding to the [ParamInfo], if available.
772                 val decl =
773                     if (parameterDecls != null && idx >= declaredParameterOffset)
774                         parameterDecls.get(idx - declaredParameterOffset)
775                     else null
776 
777                 val parameterItem =
778                     TurbineParameterItem(
779                         codebase,
780                         TurbineFileLocation.forTree(methodItem.containingClass(), decl),
781                         parameter.name(),
782                         methodItem,
783                         idx,
784                         type,
785                         parameterModifierItem,
786                     )
787                 parameterItem
788             }
789     }
790 
791     private fun createConstructors(
792         classItem: TurbineClassItem,
793         methods: List<MethodInfo>,
794         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
795     ) {
796         var hasImplicitDefaultConstructor = false
797         classItem.constructors =
798             methods
799                 .filter { it.sym().name() == "<init>" }
800                 .map { constructor ->
801                     val annotations = createAnnotations(constructor.annotations())
802                     val decl: MethDecl? = constructor.decl()
803                     val constructorModifierItem =
804                         TurbineModifierItem.create(
805                             codebase,
806                             constructor.access(),
807                             annotations,
808                             isDeprecated(javadoc(decl))
809                         )
810                     val (typeParams, constructorTypeItemFactory) =
811                         createTypeParameters(
812                             constructor.tyParams(),
813                             enclosingClassTypeItemFactory,
814                             constructor.name(),
815                         )
816                     hasImplicitDefaultConstructor =
817                         (constructor.access() and TurbineFlag.ACC_SYNTH_CTOR) != 0
818                     val name = classItem.simpleName()
819                     val documentation = javadoc(decl)
820                     val constructorItem =
821                         TurbineConstructorItem(
822                             codebase,
823                             TurbineFileLocation.forTree(classItem, decl),
824                             name,
825                             constructor.sym(),
826                             classItem,
827                             // Turbine's Binder gives return type of constructors as void but the
828                             // model expects it to the type of object being created. So, use the
829                             // containing [ClassItem]'s type as the constructor return type.
830                             classItem.type(),
831                             constructorModifierItem,
832                             typeParams,
833                             getCommentedDoc(documentation),
834                             "",
835                         )
836                     createParameters(
837                         constructorItem,
838                         decl?.params(),
839                         constructor.parameters(),
840                         constructorTypeItemFactory,
841                     )
842                     constructorItem.throwableTypes =
843                         getThrowsList(constructor.exceptions(), constructorTypeItemFactory)
844                     constructorItem
845                 }
846         classItem.hasImplicitDefaultConstructor = hasImplicitDefaultConstructor
847     }
848 
849     internal fun getQualifiedName(binaryName: String): String {
850         return binaryName.replace('/', '.').replace('$', '.')
851     }
852 
853     /**
854      * Get the ClassSymbol corresponding to a qualified name. Since the Turbine's lookup method
855      * returns only top-level classes, this method will return the ClassSymbol of outermost class
856      * for inner classes.
857      */
858     private fun getClassSymbol(name: String): ClassSymbol? {
859         val result = index.scope().lookup(createLookupKey(name))
860         return result?.let { it.sym() as ClassSymbol }
861     }
862 
863     /** Creates a LookupKey from a given name */
864     private fun createLookupKey(name: String): LookupKey {
865         val idents = name.split(".").mapIndexed { idx, it -> Ident(idx, it) }
866         return LookupKey(ImmutableList.copyOf(idents))
867     }
868 
869     private fun javadoc(item: Tree.TyDecl?): String {
870         if (!codebase.allowReadingComments) return ""
871         return item?.javadoc() ?: ""
872     }
873 
874     private fun javadoc(item: Tree.VarDecl?): String {
875         if (!codebase.allowReadingComments) return ""
876         return item?.javadoc() ?: ""
877     }
878 
879     private fun javadoc(item: Tree.MethDecl?): String {
880         if (!codebase.allowReadingComments) return ""
881         return item?.javadoc() ?: ""
882     }
883 
884     private fun isDeprecated(javadoc: String?): Boolean {
885         return javadoc?.contains("@deprecated") ?: false
886     }
887 
888     private fun getThrowsList(
889         throwsTypes: List<Type>,
890         enclosingTypeItemFactory: TurbineTypeItemFactory
891     ): List<ExceptionTypeItem> {
892         return throwsTypes.map { type -> enclosingTypeItemFactory.getExceptionType(type) }
893     }
894 
895     private fun getCommentedDoc(doc: String): String {
896         return buildString {
897                 if (doc != "") {
898                     append("/**")
899                     append(doc)
900                     append("*/")
901                 }
902             }
903             .toString()
904     }
905 
906     private fun createInitialValue(field: FieldInfo): TurbineFieldValue {
907         val optExpr = field.decl()?.init()
908         val expr = if (optExpr != null && optExpr.isPresent()) optExpr.get() else null
909         val constantValue = field.value()?.getValue()
910 
911         val initialValueWithoutRequiredConstant =
912             when {
913                 constantValue != null -> constantValue
914                 expr == null -> null
915                 else ->
916                     when (expr.kind()) {
917                         Tree.Kind.LITERAL -> {
918                             getValue((expr as Literal).value())
919                         }
920                         // Class Type
921                         Tree.Kind.CLASS_LITERAL -> {
922                             expr
923                         }
924                         else -> {
925                             null
926                         }
927                     }
928             }
929 
930         return TurbineFieldValue(constantValue, initialValueWithoutRequiredConstant)
931     }
932 
933     /** Determines whether the given method is a default enum method ("values" or "valueOf"). */
934     private fun isDefaultEnumMethod(classItem: ClassItem, methodItem: MethodItem): Boolean =
935         classItem.isEnum() &&
936             (methodItem.name() == "values" && isValuesMethod(classItem, methodItem) ||
937                 methodItem.name() == "valueOf" && isValueOfMethod(classItem, methodItem))
938 
939     /** Checks if the given method matches the signature of the "values" enum method. */
940     private fun isValuesMethod(classItem: ClassItem, methodItem: MethodItem): Boolean =
941         methodItem.returnType().let { returnType ->
942             returnType is ArrayTypeItem &&
943                 matchType(returnType.componentType, classItem) &&
944                 methodItem.parameters().isEmpty()
945         }
946 
947     /** Checks if the given method matches the signature of the "valueOf" enum method. */
948     private fun isValueOfMethod(classItem: ClassItem, methodItem: MethodItem): Boolean =
949         matchType(methodItem.returnType(), classItem) &&
950             methodItem.parameters().singleOrNull()?.type()?.let {
951                 it is ClassTypeItem && it.qualifiedName == "java.lang.String"
952             }
953                 ?: false
954 
955     private fun matchType(typeItem: TypeItem, classItem: ClassItem): Boolean =
956         typeItem is ClassTypeItem && typeItem.qualifiedName == classItem.qualifiedName()
957 
958     /**
959      * Extracts the expression corresponding to the default value of a given annotation method. If
960      * the method does not have a default value, returns null.
961      */
962     private fun getAnnotationDefaultExpression(method: MethodInfo): Tree? {
963         val optExpr = method.decl()?.defaultValue()
964         return if (optExpr != null && optExpr.isPresent()) optExpr.get() else null
965     }
966 
967     /**
968      * Extracts the default value of an annotation method and returns it as a string.
969      *
970      * @param const The constant object representing the annotation value.
971      * @param expr An optional expression tree that might provide additional context for value
972      *   extraction.
973      * @return The default value of the annotation method as a string.
974      */
975     private fun extractAnnotationDefaultValue(const: Const, expr: Tree?): String {
976         return when (const.kind()) {
977             Kind.PRIMITIVE -> {
978                 when ((const as Value).constantTypeKind()) {
979                     PrimKind.FLOAT -> {
980                         val value = (const as Const.FloatValue).value()
981                         when {
982                             value == Float.POSITIVE_INFINITY -> "java.lang.Float.POSITIVE_INFINITY"
983                             value == Float.NEGATIVE_INFINITY -> "java.lang.Float.NEGATIVE_INFINITY"
984                             else -> value.toString() + "f"
985                         }
986                     }
987                     PrimKind.DOUBLE -> {
988                         val value = (const as Const.DoubleValue).value()
989                         when {
990                             value == Double.POSITIVE_INFINITY ->
991                                 "java.lang.Double.POSITIVE_INFINITY"
992                             value == Double.NEGATIVE_INFINITY ->
993                                 "java.lang.Double.NEGATIVE_INFINITY"
994                             else -> const.toString()
995                         }
996                     }
997                     PrimKind.BYTE -> const.getValue().toString()
998                     else -> const.toString()
999                 }
1000             }
1001             Kind.ARRAY -> {
1002                 const as ArrayInitValue
1003                 // This is case where defined type is array type but default value is
1004                 // single non-array element
1005                 // For e.g. char[] letter() default 'a';
1006                 if (const.elements().count() == 1 && expr != null && !(expr is ArrayInit)) {
1007                     extractAnnotationDefaultValue(const.elements().single(), expr)
1008                 } else getValue(const).toString()
1009             }
1010             Kind.CLASS_LITERAL -> getValue(const).toString() + ".class"
1011             else -> getValue(const).toString()
1012         }
1013     }
1014 
1015     internal fun getTypeElement(name: String): TypeElement? = turbineElements.getTypeElement(name)
1016 }
1017