1 /* <lambda>null2 * Copyright (C) 2017 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 18 19 import com.android.tools.metalava.model.AnnotationAttributeValue 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.Codebase 22 import com.android.tools.metalava.model.ConstructorItem 23 import com.android.tools.metalava.model.FieldItem 24 import com.android.tools.metalava.model.Item 25 import com.android.tools.metalava.model.MethodItem 26 import com.android.tools.metalava.model.PackageItem 27 import com.android.tools.metalava.model.PackageList 28 import com.android.tools.metalava.model.ParameterItem 29 import com.android.tools.metalava.model.TypeItem 30 import com.android.tools.metalava.model.VisibilityLevel 31 import com.android.tools.metalava.model.visitors.ApiVisitor 32 import com.android.tools.metalava.model.visitors.ItemVisitor 33 import java.util.ArrayList 34 import java.util.HashMap 35 import java.util.HashSet 36 import java.util.function.Predicate 37 38 /** 39 * The [ApiAnalyzer] is responsible for walking over the various 40 * classes and members and compute visibility etc of the APIs 41 */ 42 class ApiAnalyzer( 43 /** The code to analyze */ 44 private val codebase: Codebase 45 ) { 46 /** All packages in the API */ 47 private val packages: PackageList = codebase.getPackages() 48 49 fun computeApi() { 50 if (codebase.trustedApi()) { 51 // The codebase is already an API; no consistency checks to be performed 52 return 53 } 54 55 // Apply options for packages that should be hidden 56 hidePackages() 57 skipEmitPackages() 58 59 // Propagate visibility down into individual elements -- if a class is hidden, 60 // then the methods and fields are hidden etc 61 propagateHiddenRemovedAndDocOnly(false) 62 } 63 64 fun addConstructors(filter: Predicate<Item>) { 65 // Let's say I have 66 // class GrandParent { public GrandParent(int) {} } 67 // class Parent { Parent(int) {} } 68 // class Child { public Child(int) {} } 69 // 70 // Here Parent's constructor is not public. For normal stub generation I'd end up with this: 71 // class GrandParent { public GrandParent(int) {} } 72 // class Parent { } 73 // class Child { public Child(int) {} } 74 // 75 // This doesn't compile - Parent can't have a default constructor since there isn't 76 // one for it to invoke on GrandParent. 77 // 78 // I can generate a fake constructor instead, such as 79 // Parent() { super(0); } 80 // 81 // But it's hard to do this lazily; what if I'm generating the Child class first? 82 // Therefore, we'll instead walk over the hierarchy and insert these constructors 83 // into the Item hierarchy such that code generation can find them. 84 // 85 // (We also need to handle the throws list, so we can't just unconditionally 86 // insert package private constructors 87 // 88 // To do this right I really need to process super constructors before the classes 89 // depending on them. 90 91 // Mark all classes that are the super class of some other class: 92 val allClasses = packages.allClasses().filter { filter.test(it) } 93 94 codebase.clearTags() 95 allClasses.forEach { cls -> 96 cls.superClass()?.tag = true 97 } 98 99 val leafClasses = allClasses.filter { !it.tag }.toList() 100 101 // Now walk through all the leaf classes, and walk up the super hierarchy 102 // and recursively add constructors; we'll do it recursively to make sure that 103 // the superclass has had its constructors initialized first (such that we can 104 // match the parameter lists and throws signatures), and we use the tag fields 105 // to avoid looking at all the internal classes more than once. 106 codebase.clearTags() 107 leafClasses 108 // Filter classes by filter here to not waste time in hidden packages 109 .filter { filter.test(it) } 110 .forEach { addConstructors(it, filter) } 111 } 112 113 /** 114 * Handle computing constructor hierarchy. We'll be setting several attributes: 115 * [ClassItem.stubConstructor] : The default constructor to invoke in this 116 * class from subclasses. **NOTE**: This constructor may not be part of 117 * the [ClassItem.constructors] list, e.g. for package private default constructors 118 * we've inserted (because there were no public constructors or constructors not 119 * using hidden parameter types.) 120 * 121 * If we can find a public constructor we'll put that here instead. 122 * 123 * [ConstructorItem.superConstructor] The default constructor to invoke. If set, 124 * use this rather than the [ClassItem.stubConstructor]. 125 * 126 * [Item.tag] : mark for avoiding repeated iteration of internal item nodes 127 * 128 * 129 */ 130 private fun addConstructors(cls: ClassItem, filter: Predicate<Item>) { 131 // What happens if we have 132 // package foo: 133 // public class A { public A(int) } 134 // package bar 135 // public class B extends A { public B(int) } 136 // If I just try inserting package private constructors here things will NOT work: 137 // package foo: 138 // public class A { public A(int); A() {} } 139 // package bar 140 // public class B extends A { public B(int); B() } 141 // because A <() is not accessible from B() -- it's outside the same package. 142 // 143 // So, I'll need to model the real constructors for all the scenarios where that 144 // works. 145 // 146 // The remaining challenge is that there will be some gaps: when I don't have 147 // a default constructor, subclass constructors will have to have an explicit 148 // super(args) call to pick the parent constructor to use. And which one? 149 // It generally doesn't matter; just pick one, but unfortunately, the super 150 // constructor can throw exceptions, and in that case the subclass constructor 151 // must also throw all those constructors (you can't surround a super call 152 // with try/catch.) Luckily, the source code already needs to do this to 153 // compile, so we can just use the same constructor as the super call. 154 // But there are two cases we have to deal with: 155 // (1) the constructor doesn't call a super constructor; it calls another 156 // constructor on this class. 157 // (2) the super constructor it *does* call isn't available. 158 // 159 // For (1), this means that our stub code generator should be prepared to 160 // handle both super- and this- dispatches; we'll handle this by pointing 161 // it to the constructor to use, and it checks to see if the containing class 162 // for the constructor is the same to decide whether to emit "this" or "super". 163 164 if (cls.tag || !cls.isClass()) { // Don't add constructors to interfaces, enums, annotations, etc 165 return 166 } 167 168 // First handle its super class hierarchy to make sure that we've 169 // already constructed super classes 170 val superClass = cls.filteredSuperclass(filter) 171 superClass?.let { addConstructors(it, filter) } 172 cls.tag = true 173 174 if (superClass != null) { 175 val superDefaultConstructor = superClass.stubConstructor 176 if (superDefaultConstructor != null) { 177 val constructors = cls.constructors() 178 for (constructor in constructors) { 179 val superConstructor = constructor.superConstructor 180 if (superConstructor == null || 181 (superConstructor.containingClass() != superClass && 182 superConstructor.containingClass() != cls) 183 ) { 184 constructor.superConstructor = superDefaultConstructor 185 } 186 } 187 } 188 } 189 190 // Find default constructor, if one doesn't exist 191 val allConstructors = cls.constructors() 192 if (allConstructors.isNotEmpty()) { 193 194 // Try and use a publicly accessible constructor first. 195 val constructors = cls.filteredConstructors(filter).toList() 196 if (constructors.isNotEmpty()) { 197 // Try to pick the constructor, select first by fewest throwables, then fewest parameters, 198 // then based on order in listFilter.test(cls) 199 cls.stubConstructor = constructors.reduce { first, second -> pickBest(first, second) } 200 return 201 } 202 203 // No accessible constructors are available so one will have to be created, either a private constructor to 204 // prevent instances of the class from being created, or a package private constructor for use by subclasses 205 // in the package to use. Subclasses outside the package would need a protected or public constructor which 206 // would already be part of the API so should have dropped out above. 207 // 208 // The visibility levels on the constructors from the source can give a clue as to what is required. e.g. 209 // if all constructors are private then it is ok for the generated constructor to be private, otherwise it 210 // should be package private. 211 val allPrivate = allConstructors.asSequence() 212 .map { it.isPrivate } 213 .reduce { v1, v2 -> v1 and v2 } 214 215 val visibilityLevel = if (allPrivate) VisibilityLevel.PRIVATE else VisibilityLevel.PACKAGE_PRIVATE 216 217 // No constructors, yet somebody extends this (or private constructor): we have to invent one, such that 218 // subclasses can dispatch to it in the stub files etc 219 cls.stubConstructor = cls.createDefaultConstructor().also { 220 it.mutableModifiers().setVisibilityLevel(visibilityLevel) 221 it.hidden = false 222 it.superConstructor = superClass?.stubConstructor 223 } 224 } 225 } 226 227 // TODO: Annotation test: @ParameterName, if present, must be supplied on *all* the arguments! 228 // Warn about @DefaultValue("null"); they probably meant @DefaultNull 229 // Supplying default parameter in override is not allowed! 230 231 private fun pickBest( 232 current: ConstructorItem, 233 next: ConstructorItem 234 ): ConstructorItem { 235 val currentThrowsCount = current.throwsTypes().size 236 val nextThrowsCount = next.throwsTypes().size 237 238 return if (currentThrowsCount < nextThrowsCount) { 239 current 240 } else if (currentThrowsCount > nextThrowsCount) { 241 next 242 } else { 243 val currentParameterCount = current.parameters().size 244 val nextParameterCount = next.parameters().size 245 if (currentParameterCount <= nextParameterCount) { 246 current 247 } else next 248 } 249 } 250 251 fun generateInheritedStubs(filterEmit: Predicate<Item>, filterReference: Predicate<Item>) { 252 // When analyzing libraries we may discover some new classes during traversal; these aren't 253 // part of the API but may be super classes or interfaces; these will then be added into the 254 // package class lists, which could trigger a concurrent modification, so create a snapshot 255 // of the class list and iterate over it: 256 val allClasses = packages.allClasses().toList() 257 allClasses.forEach { 258 if (filterEmit.test(it)) { 259 generateInheritedStubs(it, filterEmit, filterReference) 260 } 261 } 262 } 263 264 private fun generateInheritedStubs(cls: ClassItem, filterEmit: Predicate<Item>, filterReference: Predicate<Item>) { 265 if (!cls.isClass()) return 266 if (cls.superClass() == null) return 267 val superClasses: Sequence<ClassItem> = generateSequence(cls.superClass()) { it.superClass() } 268 val hiddenSuperClasses: Sequence<ClassItem> = 269 superClasses.filter { !filterReference.test(it) && !it.isJavaLangObject() } 270 271 if (hiddenSuperClasses.none()) { // not missing any implementation methods 272 return 273 } 274 275 addInheritedStubsFrom(cls, hiddenSuperClasses, superClasses, filterEmit, filterReference) 276 addInheritedInterfacesFrom(cls, hiddenSuperClasses, filterReference) 277 } 278 279 private fun addInheritedInterfacesFrom( 280 cls: ClassItem, 281 hiddenSuperClasses: Sequence<ClassItem>, 282 filterReference: Predicate<Item> 283 ) { 284 var interfaceTypes: MutableList<TypeItem>? = null 285 var interfaceTypeClasses: MutableList<ClassItem>? = null 286 for (hiddenSuperClass in hiddenSuperClasses) { 287 for (hiddenInterface in hiddenSuperClass.interfaceTypes()) { 288 val hiddenInterfaceClass = hiddenInterface.asClass() 289 if (filterReference.test(hiddenInterfaceClass ?: continue)) { 290 if (interfaceTypes == null) { 291 interfaceTypes = cls.interfaceTypes().toMutableList() 292 interfaceTypeClasses = 293 interfaceTypes.asSequence().map { it.asClass() }.filterNotNull().toMutableList() 294 if (cls.isInterface()) { 295 cls.superClass()?.let { interfaceTypeClasses.add(it) } 296 } 297 cls.setInterfaceTypes(interfaceTypes) 298 } 299 if (interfaceTypeClasses!!.any { it == hiddenInterfaceClass }) { 300 continue 301 } 302 303 interfaceTypeClasses.add(hiddenInterfaceClass) 304 305 if (hiddenInterfaceClass.hasTypeVariables()) { 306 val mapping = cls.mapTypeVariables(hiddenSuperClass) 307 if (mapping.isNotEmpty()) { 308 val mappedType: TypeItem = hiddenInterface.convertType(mapping, cls) 309 interfaceTypes.add(mappedType) 310 continue 311 } 312 } 313 314 interfaceTypes.add(hiddenInterface) 315 } 316 } 317 } 318 } 319 320 private fun addInheritedStubsFrom( 321 cls: ClassItem, 322 hiddenSuperClasses: Sequence<ClassItem>, 323 superClasses: Sequence<ClassItem>, 324 filterEmit: Predicate<Item>, 325 filterReference: Predicate<Item> 326 ) { 327 328 // Also generate stubs for any methods we would have inherited from abstract parents 329 // All methods from super classes that (1) aren't overridden in this class already, and 330 // (2) are overriding some method that is in a public interface accessible from this class. 331 val interfaces: Set<TypeItem> = cls.allInterfaceTypes(filterReference).asSequence().toSet() 332 333 // Note that we can't just call method.superMethods() to and see whether any of their containing 334 // classes are among our target APIs because it's possible that the super class doesn't actually 335 // implement the interface, but still provides a matching signature for the interface. 336 // Instead we'll look through all of our interface methods and look for potential overrides 337 val interfaceNames = mutableMapOf<String, MutableList<MethodItem>>() 338 for (interfaceType in interfaces) { 339 val interfaceClass = interfaceType.asClass() ?: continue 340 for (method in interfaceClass.methods()) { 341 val name = method.name() 342 val list = interfaceNames[name] ?: run { 343 val list = ArrayList<MethodItem>() 344 interfaceNames[name] = list 345 list 346 } 347 list.add(method) 348 } 349 } 350 351 // Also add in any abstract methods from public super classes 352 val publicSuperClasses = superClasses.filter { filterEmit.test(it) && !it.isJavaLangObject() } 353 for (superClass in publicSuperClasses) { 354 for (method in superClass.methods()) { 355 if (!method.modifiers.isAbstract() || !method.modifiers.isPublicOrProtected()) { 356 continue 357 } 358 val name = method.name() 359 val list = interfaceNames[name] ?: run { 360 val list = ArrayList<MethodItem>() 361 interfaceNames[name] = list 362 list 363 } 364 list.add(method) 365 } 366 } 367 368 // Also add in any concrete public methods from hidden super classes 369 for (superClass in hiddenSuperClasses) { 370 371 // Determine if there is a non-hidden class between the superClass and this class. 372 // If non hidden classes are found, don't include the methods for this hiddenSuperClass, 373 // as it will already have been included in a previous super class 374 var includeHiddenSuperClassMethods = true 375 var currentClass = cls.superClass() 376 while (currentClass != superClass && currentClass != null) { 377 if (!hiddenSuperClasses.contains(currentClass)) { 378 includeHiddenSuperClassMethods = false 379 break 380 } 381 currentClass = currentClass.superClass() 382 } 383 384 if (!includeHiddenSuperClassMethods) { 385 continue 386 } 387 388 for (method in superClass.methods()) { 389 if (method.modifiers.isAbstract() || !method.modifiers.isPublic()) { 390 continue 391 } 392 393 if (method.hasHiddenType(filterReference)) { 394 continue 395 } 396 397 val name = method.name() 398 val list = interfaceNames[name] ?: run { 399 val list = ArrayList<MethodItem>() 400 interfaceNames[name] = list 401 list 402 } 403 list.add(method) 404 } 405 } 406 407 // Find all methods that are inherited from these classes into our class 408 // (making sure that we don't have duplicates, e.g. a method defined by one 409 // inherited class and then overridden by another closer one). 410 // map from method name to super methods overriding our interfaces 411 val map = HashMap<String, MutableList<MethodItem>>() 412 413 for (superClass in hiddenSuperClasses) { 414 for (method in superClass.methods()) { 415 val modifiers = method.modifiers 416 if (!modifiers.isPrivate() && !modifiers.isAbstract()) { 417 val name = method.name() 418 val candidates = interfaceNames[name] ?: continue 419 val parameterCount = method.parameters().size 420 for (superMethod in candidates) { 421 if (parameterCount != superMethod.parameters().count()) { 422 continue 423 } 424 if (method.matches(superMethod)) { 425 val list = map[name] ?: run { 426 val newList = ArrayList<MethodItem>() 427 map[name] = newList 428 newList 429 } 430 list.add(method) 431 break 432 } 433 } 434 } 435 } 436 } 437 438 // Remove any methods that are overriding any of our existing methods 439 for (method in cls.methods()) { 440 val name = method.name() 441 val candidates = map[name] ?: continue 442 val iterator = candidates.listIterator() 443 while (iterator.hasNext()) { 444 val inheritedMethod = iterator.next() 445 if (method.matches(inheritedMethod)) { 446 iterator.remove() 447 } 448 } 449 } 450 451 // Next remove any overrides among the remaining super methods (e.g. one method from a hidden parent is 452 // overriding another method from a more distant hidden parent). 453 map.values.forEach { methods -> 454 if (methods.size >= 2) { 455 for (candidate in ArrayList(methods)) { 456 for (superMethod in candidate.allSuperMethods()) { 457 methods.remove(superMethod) 458 } 459 } 460 } 461 } 462 463 val existingMethodMap = HashMap<String, MutableList<MethodItem>>() 464 for (method in cls.methods()) { 465 val name = method.name() 466 val list = existingMethodMap[name] ?: run { 467 val newList = ArrayList<MethodItem>() 468 existingMethodMap[name] = newList 469 newList 470 } 471 list.add(method) 472 } 473 474 // We're now left with concrete methods in hidden parents that are implementing methods in public 475 // interfaces that are listed in this class. Create stubs for them: 476 map.values.flatten().forEach { 477 val method = cls.createMethod(it) 478 /* Insert comment marker: This is useful for debugging purposes but doesn't 479 belong in the stub 480 method.documentation = "// Inlined stub from hidden parent class ${it.containingClass().qualifiedName()}\n" + 481 method.documentation 482 */ 483 method.inheritedMethod = true 484 method.inheritedFrom = it.containingClass() 485 486 val name = method.name() 487 val candidates = existingMethodMap[name] 488 if (candidates != null) { 489 val iterator = candidates.listIterator() 490 while (iterator.hasNext()) { 491 val inheritedMethod = iterator.next() 492 if (method.matches(inheritedMethod)) { 493 // If we already have an override of this method, do not add it to the 494 // methods list 495 return@forEach 496 } 497 } 498 } 499 500 cls.addMethod(method) 501 } 502 } 503 504 /** Hide packages explicitly listed in [Options.hidePackages] */ 505 private fun hidePackages() { 506 for (pkgName in options.hidePackages) { 507 val pkg = codebase.findPackage(pkgName) ?: continue 508 pkg.hidden = true 509 } 510 } 511 512 /** Apply emit filters listed in [Options.skipEmitPackages] */ 513 private fun skipEmitPackages() { 514 for (pkgName in options.skipEmitPackages) { 515 val pkg = codebase.findPackage(pkgName) ?: continue 516 pkg.emit = false 517 } 518 } 519 520 /** 521 * Merge in external qualifier annotations (i.e. ones intended to be included in the API written 522 * from all configured sources. 523 */ 524 fun mergeExternalQualifierAnnotations() { 525 if (options.mergeQualifierAnnotations.isNotEmpty()) { 526 AnnotationsMerger(codebase).mergeQualifierAnnotations(options.mergeQualifierAnnotations) 527 } 528 } 529 530 /** Merge in external show/hide annotations from all configured sources */ 531 fun mergeExternalInclusionAnnotations() { 532 if (options.mergeInclusionAnnotations.isNotEmpty()) { 533 AnnotationsMerger(codebase).mergeInclusionAnnotations(options.mergeInclusionAnnotations) 534 } 535 } 536 537 /** 538 * Propagate the hidden flag down into individual elements -- if a class is hidden, then the methods and fields 539 * are hidden etc 540 */ 541 private fun propagateHiddenRemovedAndDocOnly(includingFields: Boolean) { 542 packages.accept(object : ItemVisitor(visitConstructorsAsMethods = true, nestInnerClasses = true) { 543 override fun visitPackage(pkg: PackageItem) { 544 when { 545 options.hidePackages.contains(pkg.qualifiedName()) -> pkg.hidden = true 546 pkg.modifiers.hasShowAnnotation() -> pkg.hidden = false 547 pkg.modifiers.hasHideAnnotations() -> pkg.hidden = true 548 } 549 val containingPackage = pkg.containingPackage() 550 if (containingPackage != null) { 551 if (containingPackage.hidden && !containingPackage.isDefault) { 552 pkg.hidden = true 553 } 554 if (containingPackage.docOnly) { 555 pkg.docOnly = true 556 } 557 } 558 } 559 560 override fun visitClass(cls: ClassItem) { 561 val containingClass = cls.containingClass() 562 if (cls.modifiers.hasShowAnnotation()) { 563 cls.hidden = false 564 // Make containing package non-hidden if it contains a show-annotation 565 // class. Doclava does this in PackageInfo.isHidden(). 566 cls.containingPackage().hidden = false 567 if (cls.containingClass() != null) { 568 ensureParentVisible(cls) 569 } 570 } else if (cls.modifiers.hasHideAnnotations()) { 571 cls.hidden = true 572 } else if (containingClass != null) { 573 if (containingClass.hidden) { 574 cls.hidden = true 575 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 576 // See explanation in visitMethod 577 cls.hidden = true 578 } 579 if (containingClass.docOnly) { 580 cls.docOnly = true 581 } 582 if (containingClass.removed) { 583 cls.removed = true 584 } 585 } else { 586 val containingPackage = cls.containingPackage() 587 if (containingPackage.hidden && !containingPackage.isDefault) { 588 cls.hidden = true 589 } else if (containingPackage.originallyHidden) { 590 // Package was marked hidden; it's been unhidden by some other 591 // classes (marked with show annotations) but this class 592 // should continue to default. 593 cls.hidden = true 594 } 595 if (containingPackage.docOnly && !containingPackage.isDefault) { 596 cls.docOnly = true 597 } 598 if (containingPackage.removed && !cls.modifiers.hasShowAnnotation()) { 599 cls.removed = true 600 } 601 } 602 } 603 604 override fun visitMethod(method: MethodItem) { 605 if (method.modifiers.hasShowAnnotation()) { 606 method.hidden = false 607 ensureParentVisible(method) 608 } else if (method.modifiers.hasHideAnnotations()) { 609 method.hidden = true 610 } else { 611 val containingClass = method.containingClass() 612 if (containingClass.hidden) { 613 method.hidden = true 614 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 615 // This is a member in a class that was hidden but then unhidden; 616 // but it was unhidden by a non-recursive (single) show annotation, so 617 // don't inherit the show annotation into this item. 618 method.hidden = true 619 } 620 if (containingClass.docOnly) { 621 method.docOnly = true 622 } 623 if (containingClass.removed) { 624 method.removed = true 625 } 626 } 627 } 628 629 override fun visitField(field: FieldItem) { 630 if (field.modifiers.hasShowAnnotation()) { 631 field.hidden = false 632 ensureParentVisible(field) 633 } else if (field.modifiers.hasHideAnnotations()) { 634 field.hidden = true 635 } else { 636 val containingClass = field.containingClass() 637 /* We don't always propagate field visibility down to the fields 638 because we sometimes move fields around, and in that 639 case we don't want to carry forward the "hidden" attribute 640 from the field that wasn't marked on the field but its 641 container interface. 642 */ 643 if (includingFields && containingClass.hidden) { 644 field.hidden = true 645 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 646 // See explanation in visitMethod 647 field.hidden = true 648 } 649 if (containingClass.docOnly) { 650 field.docOnly = true 651 } 652 if (containingClass.removed) { 653 field.removed = true 654 } 655 } 656 } 657 658 private fun ensureParentVisible(item: Item) { 659 val parent = item.parent() ?: return 660 if (!parent.hidden) { 661 return 662 } 663 val violatingAnnotation = if (item.modifiers.hasShowAnnotation()) { 664 item.modifiers.annotations().find { 665 options.showAnnotations.matches(it) 666 } ?: options.showAnnotations.firstQualifiedName() 667 } else if (item.modifiers.hasShowSingleAnnotation()) { 668 item.modifiers.annotations().find { 669 options.showSingleAnnotations.matches(it) 670 } ?: options.showSingleAnnotations.firstQualifiedName() 671 } else { 672 null 673 } 674 if (violatingAnnotation != null) { 675 reporter.report( 676 Issues.SHOWING_MEMBER_IN_HIDDEN_CLASS, item, 677 "Attempting to unhide ${item.describe()}, but surrounding ${parent.describe()} is " + 678 "hidden and should also be annotated with $violatingAnnotation") 679 } 680 } 681 }) 682 } 683 684 private fun checkSystemPermissions(method: MethodItem) { 685 if (method.isImplicitConstructor()) { // Don't warn on non-source elements like implicit default constructors 686 return 687 } 688 689 val annotation = method.modifiers.findAnnotation(ANDROID_REQUIRES_PERMISSION) 690 var hasAnnotation = false 691 692 if (annotation != null) { 693 hasAnnotation = true 694 for (attribute in annotation.attributes()) { 695 var values: List<AnnotationAttributeValue>? = null 696 var any = false 697 when (attribute.name) { 698 "value", "allOf" -> { 699 values = attribute.leafValues() 700 } 701 "anyOf" -> { 702 any = true 703 values = attribute.leafValues() 704 } 705 } 706 707 values ?: continue 708 709 val system = ArrayList<String>() 710 val nonSystem = ArrayList<String>() 711 val missing = ArrayList<String>() 712 for (value in values) { 713 val perm = (value.value() ?: value.toSource()).toString() 714 val level = codebase.getPermissionLevel(perm) 715 if (level == null) { 716 if (any) { 717 missing.add(perm) 718 continue 719 } 720 721 reporter.report( 722 Issues.REQUIRES_PERMISSION, method, 723 "Permission '$perm' is not defined by manifest ${codebase.manifest}." 724 ) 725 continue 726 } 727 if (level.contains("normal") || level.contains("dangerous") || 728 level.contains("ephemeral") 729 ) { 730 nonSystem.add(perm) 731 } else { 732 system.add(perm) 733 } 734 } 735 if (any && missing.size == values.size) { 736 reporter.report( 737 Issues.REQUIRES_PERMISSION, method, 738 "None of the permissions ${missing.joinToString()} are defined by manifest " + 739 "${codebase.manifest}." 740 ) 741 } 742 743 if (system.isEmpty() && nonSystem.isEmpty()) { 744 hasAnnotation = false 745 } else if (any && nonSystem.isNotEmpty() || !any && system.isEmpty()) { 746 reporter.report( 747 Issues.REQUIRES_PERMISSION, method, "Method '" + method.name() + 748 "' must be protected with a system permission; it currently" + 749 " allows non-system callers holding " + nonSystem.toString() 750 ) 751 } 752 } 753 } 754 755 if (!hasAnnotation) { 756 reporter.report( 757 Issues.REQUIRES_PERMISSION, method, "Method '" + method.name() + 758 "' must be protected with a system permission." 759 ) 760 } 761 } 762 763 fun performChecks() { 764 if (codebase.trustedApi()) { 765 // The codebase is already an API; no consistency checks to be performed 766 return 767 } 768 769 val checkSystemApi = !reporter.isSuppressed(Issues.REQUIRES_PERMISSION) && 770 options.showAnnotations.matches(ANDROID_SYSTEM_API) && options.manifest != null 771 val checkHiddenShowAnnotations = !reporter.isSuppressed(Issues.UNHIDDEN_SYSTEM_API) && 772 options.showAnnotations.isNotEmpty() 773 774 packages.accept(object : ApiVisitor() { 775 override fun visitParameter(parameter: ParameterItem) { 776 checkTypeReferencesHidden(parameter, parameter.type()) 777 } 778 779 override fun visitItem(item: Item) { 780 if (item.deprecated && !item.documentation.contains("@deprecated") && 781 // Don't warn about this in Kotlin; the Kotlin deprecation annotation includes deprecation 782 // messages (unlike java.lang.Deprecated which has no attributes). Instead, these 783 // are added to the documentation by the [DocAnalyzer]. 784 !item.isKotlin() 785 ) { 786 reporter.report( 787 Issues.DEPRECATION_MISMATCH, item, 788 "${item.toString().capitalize()}: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match" 789 ) 790 // TODO: Check opposite (doc tag but no annotation) 791 } 792 793 if (checkHiddenShowAnnotations && 794 item.hasShowAnnotation() && 795 !item.originallyHidden && 796 !item.modifiers.hasShowSingleAnnotation() 797 ) { 798 val annotationName = (item.modifiers.annotations().firstOrNull { annotation -> 799 options.showAnnotations.matches(annotation) 800 }?.qualifiedName() ?: options.showAnnotations.firstQualifiedName()).removePrefix(ANDROID_ANNOTATION_PREFIX) 801 reporter.report( 802 Issues.UNHIDDEN_SYSTEM_API, item, 803 "@$annotationName APIs must also be marked @hide: ${item.describe()}" 804 ) 805 } 806 } 807 808 override fun visitClass(cls: ClassItem) { 809 // Propagate @Deprecated flags down from classes into inner classes, if configured. 810 // Done here rather than in the analyzer which propagates visibility, since we want to do it 811 // after warning 812 val containingClass = cls.containingClass() 813 if (containingClass != null && containingClass.deprecated && compatibility.propagateDeprecatedInnerClasses) { 814 cls.deprecated = true 815 } 816 817 if (checkSystemApi) { 818 // Look for Android @SystemApi exposed outside the normal SDK; we require 819 // that they're protected with a system permission. 820 // Also flag @SystemApi apis not annotated with @hide. 821 822 // This class is a system service if it's annotated with @SystemService, 823 // or if it's android.content.pm.PackageManager 824 if (cls.modifiers.isAnnotatedWith("android.annotation.SystemService") || 825 cls.qualifiedName() == "android.content.pm.PackageManager" 826 ) { 827 // Check permissions on system services 828 for (method in cls.filteredMethods(filterEmit)) { 829 checkSystemPermissions(method) 830 } 831 } 832 } 833 } 834 835 override fun visitField(field: FieldItem) { 836 val containingClass = field.containingClass() 837 if (containingClass.deprecated && compatibility.propagateDeprecatedMembers) { 838 field.deprecated = true 839 } 840 841 checkTypeReferencesHidden(field, field.type()) 842 } 843 844 override fun visitMethod(method: MethodItem) { 845 if (!method.isConstructor()) { 846 checkTypeReferencesHidden( 847 method, 848 method.returnType()!! 849 ) // returnType is nullable only for constructors 850 } 851 852 val containingClass = method.containingClass() 853 if (containingClass.deprecated && compatibility.propagateDeprecatedMembers) { 854 method.deprecated = true 855 } 856 857 // Make sure we don't annotate findViewById & getSystemService as @Nullable. 858 // See for example 68914170. 859 val name = method.name() 860 if ((name == "findViewById" || name == "getSystemService") && method.parameters().size == 1 && 861 method.modifiers.isNullable() 862 ) { 863 reporter.report( 864 Issues.EXPECTED_PLATFORM_TYPE, method, 865 "$method should not be annotated @Nullable; it should be left unspecified to make it a platform type" 866 ) 867 val annotation = method.modifiers.annotations().find { it.isNullable() } 868 annotation?.let { 869 method.mutableModifiers().removeAnnotation(it) 870 // Have to also clear the annotation out of the return type itself, if it's a type 871 // use annotation 872 method.returnType()?.scrubAnnotations() 873 } 874 } 875 } 876 877 private fun checkTypeReferencesHidden(item: Item, type: TypeItem) { 878 if (type.primitive) { 879 return 880 } 881 882 val cls = type.asClass() 883 884 // Don't flag type parameters like T 885 if (cls?.isTypeParameter == true) { 886 return 887 } 888 889 // class may be null for things like array types and ellipsis types, 890 // but iterating through the type argument classes below will find and 891 // check the component class 892 if (cls != null && !filterReference.test(cls) && !cls.isFromClassPath()) { 893 reporter.report( 894 Issues.HIDDEN_TYPE_PARAMETER, item, 895 "${item.toString().capitalize()} references hidden type $type." 896 ) 897 } 898 899 type.typeArgumentClasses() 900 .filter { it != cls } 901 .forEach { checkTypeReferencesHidden(item, it) } 902 } 903 904 private fun checkTypeReferencesHidden(item: Item, cls: ClassItem) { 905 if (!filterReference.test(cls)) { 906 if (!cls.isFromClassPath()) { 907 reporter.report( 908 Issues.HIDDEN_TYPE_PARAMETER, item, 909 "${item.toString().capitalize()} references hidden type $cls." 910 ) 911 } 912 } else { 913 cls.typeArgumentClasses() 914 .filter { it != cls } 915 .forEach { checkTypeReferencesHidden(item, it) } 916 } 917 } 918 }) 919 } 920 921 fun handleStripping() { 922 // TODO: Switch to visitor iteration 923 // val stubPackages = options.stubPackages 924 val stubImportPackages = options.stubImportPackages 925 handleStripping(stubImportPackages) 926 } 927 928 private fun handleStripping(stubImportPackages: Set<String>) { 929 val notStrippable = HashSet<ClassItem>(5000) 930 931 val filter = ApiPredicate(ignoreShown = true) 932 933 // If a class is public or protected, not hidden, not imported and marked as included, 934 // then we can't strip it 935 val allTopLevelClasses = codebase.getPackages().allTopLevelClasses().toList() 936 allTopLevelClasses 937 .filter { it.checkLevel() && it.emit && !it.hidden() } 938 .forEach { 939 cantStripThis(it, filter, notStrippable, stubImportPackages, it, "self") 940 } 941 942 // complain about anything that looks includeable but is not supposed to 943 // be written, e.g. hidden things 944 for (cl in notStrippable) { 945 if (!cl.isHiddenOrRemoved()) { 946 val publiclyConstructable = 947 cl.constructors().any { it.checkLevel() } 948 for (m in cl.methods()) { 949 if (!m.checkLevel()) { 950 // TODO: enable this check for options.showSingleAnnotations 951 if (options.showSingleAnnotations.isEmpty() && 952 publiclyConstructable && m.modifiers.isAbstract() 953 ) { 954 reporter.report( 955 Issues.HIDDEN_ABSTRACT_METHOD, m, 956 "${m.name()} cannot be hidden and abstract when " + 957 "${cl.simpleName()} has a visible constructor, in case a " + 958 "third-party attempts to subclass it." 959 ) 960 } 961 continue 962 } 963 if (m.isHiddenOrRemoved()) { 964 reporter.report( 965 Issues.UNAVAILABLE_SYMBOL, m, 966 "Reference to unavailable method " + m.name() 967 ) 968 } else if (m.deprecated) { 969 // don't bother reporting deprecated methods 970 // unless they are public 971 reporter.report( 972 Issues.DEPRECATED, m, "Method " + cl.qualifiedName() + "." + 973 m.name() + " is deprecated" 974 ) 975 } 976 977 val returnType = m.returnType() 978 if (!m.deprecated && !cl.deprecated && returnType != null && returnType.asClass()?.deprecated == true) { 979 reporter.report( 980 Issues.REFERENCES_DEPRECATED, m, 981 "Return type of deprecated type $returnType in ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 982 ) 983 } 984 985 var hiddenClass = findHiddenClasses(returnType, stubImportPackages) 986 if (hiddenClass != null && !hiddenClass.isFromClassPath()) { 987 if (hiddenClass.qualifiedName() == returnType?.asClass()?.qualifiedName()) { 988 // Return type is hidden 989 reporter.report( 990 Issues.UNAVAILABLE_SYMBOL, m, 991 "Method ${cl.qualifiedName()}.${m.name()} returns unavailable " + 992 "type ${hiddenClass.simpleName()}" 993 ) 994 } else { 995 // Return type contains a generic parameter 996 reporter.report( 997 Issues.HIDDEN_TYPE_PARAMETER, m, 998 "Method ${cl.qualifiedName()}.${m.name()} returns unavailable " + 999 "type ${hiddenClass.simpleName()} as a type parameter" 1000 ) 1001 } 1002 } 1003 1004 for (p in m.parameters()) { 1005 val t = p.type() 1006 if (!t.primitive) { 1007 if (!m.deprecated && !cl.deprecated && t.asClass()?.deprecated == true) { 1008 reporter.report( 1009 Issues.REFERENCES_DEPRECATED, m, 1010 "Parameter of deprecated type $t in ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 1011 ) 1012 } 1013 1014 hiddenClass = findHiddenClasses(t, stubImportPackages) 1015 if (hiddenClass != null && !hiddenClass.isFromClassPath()) { 1016 if (hiddenClass.qualifiedName() == t.asClass()?.qualifiedName()) { 1017 // Parameter type is hidden 1018 reporter.report( 1019 Issues.UNAVAILABLE_SYMBOL, m, 1020 "Parameter of unavailable type $t in ${cl.qualifiedName()}.${m.name()}()" 1021 ) 1022 } else { 1023 // Parameter type contains a generic parameter 1024 reporter.report( 1025 Issues.HIDDEN_TYPE_PARAMETER, m, 1026 "Parameter uses type parameter of unavailable type $t in ${cl.qualifiedName()}.${m.name()}()" 1027 ) 1028 } 1029 } 1030 } 1031 } 1032 1033 val t = m.returnType() 1034 if (t != null && !t.primitive && !m.deprecated && !cl.deprecated && t.asClass()?.deprecated == true) { 1035 reporter.report( 1036 Issues.REFERENCES_DEPRECATED, m, 1037 "Returning deprecated type $t from ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 1038 ) 1039 } 1040 } 1041 1042 if (!cl.deprecated) { 1043 val s = cl.superClass() 1044 if (s?.deprecated == true) { 1045 reporter.report( 1046 Issues.EXTENDS_DEPRECATED, cl, 1047 "Extending deprecated super class $s from ${cl.qualifiedName()}: this class should also be deprecated" 1048 ) 1049 } 1050 1051 for (t in cl.interfaceTypes()) { 1052 if (t.asClass()?.deprecated == true) { 1053 reporter.report( 1054 Issues.EXTENDS_DEPRECATED, cl, 1055 "Implementing interface of deprecated type $t in ${cl.qualifiedName()}: this class should also be deprecated" 1056 ) 1057 } 1058 } 1059 } 1060 } else if (cl.deprecated) { 1061 // not hidden, but deprecated 1062 reporter.report(Issues.DEPRECATED, cl, "Class ${cl.qualifiedName()} is deprecated") 1063 } 1064 } 1065 } 1066 1067 private fun cantStripThis( 1068 cl: ClassItem, 1069 filter: Predicate<Item>, 1070 notStrippable: MutableSet<ClassItem>, 1071 stubImportPackages: Set<String>?, 1072 from: Item, 1073 usage: String 1074 ) { 1075 if (stubImportPackages != null && stubImportPackages.contains(cl.containingPackage().qualifiedName())) { 1076 // if the package is imported then it does not need stubbing. 1077 return 1078 } 1079 1080 if (cl.isFromClassPath()) { 1081 return 1082 } 1083 1084 if ((cl.isHiddenOrRemoved() || cl.isPackagePrivate && !cl.checkLevel()) && !cl.isTypeParameter) { 1085 reporter.report( 1086 Issues.REFERENCES_HIDDEN, from, 1087 "Class ${cl.qualifiedName()} is ${if (cl.isHiddenOrRemoved()) "hidden" else "not public"} but was referenced ($usage) from public ${from.describe( 1088 false 1089 )}" 1090 ) 1091 } 1092 1093 if (!notStrippable.add(cl)) { 1094 // slight optimization: if it already contains cl, it already contains 1095 // all of cl's parents 1096 return 1097 } 1098 1099 // cant strip any public fields or their generics 1100 for (field in cl.fields()) { 1101 if (!filter.test(field)) { 1102 continue 1103 } 1104 val fieldType = field.type() 1105 if (!fieldType.primitive) { 1106 val typeClass = fieldType.asClass() 1107 if (typeClass != null) { 1108 cantStripThis(typeClass, filter, notStrippable, stubImportPackages, field, "as field type") 1109 } 1110 for (cls in fieldType.typeArgumentClasses()) { 1111 if (cls == typeClass) { 1112 continue 1113 } 1114 cantStripThis(cls, filter, notStrippable, stubImportPackages, field, "as field type argument class") 1115 } 1116 } 1117 } 1118 // cant strip any of the type's generics 1119 for (cls in cl.typeArgumentClasses()) { 1120 cantStripThis(cls, filter, notStrippable, stubImportPackages, cl, "as type argument") 1121 } 1122 // cant strip any of the annotation elements 1123 // cantStripThis(cl.annotationElements(), notStrippable); 1124 // take care of methods 1125 cantStripThis(cl.methods(), filter, notStrippable, stubImportPackages) 1126 cantStripThis(cl.constructors(), filter, notStrippable, stubImportPackages) 1127 // blow the outer class open if this is an inner class 1128 val containingClass = cl.containingClass() 1129 if (containingClass != null) { 1130 cantStripThis(containingClass, filter, notStrippable, stubImportPackages, cl, "as containing class") 1131 } 1132 // blow open super class and interfaces 1133 // TODO: Consider using val superClass = cl.filteredSuperclass(filter) 1134 val superClass = cl.superClass() 1135 if (superClass != null) { 1136 if (superClass.isHiddenOrRemoved()) { 1137 // cl is a public class declared as extending a hidden superclass. 1138 // this is not a desired practice but it's happened, so we deal 1139 // with it by finding the first super class which passes checkLevel for purposes of 1140 // generating the doc & stub information, and proceeding normally. 1141 if (!superClass.isFromClassPath()) { 1142 reporter.report( 1143 Issues.HIDDEN_SUPERCLASS, cl, "Public class " + cl.qualifiedName() + 1144 " stripped of unavailable superclass " + superClass.qualifiedName() 1145 ) 1146 } 1147 } else { 1148 // doclava would also mark the package private super classes as unhidden, but that's not 1149 // right (this was just done for its stub handling) 1150 // cantStripThis(superClass, filter, notStrippable, stubImportPackages, cl, "as super class") 1151 1152 if (superClass.isPrivate && !superClass.isFromClassPath()) { 1153 reporter.report( 1154 Issues.PRIVATE_SUPERCLASS, cl, "Public class " + 1155 cl.qualifiedName() + " extends private class " + superClass.qualifiedName() 1156 ) 1157 } 1158 } 1159 } 1160 } 1161 1162 private fun cantStripThis( 1163 methods: List<MethodItem>, 1164 filter: Predicate<Item>, 1165 notStrippable: MutableSet<ClassItem>, 1166 stubImportPackages: Set<String>? 1167 ) { 1168 // for each method, blow open the parameters, throws and return types. also blow open their 1169 // generics 1170 for (method in methods) { 1171 if (!filter.test(method)) { 1172 continue 1173 } 1174 for (typeParameterClass in method.typeArgumentClasses()) { 1175 cantStripThis( 1176 typeParameterClass, 1177 filter, 1178 notStrippable, 1179 stubImportPackages, 1180 method, 1181 "as type parameter" 1182 ) 1183 } 1184 for (parameter in method.parameters()) { 1185 for (parameterTypeClass in parameter.type().typeArgumentClasses()) { 1186 cantStripThis( 1187 parameterTypeClass, 1188 filter, 1189 notStrippable, 1190 stubImportPackages, 1191 parameter, 1192 "as parameter type" 1193 ) 1194 for (tcl in parameter.type().typeArgumentClasses()) { 1195 if (tcl == parameterTypeClass) { 1196 continue 1197 } 1198 if (tcl.isHiddenOrRemoved()) { 1199 reporter.report( 1200 Issues.UNAVAILABLE_SYMBOL, method, 1201 "Parameter of hidden type ${tcl.fullName()} " + 1202 "in ${method.containingClass().qualifiedName()}.${method.name()}()" 1203 ) 1204 } else { 1205 cantStripThis( 1206 tcl, 1207 filter, 1208 notStrippable, 1209 stubImportPackages, 1210 parameter, 1211 "as type parameter" 1212 ) 1213 } 1214 } 1215 } 1216 } 1217 for (thrown in method.throwsTypes()) { 1218 cantStripThis(thrown, filter, notStrippable, stubImportPackages, method, "as exception") 1219 } 1220 val returnType = method.returnType() 1221 if (returnType != null && !returnType.primitive) { 1222 val returnTypeClass = returnType.asClass() 1223 if (returnTypeClass != null) { 1224 cantStripThis(returnTypeClass, filter, notStrippable, stubImportPackages, method, "as return type") 1225 for (tyItem in returnType.typeArgumentClasses()) { 1226 if (tyItem == returnTypeClass) { 1227 continue 1228 } 1229 cantStripThis( 1230 tyItem, 1231 filter, 1232 notStrippable, 1233 stubImportPackages, 1234 method, 1235 "as return type parameter" 1236 ) 1237 } 1238 } 1239 } 1240 } 1241 } 1242 1243 /** 1244 * Find references to hidden classes. 1245 * 1246 * This finds hidden classes that are used by public parts of the API in order to ensure the 1247 * API is self consistent and does not reference classes that are not included in 1248 * the stubs. Any such references cause an error to be reported. 1249 * 1250 * A reference to an imported class is not treated as an error, even though imported classes 1251 * are hidden from the stub generation. That is because imported classes are, by definition, 1252 * excluded from the set of classes for which stubs are required. 1253 * 1254 * @param ti the type information to examine for references to hidden classes. 1255 * @param stubImportPackages the possibly null set of imported package names. 1256 * @return a reference to a hidden class or null if there are none 1257 */ 1258 private fun findHiddenClasses(ti: TypeItem?, stubImportPackages: Set<String>?): ClassItem? { 1259 ti ?: return null 1260 val ci = ti.asClass() ?: return null 1261 return findHiddenClasses(ci, stubImportPackages) 1262 } 1263 1264 private fun findHiddenClasses(ci: ClassItem, stubImportPackages: Set<String>?): ClassItem? { 1265 if (stubImportPackages != null && stubImportPackages.contains(ci.containingPackage().qualifiedName())) { 1266 return null 1267 } 1268 if (ci.isHiddenOrRemoved()) return ci 1269 for (tii in ci.toType().typeArgumentClasses()) { 1270 // Avoid infinite recursion in the case of Foo<T extends Foo> 1271 if (tii != ci) { 1272 val hiddenClass = findHiddenClasses(tii, stubImportPackages) 1273 if (hiddenClass != null) return hiddenClass 1274 } 1275 } 1276 return null 1277 } 1278 } 1279