1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.model 18 19 import com.android.tools.metalava.reporter.Issues 20 21 /** A factory that will create an [ApiVariantSelectors] for a specific [Item]. */ 22 typealias ApiVariantSelectorsFactory = (Item) -> ApiVariantSelectors 23 24 /** Contains properties that select which, if any, variant of an API an [Item] belongs in. */ 25 sealed class ApiVariantSelectors { 26 /** 27 * Indicates whether the item was explicitly hidden in the source, e.g. via an `@hide` javadoc 28 * tag in its [Item.documentation], or a hide annotation directly on the [Item]. 29 */ 30 abstract val originallyHidden: Boolean 31 32 /** 33 * Indicates whether children of an [Item] should be hidden, i.e. should not be included in ANY 34 * API surface variant. 35 * 36 * Initially set to [originallyHidden] but updated due to inheritance. 37 */ 38 internal abstract val inheritableHidden: Boolean 39 40 /** 41 * Indicates whether the [Item] is accessible, and its enclosing classes are accessible. 42 * 43 * An [Item] is accessible if it is either `public` or `protected`. In Kotlin it is also 44 * accessible if it is `internal` as long as it is annotated with `@PublishedApi`. However, that 45 * annotation is not treated specially in Metalava, instead it relies on the user to specify 46 * `@PublishedApi` as a show annotation and this just assumes that any show annotation is enough 47 * to make it accessible. 48 */ 49 abstract val accessible: Boolean 50 51 /** 52 * Indicates whether the [Item] should be hidden, i.e. should not be included in ANY API surface 53 * variant. 54 * 55 * Initially set to [inheritableHidden] but updated due to show annotations. 56 */ 57 abstract val hidden: Boolean 58 59 /** 60 * Indicates whether the [Item] should be included in the doc only API surface variant. 61 * 62 * Initially set to `true` if the [Item.documentation] contains `@doconly` but updated due to 63 * inheritance. 64 */ 65 abstract val docOnly: Boolean 66 67 /** 68 * Indicates whether the [Item] should be in the removed API surface variant. 69 * 70 * Initially set to `true` if the [Item.documentation] contains `@removed` but updated due to 71 * inheritance. 72 */ 73 abstract val removed: Boolean 74 75 /** Determines whether this item will be shown as part of the API or not. */ 76 abstract val showability: Showability 77 78 /** Create a duplicate of this for the specified [Item]. */ 79 abstract fun duplicate(item: Item): ApiVariantSelectors 80 81 /** 82 * Update the mutable properties of this by inheriting state from the parent selectors, if 83 * available. 84 */ 85 abstract fun inheritInto() 86 87 companion object { 88 /** 89 * An [ApiVariantSelectors] factory that will always return an immutable 90 * [ApiVariantSelectors]. It will return `false` for all the properties and throw an error 91 * on any attempt to set a property. 92 */ 93 val IMMUTABLE_FACTORY: ApiVariantSelectorsFactory = { Immutable } 94 95 /** 96 * An [ApiVariantSelectors] factory that will return a new, mutable, [ApiVariantSelectors] 97 * for each [SelectableItem]. 98 * 99 * This cannot be used on an [Item] that is not a [SelectableItem], use [IMMUTABLE_FACTORY] 100 * instead. 101 */ 102 val MUTABLE_FACTORY: ApiVariantSelectorsFactory = { 103 if (it is SelectableItem) Mutable(it) 104 else error("Cannot create Mutable for non-SelectableItem, use Immutable instead") 105 } 106 } 107 108 /** 109 * An immutable [ApiVariantSelectors] that will return `false` for all the properties and fail 110 * on any attempt to set the `var` properties. 111 */ 112 @Suppress("ConvertObjectToDataObject") // Requires language level 1.9 113 private object Immutable : ApiVariantSelectors() { 114 115 override val originallyHidden: Boolean 116 get() = false 117 118 override val inheritableHidden: Boolean 119 get() = false 120 121 /** 122 * Defaults to `true` as this is used by `Item`s loaded from an API signature file which 123 * typically only contains accessible `Item`s. It is possible that it could contain 124 * inaccessible `Item`s but at the moment that is not supported. 125 */ 126 override val accessible: Boolean 127 get() = true 128 129 override val hidden: Boolean 130 get() = false 131 132 override val docOnly: Boolean 133 get() = false 134 135 override var removed: Boolean 136 get() = false 137 set(value) { 138 error("Cannot set `removed` to $value") 139 } 140 141 override val showability: Showability 142 get() = Showability.NO_EFFECT 143 144 override fun duplicate(item: Item): ApiVariantSelectors = this 145 146 override fun inheritInto() = error("Cannot inheritInto() $this") 147 148 override fun toString() = "Immutable" 149 } 150 151 /** 152 * A mutable [ApiVariantSelectors]. 153 * 154 * [originallyHidden] will be `true` if it's [item]'s documentation contains one of `@hide`, 155 * `@pending` or `@suppress` or its [SelectableItem] has a hide annotation associated with it. 156 * 157 * Unless [hidden] is written before reading then it will default to `true` if 158 * [originallyHidden] is `true` and it does not have any show annotations. 159 * 160 * [docOnly] will be initialized to `true` if it's [item]'s documentation contains `@doconly`. 161 * 162 * [removed] will be initialized to `true` if it's [item]'s documentation contains `@removed`. 163 * 164 * This uses bits in [propertyHasBeenSetBits] and [propertyValueBits] to handle lazy 165 * initialization and store the value. The main purpose of using bit masks is not primarily 166 * performance or to reduce storage (although keeping that down is a factor) but rather to 167 * support lazy initialization with optional setters without duplicating lots of complicated 168 * code. 169 */ 170 private class Mutable(private val item: SelectableItem) : ApiVariantSelectors() { 171 172 /** 173 * Contains a bit for each lazy boolean property indicating whether it has been set, either 174 * implicitly during initialization or explicitly via its setter. 175 * 176 * If a bit is set in here then the corresponding bit in [propertyValueBits] contains the 177 * value. 178 */ 179 private var propertyHasBeenSetBits = 0 180 181 /** 182 * Contains a bit for each lazy boolean property indicating its value. 183 * 184 * A bit in here represents the value of the property if and only if the corresponding bit 185 * has been set in [propertyHasBeenSetBits]. Otherwise, the value of the bit is undefined. 186 */ 187 private var propertyValueBits = 0 188 189 /** 190 * Get the value of a property from [propertyValueBits], initializing it if it has not yet 191 * been set. 192 * 193 * @param propertyBitMask the bit mask in [propertyHasBeenSetBits] and [propertyValueBits] 194 * which indicates whether the associated property's value has been set and if so what its 195 * value is. 196 * @param initialValueProvider a lambda which returns the initial value of the property if 197 * it has not yet been set. 198 */ 199 private inline fun lazyGet(propertyBitMask: Int, initialValueProvider: () -> Boolean) = 200 // Check to see if the property has been set first before accessing the value. 201 if ((propertyHasBeenSetBits and propertyBitMask) == 0) { 202 // The property has not been set so get the initial value and store it. 203 val result = initialValueProvider() 204 // Record that the property has been set. 205 propertyHasBeenSetBits = propertyHasBeenSetBits or propertyBitMask 206 // Record the value. 207 if (result) propertyValueBits = propertyValueBits or propertyBitMask 208 // Return the result. 209 result 210 } else { 211 // The property has been set so get its value. 212 (propertyValueBits and propertyBitMask) != 0 213 } 214 215 /** 216 * Like [lazyGet] except that if the flag is not set it will invoke [inheritInto] if it has 217 * not already been called. It will then check to see if the property has been set and if it 218 * has then the value will be returned. Otherwise, it will invoke the [initialValueProvider] 219 * just as [lazyGet] does. 220 */ 221 private inline fun lazyGetAfterInherit( 222 propertyBitMask: Int, 223 initialValueProvider: () -> Boolean 224 ): Boolean { 225 if ((propertyHasBeenSetBits and propertyBitMask) == 0) { 226 // The property has not been set so first call `inheritInto()` to give it a chance 227 // to initialize the property. It will return immediately if it had nothing to do. 228 inheritInto() 229 230 // At this point inheritInfo may have been called and may have set the property, 231 // but it also may not so check again and if it has not then set it to its initial 232 // value. 233 return lazyGet(propertyBitMask, initialValueProvider) 234 } else { 235 // The property has been set so return its value. 236 return (propertyValueBits and propertyBitMask) != 0 237 } 238 } 239 240 /** 241 * Set the value of a property in [propertyValueBits], skipping initializing it that has not 242 * already been done. 243 * 244 * @param propertyBitMask the bit mask in [propertyHasBeenSetBits] and [propertyValueBits] 245 * which indicates whether the associated property's value has been set and if so what its 246 * value is. 247 * @param value the new value of the property. 248 */ 249 private fun lazySet(propertyBitMask: Int, value: Boolean) { 250 // Record that the property has been set. 251 propertyHasBeenSetBits = propertyHasBeenSetBits or propertyBitMask 252 if (value) { 253 // The value is true so set the bit. 254 propertyValueBits = propertyValueBits or propertyBitMask 255 } else { 256 // The value is false so clear the bit. 257 propertyValueBits = propertyValueBits and propertyBitMask.inv() 258 } 259 } 260 261 override val originallyHidden: Boolean 262 get() = 263 lazyGet(ORIGINALLY_HIDDEN_BIT_MASK) { 264 // The item is originally hidden if the javadoc contains @hide or similar, or 265 // it is tagged with a hide annotation. That is true even if the hide annotation 266 // is superseded by a show annotation. 267 item.documentation.isHidden || item.hasHideAnnotation() 268 } 269 270 override var inheritableHidden: Boolean 271 get() = 272 lazyGetAfterInherit(INHERITABLE_HIDDEN_BIT_MASK) { 273 // By default, i.e. if the property has not been set, the contents of this item 274 // will be hidden if this item was originally hidden and this item did not have 275 // a show annotation that applies recursively to its contents. Otherwise, the 276 // item's contents will be visible. 277 originallyHidden && !showability.showRecursive() 278 } 279 set(value) { 280 lazySet(INHERITABLE_HIDDEN_BIT_MASK, value) 281 } 282 283 override val accessible: Boolean 284 get() = 285 lazyGet(ACCESSIBLE_BIT_MASK) { 286 when (item) { 287 // Packages are always accessible. 288 is PackageItem -> true 289 else -> 290 // This is accessible if it is public, protected or internal (with show 291 // annotation) and none of its containing classes, if any, are 292 // inaccessible. 293 (item.isPublic || 294 item.isProtected || 295 (item.isInternal && showability.show())) && 296 item.containingClass()?.variantSelectors?.accessible != false 297 } 298 } 299 300 override var hidden: Boolean 301 get() = 302 lazyGetAfterInherit(HIDDEN_BIT_MASK) { 303 // By default, i.e. if the property has not been set, this item will be hidden 304 // if it inherits hidden from its parent (or was originally hidden) and this 305 // item does not have a show annotation of any sort. Otherwise, this item is 306 // visible. 307 inheritableHidden && !showability.show() 308 } 309 set(value) { 310 lazySet(HIDDEN_BIT_MASK, value) 311 } 312 313 override val docOnly: Boolean 314 get() = 315 lazyGet(DOCONLY_BIT_MASK) { 316 (item.parent()?.variantSelectors?.docOnly == true) || 317 item.documentation.isDocOnly 318 } 319 320 override var removed: Boolean 321 get() = 322 lazyGet(REMOVED_BIT_MASK) { 323 (item.parent()?.variantSelectors?.removed == true) || 324 item.documentation.isRemoved 325 } 326 // This is only used for testing. 327 set(value) { 328 lazySet(REMOVED_BIT_MASK, value) 329 } 330 331 /** Cache of [showability]. */ 332 internal var _showability: Showability? = null 333 334 override val showability: Showability 335 get() = 336 _showability 337 ?: let { 338 _showability = item.codebase.annotationManager.getShowabilityForItem(item) 339 _showability!! 340 } 341 342 override fun duplicate(item: Item): ApiVariantSelectors = Mutable(item as SelectableItem) 343 344 /** 345 * Records whether [inheritInto] was called as it must only be called once. 346 * 347 * This uses [lazyGet] and [lazySet] to be consistent with other properties and makes it 348 * easy to include the information in the [toString] result. 349 */ 350 internal var inheritIntoWasCalled 351 get() = lazyGet(INHERIT_INTO_BIT_MASK) { false } 352 set(value) { 353 lazySet(INHERIT_INTO_BIT_MASK, value) 354 } 355 356 override fun inheritInto() { 357 // This must only be called once. 358 if (inheritIntoWasCalled) return 359 inheritIntoWasCalled = true 360 361 // PackageItem behaves quite differently to the other Item types so do it first. 362 if (item is PackageItem) { 363 showability.let { showability -> 364 when { 365 showability.show() -> inheritableHidden = false 366 showability.hide() -> inheritableHidden = true 367 } 368 } 369 val containingPackageSelectors = 370 item.containingPackage()?.variantSelectors ?: return 371 if (containingPackageSelectors.inheritableHidden) { 372 inheritableHidden = true 373 } 374 return 375 } 376 377 // Inheritance is only done on a few Item types, ignore the rest. 378 if (item !is ClassItem && item !is CallableItem && item !is FieldItem) return 379 380 if (item is ClassItem) { 381 // Workaround: we're pulling in .aidl files from .jar files. These are 382 // marked @hide, but since we only see the .class files we don't know that. 383 if ( 384 item.simpleName().startsWith("I") && 385 item.origin == ClassOrigin.CLASS_PATH && 386 item.interfaceTypes().any { it.qualifiedName == "android.os.IInterface" } 387 ) { 388 hidden = true 389 return 390 } 391 } 392 393 if (showability.show()) { 394 // If the showability is recursive then set inheritableHidden to false, that will 395 // unhide any contents of this item too, unless they hide themselves. 396 if (showability.showRecursive()) { 397 inheritableHidden = false 398 } 399 // Whether the showability is recursive or not a show annotation of any sort will 400 // always unhide this item. 401 hidden = false 402 403 if (item is ClassItem) { 404 // Make containing package non-hidden if it contains a show-annotation class. 405 val containingPackageSelectors = item.containingPackage().variantSelectors 406 // Only unhide the package, do not affect anything that might inherit from that 407 // package. 408 (containingPackageSelectors as Mutable).hidden = false 409 } 410 411 if (item.containingClass() != null) { 412 ensureParentVisible() 413 } 414 } else if (showability.hide()) { 415 inheritableHidden = true 416 } else { 417 val containingClassSelectors = item.containingClass()?.variantSelectors 418 if (containingClassSelectors != null) { 419 if (item is FieldItem) { 420 if ( 421 containingClassSelectors.originallyHidden && 422 containingClassSelectors.showability.showNonRecursive() 423 ) { 424 // This is a member in a class that was hidden but then unhidden; but it 425 // was 426 // unhidden by a non-recursive (single) show annotation, so don't 427 // inherit 428 // the show annotation into this item. 429 inheritableHidden = true 430 } 431 } else if (containingClassSelectors.inheritableHidden) { 432 inheritableHidden = true 433 } 434 } else if (item is ClassItem) { 435 // This will only be executed for top level classes, i.e. containing class is 436 // null. They inherit their properties from the containing package. 437 val containingPackageSelectors = item.containingPackage().variantSelectors 438 if (containingPackageSelectors.inheritableHidden) { 439 inheritableHidden = true 440 } 441 } 442 } 443 } 444 445 /** 446 * Ensure that the parents of a visible [Item], i.e. one whose [Item.hidden] property is 447 * `false` are themselves visible. 448 * 449 * Note: This will only be called when [item] is a class, constructor, method or field. In 450 * particular, it does not apply to [PackageItem]s as they are completely separate from one 451 * another, i.e. you do not need to have package `abc.qrs` be visible in order to have 452 * `abc.qrs.xyz` be visible. 453 */ 454 private fun ensureParentVisible() { 455 val parent = item.parent() ?: return 456 457 // If the parent is not hidden then everything is fine. 458 if (!parent.hidden) { 459 return 460 } 461 462 // Otherwise, find a show annotation to blame it on and report the issue. 463 item.modifiers.findAnnotation(AnnotationItem::isShowAnnotation)?.let { 464 violatingAnnotation -> 465 item.codebase.reporter.report( 466 Issues.SHOWING_MEMBER_IN_HIDDEN_CLASS, 467 item, 468 "Attempting to unhide ${item.describe()}, but surrounding ${parent.describe()} is " + 469 "hidden and should also be annotated with $violatingAnnotation" 470 ) 471 } 472 } 473 474 override fun toString(): String { 475 return buildString { 476 append(item.describe()) 477 append(" {\n") 478 for ((bitPosition, propertyName) in propertyNamePerBit.withIndex()) { 479 val bitMask = 1 shl bitPosition 480 append(" ") 481 append(propertyName) 482 append("=") 483 if ((propertyHasBeenSetBits and bitMask) == 0) { 484 append("<not-set>") 485 } else if ((propertyValueBits and bitMask) == 0) { 486 append("false") 487 } else { 488 append("true") 489 } 490 append(",\n") 491 } 492 append(" showability=") 493 if (_showability == null) { 494 append("<not-set>") 495 } else { 496 append(_showability) 497 } 498 append(",\n") 499 append("}") 500 } 501 } 502 503 override fun equals(other: Any?): Boolean { 504 if (this === other) return true 505 if (other !is Mutable) return false 506 507 if (item != other.item) return false 508 if (propertyHasBeenSetBits != other.propertyHasBeenSetBits) return false 509 if (propertyValueBits != other.propertyValueBits) return false 510 if (_showability != other._showability) return false 511 512 return true 513 } 514 515 override fun hashCode(): Int { 516 var result = item.hashCode() 517 result = 31 * result + propertyHasBeenSetBits 518 result = 31 * result + propertyValueBits 519 result = 31 * result + _showability.hashCode() 520 return result 521 } 522 523 companion object { 524 // `originallyHidden` related constants 525 private const val ORIGINALLY_HIDDEN_BIT_POSITION: Int = 0 526 private const val ORIGINALLY_HIDDEN_BIT_MASK: Int = 1 shl ORIGINALLY_HIDDEN_BIT_POSITION 527 528 // `inheritableHidden` related constants 529 private const val INHERITABLE_HIDDEN_BIT_POSITION: Int = 530 ORIGINALLY_HIDDEN_BIT_POSITION + 1 531 private const val INHERITABLE_HIDDEN_BIT_MASK: Int = 532 1 shl INHERITABLE_HIDDEN_BIT_POSITION 533 534 // `hidden` related constants 535 private const val HIDDEN_BIT_POSITION: Int = INHERITABLE_HIDDEN_BIT_POSITION + 1 536 private const val HIDDEN_BIT_MASK: Int = 1 shl HIDDEN_BIT_POSITION 537 538 // `accessible` related constants 539 private const val ACCESSIBLE_BIT_POSITION: Int = HIDDEN_BIT_POSITION + 1 540 private const val ACCESSIBLE_BIT_MASK: Int = 1 shl ACCESSIBLE_BIT_POSITION 541 542 // `docOnly` related constants 543 private const val DOCONLY_BIT_POSITION: Int = ACCESSIBLE_BIT_POSITION + 1 544 private const val DOCONLY_BIT_MASK: Int = 1 shl DOCONLY_BIT_POSITION 545 546 // `removed` related constants 547 private const val REMOVED_BIT_POSITION: Int = DOCONLY_BIT_POSITION + 1 548 private const val REMOVED_BIT_MASK: Int = 1 shl REMOVED_BIT_POSITION 549 550 /** 551 * Bit mask in [propertyHasBeenSetBits] that indicates whether [inheritInto] has been 552 * called. 553 */ 554 private const val INHERIT_INTO_BIT_POSITION = REMOVED_BIT_POSITION + 1 555 private const val INHERIT_INTO_BIT_MASK = 1 shl INHERIT_INTO_BIT_POSITION 556 557 /** The count of the number of bits used. */ 558 private const val COUNT_BITS_USED = INHERIT_INTO_BIT_POSITION + 1 559 560 /** Map from bit to the associated property name, used in toString() */ 561 private val propertyNamePerBit = 562 Array(COUNT_BITS_USED) { "" } 563 .also { array -> 564 array[ORIGINALLY_HIDDEN_BIT_POSITION] = "originallyHidden" 565 array[INHERITABLE_HIDDEN_BIT_POSITION] = "inheritableHidden" 566 array[HIDDEN_BIT_POSITION] = "hidden" 567 array[ACCESSIBLE_BIT_POSITION] = "accessible" 568 array[DOCONLY_BIT_POSITION] = "docOnly" 569 array[REMOVED_BIT_POSITION] = "removed" 570 array[INHERIT_INTO_BIT_POSITION] = "inheritIntoWasCalled" 571 } 572 } 573 } 574 575 /** 576 * Encapsulates the expected state of a [Mutable] instance. 577 * 578 * A data class was chosen for this because the nature of the [Mutable] class is such that 579 * generally, once a property has been set it is not changed (not strictly true for packages). 580 * Tests will typically, test the state, make a change (e.g. get the value of a property), check 581 * the new state and so on. The [copy] method generated for data classes makes it easy to 582 * incrementally modify the state without having to repeat all the previous changes. 583 * 584 * For `var` properties in [Mutable] each corresponding optional parameter will have no effect 585 * if `null` but otherwise will be used to set the corresponding `var` property in the returned 586 * object. 587 * 588 * The `val` properties like [originallyHidden] cannot be set to a specific value. So, all that 589 * this can do is force it to be initialized. That means that the [ApiVariantSelectors] returned 590 * from [createSelectorsforTesting] will only verify whether it is set or not-set as expected. 591 * It cannot test if the value is expected. That will need to be done by the caller. 592 */ 593 data class TestableSelectorsState( 594 val item: SelectableItem, 595 val originallyHidden: Boolean? = null, 596 val inheritIntoWasCalled: Boolean = false, 597 val inheritableHidden: Boolean? = null, 598 val hidden: Boolean? = null, 599 val docOnly: Boolean? = null, 600 val removed: Boolean? = null, 601 val showability: Showability? = null, 602 ) { 603 604 /** 605 * Create a [Mutable] instance whose state matches this that can be used as the expected 606 * state in a test. 607 */ 608 fun createSelectorsforTesting(): ApiVariantSelectors = 609 Mutable(item).also { selectors -> 610 // If originally hidden is set then force it to be initialized. 611 originallyHidden?.let { 612 // It is expected to be set so force it to be initialized. 613 selectors.originallyHidden 614 } 615 if (inheritIntoWasCalled) selectors.inheritIntoWasCalled = true 616 inheritableHidden?.let { selectors.inheritableHidden = it } 617 hidden?.let { selectors.hidden = it } 618 docOnly?.let { 619 // It is expected to be set so force it to be initialized. 620 selectors.docOnly 621 } 622 removed?.let { selectors.removed = it } 623 showability?.let { selectors._showability = it } 624 } 625 } 626 } 627