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.model 18 19 @MetalavaApi 20 interface MethodItem : CallableItem, InheritableItem { 21 /** 22 * The property this method is an accessor for; inverse of [PropertyItem.getter] and 23 * [PropertyItem.setter] 24 */ 25 val property: PropertyItem? 26 get() = null 27 28 override val effectivelyDeprecated: Boolean 29 get() = 30 originallyDeprecated || 31 containingClass().effectivelyDeprecated || 32 // Accessors inherit deprecation from their properties. Uses originallyDeprecated to 33 // prevent a cycle because effectivelyDeprecated on the property checks the getter. 34 // Also prevents deprecation from propagating getter -> property -> setter. 35 property?.originallyDeprecated == true 36 37 @Deprecated( 38 message = 39 "There is no point in calling this method on MethodItem as it always returns false", 40 ReplaceWith("") 41 ) 42 override fun isConstructor() = false 43 44 /** Returns true if this method is a Kotlin extension method */ 45 fun isExtensionMethod(): Boolean 46 47 /** Returns the super methods that this method is overriding */ 48 fun superMethods(): List<MethodItem> 49 50 override fun findCorrespondingItemIn( 51 codebase: Codebase, 52 superMethods: Boolean, 53 duplicate: Boolean, 54 ): MethodItem? { 55 val correspondingClassItem = containingClass().findCorrespondingItemIn(codebase) 56 val correspondingMethodItem = 57 correspondingClassItem?.findMethod( 58 this, 59 includeSuperClasses = superMethods, 60 includeInterfaces = superMethods, 61 ) 62 return if ( 63 correspondingMethodItem != null && 64 duplicate && 65 correspondingMethodItem.containingClass() !== correspondingClassItem 66 ) 67 correspondingMethodItem.duplicate(correspondingClassItem) 68 else correspondingMethodItem 69 } 70 71 fun allSuperMethods(): Sequence<MethodItem> { 72 val original = superMethods().firstOrNull() ?: return emptySequence() 73 return generateSequence(original) { item -> 74 val superMethods = item.superMethods() 75 superMethods.firstOrNull() 76 } 77 } 78 79 /** 80 * If this method requires override in the child class to prevent error when compiling the stubs 81 */ 82 @Deprecated("This property should not be accessed directly.") var _requiresOverride: Boolean? 83 84 /** 85 * Duplicates this method item. 86 * 87 * Override to specialize the return type. 88 */ 89 override fun duplicate(targetContainingClass: ClassItem): MethodItem 90 91 fun findPredicateSuperMethod(predicate: FilterPredicate): MethodItem? { 92 val superMethods = superMethods() 93 for (method in superMethods) { 94 if (predicate.test(method)) { 95 return method 96 } 97 } 98 99 for (method in superMethods) { 100 val found = method.findPredicateSuperMethod(predicate) 101 if (found != null) { 102 return found 103 } 104 } 105 106 return null 107 } 108 109 override fun accept(visitor: ItemVisitor) { 110 visitor.visit(this) 111 } 112 113 companion object { 114 /** 115 * Compare two types to see if they are considered the same. 116 * 117 * Same means, functionally equivalent at both compile time and runtime. 118 * 119 * TODO: Compare annotations to see for example whether you've refined the nullness policy; 120 * if so, that should be included 121 */ 122 private fun sameType( 123 t1: TypeItem, 124 t2: TypeItem, 125 addAdditionalOverrides: Boolean, 126 ): Boolean { 127 // Compare the types in two ways. 128 // 1. Using `TypeItem.equals(TypeItem)` which is basically a textual comparison that 129 // ignores type parameter bounds but includes everuthing else that is present in the 130 // string representation of the type apart from white space differences. This is 131 // needed to preserve methods that change annotations, e.g. adding `@NonNull`, which 132 // are significant to the API, and also to preserver legacy behavior to reduce churn 133 // in API signature files. 134 // 2. Comparing their erased types which takes into account type parameter bounds but 135 // ignores annotations and generic types. Comparing erased types will retain more 136 // methods overrides in the signature file so only do it when adding additional 137 // overrides. 138 return t1 == t2 && 139 (!addAdditionalOverrides || t1.toErasedTypeString() == t2.toErasedTypeString()) 140 } 141 142 fun sameSignature( 143 method: MethodItem, 144 superMethod: MethodItem, 145 addAdditionalOverrides: Boolean, 146 ): Boolean { 147 // If the return types differ, override it (e.g. parent implements clone(), 148 // subclass overrides with more specific return type) 149 if ( 150 !sameType( 151 method.returnType(), 152 superMethod.returnType(), 153 addAdditionalOverrides = addAdditionalOverrides 154 ) 155 ) { 156 return false 157 } 158 159 if ( 160 method.effectivelyDeprecated != superMethod.effectivelyDeprecated && 161 !method.effectivelyDeprecated 162 ) { 163 return false 164 } 165 166 // Compare modifier lists; note that here we need to 167 // skip modifiers that don't apply in compat mode if set 168 if (!method.modifiers.equivalentTo(method, superMethod.modifiers)) { 169 return false 170 } 171 172 val parameterList1 = method.parameters() 173 val parameterList2 = superMethod.parameters() 174 175 if (parameterList1.size != parameterList2.size) { 176 return false 177 } 178 179 assert(parameterList1.size == parameterList2.size) 180 for (i in parameterList1.indices) { 181 val p1 = parameterList1[i] 182 val p2 = parameterList2[i] 183 val pt1 = p1.type() 184 val pt2 = p2.type() 185 186 if (!sameType(pt1, pt2, addAdditionalOverrides)) { 187 return false 188 } 189 } 190 191 // Also compare throws lists 192 val throwsList12 = method.throwsTypes() 193 val throwsList2 = superMethod.throwsTypes() 194 195 if (throwsList12.size != throwsList2.size) { 196 return false 197 } 198 199 assert(throwsList12.size == throwsList2.size) 200 for (i in throwsList12.indices) { 201 val p1 = throwsList12[i] 202 val p2 = throwsList2[i] 203 val pt1 = p1.toTypeString() 204 val pt2 = p2.toTypeString() 205 if (pt1 != pt2) { // assumes throws lists are sorted! 206 return false 207 } 208 } 209 210 return true 211 } 212 } 213 214 /** 215 * Check whether this method is a synthetic enum method. 216 * 217 * i.e. `getEntries()` from Kotlin and `values()` and `valueOf(String)` from both Java and 218 * Kotlin. 219 */ 220 fun isEnumSyntheticMethod(): Boolean { 221 if (!containingClass().isEnum()) return false 222 val parameters = parameters() 223 return when (parameters.size) { 224 0 -> name().let { name -> name == JAVA_ENUM_VALUES || name == "getEntries" } 225 1 -> name() == JAVA_ENUM_VALUE_OF && parameters[0].type().isString() 226 else -> false 227 } 228 } 229 230 /** 231 * If annotation method, returns the legacy default value as a source expression. 232 * 233 * This is called `legacy` because this an old, inconsistent representation of the default value 234 * that exposes implementation details. It will be replaced by a properly modelled value 235 * representation. 236 */ 237 fun legacyDefaultValue(): String 238 239 /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */ 240 fun isKotlinProperty(): Boolean = false 241 242 /** 243 * Determines if the method is a method that needs to be overridden in any child classes that 244 * extend this [MethodItem] in order to prevent errors when compiling the stubs or the reverse 245 * dependencies of stubs. 246 * 247 * @return Boolean value indicating whether the method needs to be overridden in the child 248 * classes 249 */ 250 @Suppress("DEPRECATION") 251 private fun requiresOverride(): Boolean { 252 _requiresOverride?.let { 253 return _requiresOverride as Boolean 254 } 255 256 _requiresOverride = computeRequiresOverride() 257 258 return _requiresOverride as Boolean 259 } 260 261 private fun computeRequiresOverride(): Boolean { 262 val isVisible = !hidden || hasShowAnnotation() 263 264 // When the method is a concrete, non-default method, its overriding method is not required 265 // to be shown in the signature file. 266 return if (!modifiers.isAbstract() && !modifiers.isDefault()) { 267 false 268 } else if (superMethods().isEmpty()) { 269 // If the method is abstract and is not overriding any parent methods, 270 // it requires override in the child class if it is visible 271 isVisible 272 } else { 273 // If the method is abstract and is overriding any visible parent methods: 274 // it needs to be overridden if: 275 // - it is visible or 276 // - all super methods are either java.lang.Object method or requires override 277 isVisible || 278 superMethods().all { 279 it.containingClass().isJavaLangObject() || it.requiresOverride() 280 } 281 } 282 } 283 284 private fun getUniqueSuperInterfaceMethods( 285 superInterfaceMethods: List<MethodItem> 286 ): List<MethodItem> { 287 val visitCountMap = mutableMapOf<ClassItem, Int>() 288 289 // perform BFS on all super interfaces of each super interface methods' 290 // containing interface to determine the leaf interface of each unique hierarchy. 291 superInterfaceMethods.forEach { 292 val superInterface = it.containingClass() 293 val queue = mutableListOf(superInterface) 294 while (queue.isNotEmpty()) { 295 val s = queue.removeFirst() 296 visitCountMap[s] = visitCountMap.getOrDefault(s, 0) + 1 297 queue.addAll( 298 s.interfaceTypes().mapNotNull { interfaceType -> interfaceType.asClass() } 299 ) 300 } 301 } 302 303 // If visit count is greater than 1, it means the interface is within the hierarchy of 304 // another method, thus filter out. 305 return superInterfaceMethods.filter { visitCountMap[it.containingClass()]!! == 1 } 306 } 307 308 /** 309 * Determines if the method needs to be added to the signature file in order to prevent errors 310 * when compiling the stubs or the reverse dependencies of the stubs. 311 * 312 * @return Boolean value indicating whether the method needs to be added to the signature file 313 */ 314 fun isRequiredOverridingMethodForTextStub(): Boolean { 315 return (containingClass().isClass() && 316 !modifiers.isAbstract() && 317 superMethods().isNotEmpty() && 318 superMethods().let { 319 if (it.size == 1 && it.first().containingClass().isJavaLangObject()) { 320 // If the method is extending a java.lang.Object method, 321 // it only required override when it is directly (not transitively) overriding 322 // it and the signature differs (e.g. visibility or modifier 323 // changes) 324 !sameSignature( 325 this, 326 it.first(), 327 // This method is only called when add-additional-overrides=yes. 328 addAdditionalOverrides = true, 329 ) 330 } else { 331 // Since a class can extend a single class except Object, 332 // there is only one non-Object super class method at max. 333 val superClassMethods = 334 it.firstOrNull { superMethod -> 335 superMethod.containingClass().isClass() && 336 !superMethod.containingClass().isJavaLangObject() 337 } 338 339 // Assume a class implements two interfaces A and B; 340 // A provides a default super method, and B provides an abstract super method. 341 // In such case, the child method is a required overriding method when: 342 // - A and B do not extend each other or 343 // - A is a super interface of B 344 // On the other hand, the child method is not a required overriding method when: 345 // - B is a super interface of A 346 // Given this, we should make decisions only based on the leaf interface of each 347 // unique hierarchy. 348 val uniqueSuperInterfaceMethods = 349 getUniqueSuperInterfaceMethods( 350 it.filter { superMethod -> superMethod.containingClass().isInterface() } 351 ) 352 353 // If super method is non-null, whether this method is required 354 // is determined by whether the super method requires override. 355 // If super method is null, this method is required if there is a 356 // unique super interface that requires override. 357 superClassMethods?.requiresOverride() 358 ?: uniqueSuperInterfaceMethods.any { s -> s.requiresOverride() } 359 } 360 }) || 361 // To inherit methods with override-equivalent signatures 362 // See https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.4.1.3 363 (containingClass().isInterface() && 364 superMethods().count { it.modifiers.isAbstract() || it.modifiers.isDefault() } > 1) 365 } 366 } 367