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