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 import com.android.tools.metalava.model.text.TextCodebase 20 import com.android.tools.metalava.model.visitors.ItemVisitor 21 import com.android.tools.metalava.model.visitors.TypeVisitor 22 import java.util.function.Predicate 23 24 interface MethodItem : MemberItem { 25 /** 26 * The property this method is an accessor for; inverse of [PropertyItem.getter] and 27 * [PropertyItem.setter] 28 */ 29 val property: PropertyItem? 30 get() = null 31 32 /** Whether this method is a constructor */ 33 fun isConstructor(): Boolean 34 35 /** The type of this field, or null for constructors */ 36 fun returnType(): TypeItem? 37 38 /** The list of parameters */ 39 fun parameters(): List<ParameterItem> 40 41 /** Returns true if this method is a Kotlin extension method */ 42 fun isExtensionMethod(): Boolean 43 44 /** Returns the super methods that this method is overriding */ 45 fun superMethods(): List<MethodItem> 46 47 override fun type(): TypeItem? = returnType() 48 49 /** Returns the main documentation for the method (the documentation before any tags). */ 50 fun findMainDocumentation(): String 51 52 /** 53 * Like [internalName] but is the desc-portion of the internal signature, 54 * e.g. for the method "void create(int x, int y)" the internal name of 55 * the constructor is "create" and the desc is "(II)V" 56 */ 57 fun internalDesc(voidConstructorTypes: Boolean = false): String { 58 val sb = StringBuilder() 59 sb.append("(") 60 61 // Non-static inner classes get an implicit constructor parameter for the 62 // outer type 63 if (isConstructor() && containingClass().containingClass() != null && 64 !containingClass().modifiers.isStatic() 65 ) { 66 sb.append(containingClass().containingClass()?.toType()?.internalName() ?: "") 67 } 68 69 for (parameter in parameters()) { 70 sb.append(parameter.type().internalName()) 71 } 72 73 sb.append(")") 74 sb.append(if (voidConstructorTypes && isConstructor()) "V" else returnType()?.internalName() ?: "V") 75 return sb.toString() 76 } 77 78 fun allSuperMethods(): Sequence<MethodItem> { 79 val original = superMethods().firstOrNull() ?: return emptySequence() 80 return generateSequence(original) { item -> 81 val superMethods = item.superMethods() 82 superMethods.firstOrNull() 83 } 84 } 85 86 /** Any type parameters for the class, if any, as a source string (with fully qualified class names) */ 87 fun typeParameterList(): TypeParameterList 88 89 /** Returns the classes that are part of the type parameters of this method, if any */ 90 fun typeArgumentClasses(): List<ClassItem> = codebase.unsupported() 91 92 /** Types of exceptions that this method can throw */ 93 fun throwsTypes(): List<ClassItem> 94 95 /** Returns true if this class throws the given exception */ 96 fun throws(qualifiedName: String): Boolean { 97 for (type in throwsTypes()) { 98 if (type.extends(qualifiedName)) { 99 return true 100 } 101 } 102 103 for (type in throwsTypes()) { 104 if (type.qualifiedName() == qualifiedName) { 105 return true 106 } 107 } 108 109 return false 110 } 111 112 fun filteredThrowsTypes(predicate: Predicate<Item>): Collection<ClassItem> { 113 if (throwsTypes().isEmpty()) { 114 return emptyList() 115 } 116 return filteredThrowsTypes(predicate, LinkedHashSet()) 117 } 118 119 private fun filteredThrowsTypes( 120 predicate: Predicate<Item>, 121 classes: LinkedHashSet<ClassItem> 122 ): LinkedHashSet<ClassItem> { 123 124 for (cls in throwsTypes()) { 125 if (predicate.test(cls) || cls.isTypeParameter) { 126 classes.add(cls) 127 } else { 128 // Excluded, but it may have super class throwables that are included; if so, include those 129 var curr = cls.publicSuperClass() 130 while (curr != null) { 131 if (predicate.test(curr)) { 132 classes.add(curr) 133 break 134 } 135 curr = curr.publicSuperClass() 136 } 137 } 138 } 139 return classes 140 } 141 142 /** 143 * If this method is inherited from a hidden super class, but implements a method 144 * from a public interface, this property is set. This is necessary because these 145 * methods should not be listed in signature files (at least not in compatibility mode), 146 * whereas in stub files it's necessary for them to be included (otherwise subclasses 147 * may think the method required and not yet implemented, e.g. the class must be 148 * abstract.) 149 */ 150 var inheritedMethod: Boolean 151 152 /** 153 * If this method is inherited from a super class (typically via [duplicate]) this 154 * field points to the original class it was inherited from 155 */ 156 var inheritedFrom: ClassItem? 157 158 /** 159 * Duplicates this field item. Used when we need to insert inherited fields from 160 * interfaces etc. 161 */ 162 fun duplicate(targetContainingClass: ClassItem): MethodItem 163 164 fun findPredicateSuperMethod(predicate: Predicate<Item>): MethodItem? { 165 if (isConstructor()) { 166 return null 167 } 168 169 val superMethods = superMethods() 170 for (method in superMethods) { 171 if (predicate.test(method)) { 172 return method 173 } 174 } 175 176 for (method in superMethods) { 177 val found = method.findPredicateSuperMethod(predicate) 178 if (found != null) { 179 return found 180 } 181 } 182 183 return null 184 } 185 186 override fun accept(visitor: ItemVisitor) { 187 if (visitor.skip(this)) { 188 return 189 } 190 191 visitor.visitItem(this) 192 if (isConstructor()) { 193 visitor.visitConstructor(this as ConstructorItem) 194 } else { 195 visitor.visitMethod(this) 196 } 197 198 for (parameter in parameters()) { 199 parameter.accept(visitor) 200 } 201 202 if (isConstructor()) { 203 visitor.afterVisitConstructor(this as ConstructorItem) 204 } else { 205 visitor.afterVisitMethod(this) 206 } 207 visitor.afterVisitItem(this) 208 } 209 210 override fun acceptTypes(visitor: TypeVisitor) { 211 if (visitor.skip(this)) { 212 return 213 } 214 215 if (!isConstructor()) { 216 val type = returnType() 217 if (type != null) { // always true when not a constructor 218 visitor.visitType(type, this) 219 } 220 } 221 222 for (parameter in parameters()) { 223 parameter.acceptTypes(visitor) 224 } 225 226 for (exception in throwsTypes()) { 227 exception.acceptTypes(visitor) 228 } 229 230 if (!isConstructor()) { 231 val type = returnType() 232 if (type != null) { 233 visitor.visitType(type, this) 234 } 235 } 236 } 237 238 companion object { 239 private fun compareMethods(o1: MethodItem, o2: MethodItem): Int { 240 val name1 = o1.name() 241 val name2 = o2.name() 242 if (name1 == name2) { 243 val rankDelta = o1.sortingRank - o2.sortingRank 244 if (rankDelta != 0) { 245 return rankDelta 246 } 247 248 // Compare by the rest of the signature to ensure stable output (we don't need to sort 249 // by return value or modifiers or modifiers or throws-lists since methods can't be overloaded 250 // by just those attributes 251 val p1 = o1.parameters() 252 val p2 = o2.parameters() 253 val p1n = p1.size 254 val p2n = p2.size 255 for (i in 0 until minOf(p1n, p2n)) { 256 val compareTypes = 257 p1[i].type().toTypeString() 258 .compareTo(p2[i].type().toTypeString(), ignoreCase = true) 259 if (compareTypes != 0) { 260 return compareTypes 261 } 262 // (Don't compare names; they're not part of the signatures) 263 } 264 return p1n.compareTo(p2n) 265 } 266 267 return name1.compareTo(name2) 268 } 269 270 val comparator: Comparator<MethodItem> = Comparator { o1, o2 -> compareMethods(o1, o2) } 271 val sourceOrderComparator: Comparator<MethodItem> = Comparator { o1, o2 -> 272 val delta = o1.sortingRank - o2.sortingRank 273 if (delta == 0) { 274 // Within a source file all the items will have unique sorting ranks, but since 275 // we copy methods in from hidden super classes it's possible for ranks to clash, 276 // and in that case we'll revert to a signature based comparison 277 comparator.compare(o1, o2) 278 } else { 279 delta 280 } 281 } 282 283 fun sameSignature(method: MethodItem, superMethod: MethodItem, compareRawTypes: Boolean = false): Boolean { 284 // If the return types differ, override it (e.g. parent implements clone(), 285 // subclass overrides with more specific return type) 286 if (method.returnType() != superMethod.returnType()) { 287 return false 288 } 289 290 if (method.deprecated != superMethod.deprecated && !method.deprecated) { 291 return false 292 } 293 294 // Compare modifier lists; note that here we need to 295 // skip modifiers that don't apply in compat mode if set 296 if (!method.modifiers.equivalentTo(superMethod.modifiers)) { 297 return false 298 } 299 300 val parameterList1 = method.parameters() 301 val parameterList2 = superMethod.parameters() 302 303 if (parameterList1.size != parameterList2.size) { 304 return false 305 } 306 307 assert(parameterList1.size == parameterList2.size) 308 for (i in parameterList1.indices) { 309 val p1 = parameterList1[i] 310 val p2 = parameterList2[i] 311 val pt1 = p1.type() 312 val pt2 = p2.type() 313 314 if (compareRawTypes) { 315 if (pt1.toErasedTypeString() != pt2.toErasedTypeString()) { 316 return false 317 } 318 } else { 319 if (pt1 != pt2) { 320 return false 321 } 322 } 323 324 // TODO: Compare annotations to see for example whether 325 // you've refined the nullness policy; if so, that should be included 326 } 327 328 // Also compare throws lists 329 val throwsList12 = method.throwsTypes() 330 val throwsList2 = superMethod.throwsTypes() 331 332 if (throwsList12.size != throwsList2.size) { 333 return false 334 } 335 336 assert(throwsList12.size == throwsList2.size) 337 for (i in throwsList12.indices) { 338 val p1 = throwsList12[i] 339 val p2 = throwsList2[i] 340 val pt1 = p1.qualifiedName() 341 val pt2 = p2.qualifiedName() 342 if (pt1 != pt2) { // assumes throws lists are sorted! 343 return false 344 } 345 } 346 347 return true 348 } 349 } 350 351 fun formatParameters(): String? { 352 // TODO: Generalize, allow callers to control whether to include annotations, whether to erase types, 353 // whether to include names, etc 354 if (parameters().isEmpty()) { 355 return "" 356 } 357 val sb = StringBuilder() 358 for (parameter in parameters()) { 359 if (sb.isNotEmpty()) { 360 sb.append(", ") 361 } 362 sb.append(parameter.type().toTypeString()) 363 } 364 365 return sb.toString() 366 } 367 368 override fun requiresNullnessInfo(): Boolean { 369 return when { 370 modifiers.hasJvmSyntheticAnnotation() -> false 371 isConstructor() -> false 372 (returnType()?.primitive != true) -> true 373 parameters().any { !it.type().primitive } -> true 374 else -> false 375 } 376 } 377 378 override fun hasNullnessInfo(): Boolean { 379 if (!requiresNullnessInfo()) { 380 return true 381 } 382 383 if (!isConstructor() && returnType()?.primitive != true) { 384 if (!modifiers.hasNullnessInfo()) { 385 return false 386 } 387 } 388 389 @Suppress("LoopToCallChain") // The quickfix is wrong! (covered by AnnotationStatisticsTest) 390 for (parameter in parameters()) { 391 if (!parameter.hasNullnessInfo()) { 392 return false 393 } 394 } 395 396 return true 397 } 398 399 fun isImplicitConstructor(): Boolean { 400 return isConstructor() && modifiers.isPublic() && parameters().isEmpty() 401 } 402 403 /** Finds uncaught exceptions actually thrown inside this method (as opposed to ones 404 * declared in the signature) */ 405 fun findThrownExceptions(): Set<ClassItem> = codebase.unsupported() 406 407 /** If annotation method, returns the default value as a source expression */ 408 fun defaultValue(): String = "" 409 410 fun hasDefaultValue(): Boolean { 411 return defaultValue() != "" 412 } 413 414 /** 415 * Returns true if this method is a signature match for the given method (e.g. can 416 * be overriding). This checks that the name and parameter lists match, but ignores 417 * differences in parameter names, return value types and throws list types. 418 */ 419 fun matches(other: MethodItem): Boolean { 420 if (this === other) return true 421 422 if (name() != other.name()) { 423 return false 424 } 425 426 val parameters1 = parameters() 427 val parameters2 = other.parameters() 428 429 if (parameters1.size != parameters2.size) { 430 return false 431 } 432 433 for (i in parameters1.indices) { 434 val parameter1 = parameters1[i] 435 val parameter2 = parameters2[i] 436 val typeString1 = parameter1.type().toString() 437 val typeString2 = parameter2.type().toString() 438 if (typeString1 == typeString2) { 439 continue 440 } 441 val type1 = parameter1.type().toErasedTypeString(this) 442 val type2 = parameter2.type().toErasedTypeString(other) 443 444 if (type1 != type2) { 445 // Workaround for signature-based codebase, where we can't always resolve generic 446 // parameters: if we see a mismatch here which looks like a failure to erase say T into 447 // java.lang.Object, don't treat that as a mismatch. (Similar common case: T[] and Object[]) 448 if (typeString1[0].isUpperCase() && typeString1.length == 1 && 449 parameter1.codebase is TextCodebase 450 ) { 451 continue 452 } 453 if (typeString2.length >= 2 && !typeString2[1].isLetterOrDigit() && 454 parameter1.codebase is TextCodebase 455 ) { 456 continue 457 } 458 return false 459 } 460 } 461 return true 462 } 463 464 /** Returns whether this method has any types in its signature that does not match the given filter */ 465 fun hasHiddenType(filterReference: Predicate<Item>): Boolean { 466 for (parameter in parameters()) { 467 val type = parameter.type() 468 if (type.hasTypeArguments()) { 469 for (argument in type.typeArgumentClasses()) { 470 if (!filterReference.test(argument)) { 471 return true 472 } 473 } 474 } 475 val clz = type.asClass() ?: continue 476 if (!filterReference.test(clz)) { 477 return true 478 } 479 } 480 481 val returnType = returnType() 482 if (returnType != null) { 483 val returnTypeClass = returnType.asClass() 484 if (returnTypeClass != null && !filterReference.test(returnTypeClass)) { 485 return true 486 } 487 if (returnType.hasTypeArguments()) { 488 for (argument in returnType.typeArgumentClasses()) { 489 if (!filterReference.test(argument)) { 490 return true 491 } 492 } 493 } 494 } 495 496 if (typeParameterList().typeParameterCount() > 0) { 497 for (argument in typeArgumentClasses()) { 498 if (!filterReference.test(argument)) { 499 return true 500 } 501 } 502 } 503 504 return false 505 } 506 507 override fun hasShowAnnotationInherited(): Boolean { 508 if (super.hasShowAnnotationInherited()) { 509 return true 510 } 511 return superMethods().any { 512 it.hasShowAnnotationInherited() 513 } 514 } 515 516 override fun onlyShowForStubPurposesInherited(): Boolean { 517 if (super.onlyShowForStubPurposesInherited()) { 518 return true 519 } 520 return superMethods().any { 521 it.onlyShowForStubPurposesInherited() 522 } 523 } 524 525 /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */ 526 fun isKotlinProperty(): Boolean = false 527 528 /** Returns true if this is a synthetic enum method */ 529 fun isEnumSyntheticMethod(): Boolean { 530 return containingClass().isEnum() && 531 ( 532 name() == "values" && parameters().isEmpty() || 533 name() == "valueOf" && parameters().size == 1 && 534 parameters()[0].type().isString() 535 ) 536 } 537 } 538