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. Returns the containing class 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()) 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 visitor.visitType(type, this) 218 } 219 220 for (parameter in parameters()) { 221 parameter.acceptTypes(visitor) 222 } 223 224 for (exception in throwsTypes()) { 225 exception.acceptTypes(visitor) 226 } 227 228 if (!isConstructor()) { 229 val type = returnType() 230 visitor.visitType(type, this) 231 } 232 } 233 234 companion object { 235 private fun compareMethods(o1: MethodItem, o2: MethodItem): Int { 236 val name1 = o1.name() 237 val name2 = o2.name() 238 if (name1 == name2) { 239 val rankDelta = o1.sortingRank - o2.sortingRank 240 if (rankDelta != 0) { 241 return rankDelta 242 } 243 244 // Compare by the rest of the signature to ensure stable output (we don't need to sort 245 // by return value or modifiers or modifiers or throws-lists since methods can't be overloaded 246 // by just those attributes 247 val p1 = o1.parameters() 248 val p2 = o2.parameters() 249 val p1n = p1.size 250 val p2n = p2.size 251 for (i in 0 until minOf(p1n, p2n)) { 252 val compareTypes = 253 p1[i].type().toTypeString() 254 .compareTo(p2[i].type().toTypeString(), ignoreCase = true) 255 if (compareTypes != 0) { 256 return compareTypes 257 } 258 // (Don't compare names; they're not part of the signatures) 259 } 260 return p1n.compareTo(p2n) 261 } 262 263 return name1.compareTo(name2) 264 } 265 266 val comparator: Comparator<MethodItem> = Comparator { o1, o2 -> compareMethods(o1, o2) } 267 val sourceOrderComparator: Comparator<MethodItem> = Comparator { o1, o2 -> 268 val delta = o1.sortingRank - o2.sortingRank 269 if (delta == 0) { 270 // Within a source file all the items will have unique sorting ranks, but since 271 // we copy methods in from hidden super classes it's possible for ranks to clash, 272 // and in that case we'll revert to a signature based comparison 273 comparator.compare(o1, o2) 274 } else { 275 delta 276 } 277 } 278 279 fun sameSignature(method: MethodItem, superMethod: MethodItem, compareRawTypes: Boolean = false): Boolean { 280 // If the return types differ, override it (e.g. parent implements clone(), 281 // subclass overrides with more specific return type) 282 if (method.returnType() != superMethod.returnType()) { 283 return false 284 } 285 286 if (method.deprecated != superMethod.deprecated && !method.deprecated) { 287 return false 288 } 289 290 // Compare modifier lists; note that here we need to 291 // skip modifiers that don't apply in compat mode if set 292 if (!method.modifiers.equivalentTo(superMethod.modifiers)) { 293 return false 294 } 295 296 val parameterList1 = method.parameters() 297 val parameterList2 = superMethod.parameters() 298 299 if (parameterList1.size != parameterList2.size) { 300 return false 301 } 302 303 assert(parameterList1.size == parameterList2.size) 304 for (i in parameterList1.indices) { 305 val p1 = parameterList1[i] 306 val p2 = parameterList2[i] 307 val pt1 = p1.type() 308 val pt2 = p2.type() 309 310 if (compareRawTypes) { 311 if (pt1.toErasedTypeString() != pt2.toErasedTypeString()) { 312 return false 313 } 314 } else { 315 if (pt1 != pt2) { 316 return false 317 } 318 } 319 320 // TODO: Compare annotations to see for example whether 321 // you've refined the nullness policy; if so, that should be included 322 } 323 324 // Also compare throws lists 325 val throwsList12 = method.throwsTypes() 326 val throwsList2 = superMethod.throwsTypes() 327 328 if (throwsList12.size != throwsList2.size) { 329 return false 330 } 331 332 assert(throwsList12.size == throwsList2.size) 333 for (i in throwsList12.indices) { 334 val p1 = throwsList12[i] 335 val p2 = throwsList2[i] 336 val pt1 = p1.qualifiedName() 337 val pt2 = p2.qualifiedName() 338 if (pt1 != pt2) { // assumes throws lists are sorted! 339 return false 340 } 341 } 342 343 return true 344 } 345 } 346 347 fun formatParameters(): String? { 348 // TODO: Generalize, allow callers to control whether to include annotations, whether to erase types, 349 // whether to include names, etc 350 if (parameters().isEmpty()) { 351 return "" 352 } 353 val sb = StringBuilder() 354 for (parameter in parameters()) { 355 if (sb.isNotEmpty()) { 356 sb.append(", ") 357 } 358 sb.append(parameter.type().toTypeString()) 359 } 360 361 return sb.toString() 362 } 363 364 override fun requiresNullnessInfo(): Boolean { 365 return when { 366 modifiers.hasJvmSyntheticAnnotation() -> false 367 isConstructor() -> false 368 (!returnType().primitive) -> true 369 parameters().any { !it.type().primitive } -> true 370 else -> false 371 } 372 } 373 374 override fun hasNullnessInfo(): Boolean { 375 if (!requiresNullnessInfo()) { 376 return true 377 } 378 379 if (!isConstructor() && !returnType().primitive) { 380 if (!modifiers.hasNullnessInfo()) { 381 return false 382 } 383 } 384 385 @Suppress("LoopToCallChain") // The quickfix is wrong! (covered by AnnotationStatisticsTest) 386 for (parameter in parameters()) { 387 if (!parameter.hasNullnessInfo()) { 388 return false 389 } 390 } 391 392 return true 393 } 394 395 fun isImplicitConstructor(): Boolean { 396 return isConstructor() && modifiers.isPublic() && parameters().isEmpty() 397 } 398 399 /** Finds uncaught exceptions actually thrown inside this method (as opposed to ones 400 * declared in the signature) */ 401 fun findThrownExceptions(): Set<ClassItem> = codebase.unsupported() 402 403 /** If annotation method, returns the default value as a source expression */ 404 fun defaultValue(): String = "" 405 406 fun hasDefaultValue(): Boolean { 407 return defaultValue() != "" 408 } 409 410 /** 411 * Returns true if this method is a signature match for the given method (e.g. can 412 * be overriding). This checks that the name and parameter lists match, but ignores 413 * differences in parameter names, return value types and throws list types. 414 */ 415 fun matches(other: MethodItem): Boolean { 416 if (this === other) return true 417 418 if (name() != other.name()) { 419 return false 420 } 421 422 val parameters1 = parameters() 423 val parameters2 = other.parameters() 424 425 if (parameters1.size != parameters2.size) { 426 return false 427 } 428 429 for (i in parameters1.indices) { 430 val parameter1 = parameters1[i] 431 val parameter2 = parameters2[i] 432 val typeString1 = parameter1.type().toString() 433 val typeString2 = parameter2.type().toString() 434 if (typeString1 == typeString2) { 435 continue 436 } 437 val type1 = parameter1.type().toErasedTypeString(this) 438 val type2 = parameter2.type().toErasedTypeString(other) 439 440 if (type1 != type2) { 441 // Workaround for signature-based codebase, where we can't always resolve generic 442 // parameters: if we see a mismatch here which looks like a failure to erase say T into 443 // java.lang.Object, don't treat that as a mismatch. (Similar common case: T[] and Object[]) 444 if (typeString1[0].isUpperCase() && typeString1.length == 1 && 445 parameter1.codebase is TextCodebase 446 ) { 447 continue 448 } 449 if (typeString2.length >= 2 && !typeString2[1].isLetterOrDigit() && 450 parameter1.codebase is TextCodebase 451 ) { 452 continue 453 } 454 return false 455 } 456 } 457 return true 458 } 459 460 /** Returns whether this method has any types in its signature that does not match the given filter */ 461 fun hasHiddenType(filterReference: Predicate<Item>): Boolean { 462 for (parameter in parameters()) { 463 val type = parameter.type() 464 if (type.hasTypeArguments()) { 465 for (argument in type.typeArgumentClasses()) { 466 if (!filterReference.test(argument)) { 467 return true 468 } 469 } 470 } 471 val clz = type.asClass() ?: continue 472 if (!filterReference.test(clz)) { 473 return true 474 } 475 } 476 477 val returnType = returnType() 478 val returnTypeClass = returnType.asClass() 479 if (returnTypeClass != null && !filterReference.test(returnTypeClass)) { 480 return true 481 } 482 if (returnType.hasTypeArguments()) { 483 for (argument in returnType.typeArgumentClasses()) { 484 if (!filterReference.test(argument)) { 485 return true 486 } 487 } 488 } 489 490 if (typeParameterList().typeParameterCount() > 0) { 491 for (argument in typeArgumentClasses()) { 492 if (!filterReference.test(argument)) { 493 return true 494 } 495 } 496 } 497 498 return false 499 } 500 501 override fun hasShowAnnotationInherited(): Boolean { 502 if (super.hasShowAnnotationInherited()) { 503 return true 504 } 505 return superMethods().any { 506 it.hasShowAnnotationInherited() 507 } 508 } 509 510 override fun onlyShowForStubPurposesInherited(): Boolean { 511 if (super.onlyShowForStubPurposesInherited()) { 512 return true 513 } 514 return superMethods().any { 515 it.onlyShowForStubPurposesInherited() 516 } 517 } 518 519 /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */ 520 fun isKotlinProperty(): Boolean = false 521 522 /** Returns true if this is a synthetic enum method */ 523 fun isEnumSyntheticMethod(): Boolean { 524 return containingClass().isEnum() && 525 ( 526 name() == "values" && parameters().isEmpty() || 527 name() == "valueOf" && parameters().size == 1 && 528 parameters()[0].type().isString() 529 ) 530 } 531 } 532