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