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