Home | History | Annotate | Download | only in model
      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 }