• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 @MetalavaApi
20 interface MethodItem : CallableItem, InheritableItem {
21     /**
22      * The property this method is an accessor for; inverse of [PropertyItem.getter] and
23      * [PropertyItem.setter]
24      */
25     val property: PropertyItem?
26         get() = null
27 
28     override val effectivelyDeprecated: Boolean
29         get() =
30             originallyDeprecated ||
31                 containingClass().effectivelyDeprecated ||
32                 // Accessors inherit deprecation from their properties. Uses originallyDeprecated to
33                 // prevent a cycle because effectivelyDeprecated on the property checks the getter.
34                 // Also prevents deprecation from propagating getter -> property -> setter.
35                 property?.originallyDeprecated == true
36 
37     @Deprecated(
38         message =
39             "There is no point in calling this method on MethodItem as it always returns false",
40         ReplaceWith("")
41     )
42     override fun isConstructor() = false
43 
44     /** Returns true if this method is a Kotlin extension method */
45     fun isExtensionMethod(): Boolean
46 
47     /** Returns the super methods that this method is overriding */
48     fun superMethods(): List<MethodItem>
49 
50     override fun findCorrespondingItemIn(
51         codebase: Codebase,
52         superMethods: Boolean,
53         duplicate: Boolean,
54     ): MethodItem? {
55         val correspondingClassItem = containingClass().findCorrespondingItemIn(codebase)
56         val correspondingMethodItem =
57             correspondingClassItem?.findMethod(
58                 this,
59                 includeSuperClasses = superMethods,
60                 includeInterfaces = superMethods,
61             )
62         return if (
63             correspondingMethodItem != null &&
64                 duplicate &&
65                 correspondingMethodItem.containingClass() !== correspondingClassItem
66         )
67             correspondingMethodItem.duplicate(correspondingClassItem)
68         else correspondingMethodItem
69     }
70 
71     fun allSuperMethods(): Sequence<MethodItem> {
72         val original = superMethods().firstOrNull() ?: return emptySequence()
73         return generateSequence(original) { item ->
74             val superMethods = item.superMethods()
75             superMethods.firstOrNull()
76         }
77     }
78 
79     /**
80      * If this method requires override in the child class to prevent error when compiling the stubs
81      */
82     @Deprecated("This property should not be accessed directly.") var _requiresOverride: Boolean?
83 
84     /**
85      * Duplicates this method item.
86      *
87      * Override to specialize the return type.
88      */
89     override fun duplicate(targetContainingClass: ClassItem): MethodItem
90 
91     fun findPredicateSuperMethod(predicate: FilterPredicate): MethodItem? {
92         val superMethods = superMethods()
93         for (method in superMethods) {
94             if (predicate.test(method)) {
95                 return method
96             }
97         }
98 
99         for (method in superMethods) {
100             val found = method.findPredicateSuperMethod(predicate)
101             if (found != null) {
102                 return found
103             }
104         }
105 
106         return null
107     }
108 
109     override fun accept(visitor: ItemVisitor) {
110         visitor.visit(this)
111     }
112 
113     companion object {
114         /**
115          * Compare two types to see if they are considered the same.
116          *
117          * Same means, functionally equivalent at both compile time and runtime.
118          *
119          * TODO: Compare annotations to see for example whether you've refined the nullness policy;
120          *   if so, that should be included
121          */
122         private fun sameType(
123             t1: TypeItem,
124             t2: TypeItem,
125             addAdditionalOverrides: Boolean,
126         ): Boolean {
127             // Compare the types in two ways.
128             // 1. Using `TypeItem.equals(TypeItem)` which is basically a textual comparison that
129             //    ignores type parameter bounds but includes everuthing else that is present in the
130             //    string representation of the type apart from white space differences. This is
131             //    needed to preserve methods that change annotations, e.g. adding `@NonNull`, which
132             //    are significant to the API, and also to preserver legacy behavior to reduce churn
133             //    in API signature files.
134             // 2. Comparing their erased types which takes into account type parameter bounds but
135             //    ignores annotations and generic types. Comparing erased types will retain more
136             //    methods overrides in the signature file so only do it when adding additional
137             //    overrides.
138             return t1 == t2 &&
139                 (!addAdditionalOverrides || t1.toErasedTypeString() == t2.toErasedTypeString())
140         }
141 
142         fun sameSignature(
143             method: MethodItem,
144             superMethod: MethodItem,
145             addAdditionalOverrides: Boolean,
146         ): Boolean {
147             // If the return types differ, override it (e.g. parent implements clone(),
148             // subclass overrides with more specific return type)
149             if (
150                 !sameType(
151                     method.returnType(),
152                     superMethod.returnType(),
153                     addAdditionalOverrides = addAdditionalOverrides
154                 )
155             ) {
156                 return false
157             }
158 
159             if (
160                 method.effectivelyDeprecated != superMethod.effectivelyDeprecated &&
161                     !method.effectivelyDeprecated
162             ) {
163                 return false
164             }
165 
166             // Compare modifier lists; note that here we need to
167             // skip modifiers that don't apply in compat mode if set
168             if (!method.modifiers.equivalentTo(method, superMethod.modifiers)) {
169                 return false
170             }
171 
172             val parameterList1 = method.parameters()
173             val parameterList2 = superMethod.parameters()
174 
175             if (parameterList1.size != parameterList2.size) {
176                 return false
177             }
178 
179             assert(parameterList1.size == parameterList2.size)
180             for (i in parameterList1.indices) {
181                 val p1 = parameterList1[i]
182                 val p2 = parameterList2[i]
183                 val pt1 = p1.type()
184                 val pt2 = p2.type()
185 
186                 if (!sameType(pt1, pt2, addAdditionalOverrides)) {
187                     return false
188                 }
189             }
190 
191             // Also compare throws lists
192             val throwsList12 = method.throwsTypes()
193             val throwsList2 = superMethod.throwsTypes()
194 
195             if (throwsList12.size != throwsList2.size) {
196                 return false
197             }
198 
199             assert(throwsList12.size == throwsList2.size)
200             for (i in throwsList12.indices) {
201                 val p1 = throwsList12[i]
202                 val p2 = throwsList2[i]
203                 val pt1 = p1.toTypeString()
204                 val pt2 = p2.toTypeString()
205                 if (pt1 != pt2) { // assumes throws lists are sorted!
206                     return false
207                 }
208             }
209 
210             return true
211         }
212     }
213 
214     /**
215      * Check whether this method is a synthetic enum method.
216      *
217      * i.e. `getEntries()` from Kotlin and `values()` and `valueOf(String)` from both Java and
218      * Kotlin.
219      */
220     fun isEnumSyntheticMethod(): Boolean {
221         if (!containingClass().isEnum()) return false
222         val parameters = parameters()
223         return when (parameters.size) {
224             0 -> name().let { name -> name == JAVA_ENUM_VALUES || name == "getEntries" }
225             1 -> name() == JAVA_ENUM_VALUE_OF && parameters[0].type().isString()
226             else -> false
227         }
228     }
229 
230     /**
231      * If annotation method, returns the legacy default value as a source expression.
232      *
233      * This is called `legacy` because this an old, inconsistent representation of the default value
234      * that exposes implementation details. It will be replaced by a properly modelled value
235      * representation.
236      */
237     fun legacyDefaultValue(): String
238 
239     /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */
240     fun isKotlinProperty(): Boolean = false
241 
242     /**
243      * Determines if the method is a method that needs to be overridden in any child classes that
244      * extend this [MethodItem] in order to prevent errors when compiling the stubs or the reverse
245      * dependencies of stubs.
246      *
247      * @return Boolean value indicating whether the method needs to be overridden in the child
248      *   classes
249      */
250     @Suppress("DEPRECATION")
251     private fun requiresOverride(): Boolean {
252         _requiresOverride?.let {
253             return _requiresOverride as Boolean
254         }
255 
256         _requiresOverride = computeRequiresOverride()
257 
258         return _requiresOverride as Boolean
259     }
260 
261     private fun computeRequiresOverride(): Boolean {
262         val isVisible = !hidden || hasShowAnnotation()
263 
264         // When the method is a concrete, non-default method, its overriding method is not required
265         // to be shown in the signature file.
266         return if (!modifiers.isAbstract() && !modifiers.isDefault()) {
267             false
268         } else if (superMethods().isEmpty()) {
269             // If the method is abstract and is not overriding any parent methods,
270             // it requires override in the child class if it is visible
271             isVisible
272         } else {
273             // If the method is abstract and is overriding any visible parent methods:
274             // it needs to be overridden if:
275             // - it is visible or
276             // - all super methods are either java.lang.Object method or requires override
277             isVisible ||
278                 superMethods().all {
279                     it.containingClass().isJavaLangObject() || it.requiresOverride()
280                 }
281         }
282     }
283 
284     private fun getUniqueSuperInterfaceMethods(
285         superInterfaceMethods: List<MethodItem>
286     ): List<MethodItem> {
287         val visitCountMap = mutableMapOf<ClassItem, Int>()
288 
289         // perform BFS on all super interfaces of each super interface methods'
290         // containing interface to determine the leaf interface of each unique hierarchy.
291         superInterfaceMethods.forEach {
292             val superInterface = it.containingClass()
293             val queue = mutableListOf(superInterface)
294             while (queue.isNotEmpty()) {
295                 val s = queue.removeFirst()
296                 visitCountMap[s] = visitCountMap.getOrDefault(s, 0) + 1
297                 queue.addAll(
298                     s.interfaceTypes().mapNotNull { interfaceType -> interfaceType.asClass() }
299                 )
300             }
301         }
302 
303         // If visit count is greater than 1, it means the interface is within the hierarchy of
304         // another method, thus filter out.
305         return superInterfaceMethods.filter { visitCountMap[it.containingClass()]!! == 1 }
306     }
307 
308     /**
309      * Determines if the method needs to be added to the signature file in order to prevent errors
310      * when compiling the stubs or the reverse dependencies of the stubs.
311      *
312      * @return Boolean value indicating whether the method needs to be added to the signature file
313      */
314     fun isRequiredOverridingMethodForTextStub(): Boolean {
315         return (containingClass().isClass() &&
316             !modifiers.isAbstract() &&
317             superMethods().isNotEmpty() &&
318             superMethods().let {
319                 if (it.size == 1 && it.first().containingClass().isJavaLangObject()) {
320                     // If the method is extending a java.lang.Object method,
321                     // it only required override when it is directly (not transitively) overriding
322                     // it and the signature differs (e.g. visibility or modifier
323                     // changes)
324                     !sameSignature(
325                         this,
326                         it.first(),
327                         // This method is only called when add-additional-overrides=yes.
328                         addAdditionalOverrides = true,
329                     )
330                 } else {
331                     // Since a class can extend a single class except Object,
332                     // there is only one non-Object super class method at max.
333                     val superClassMethods =
334                         it.firstOrNull { superMethod ->
335                             superMethod.containingClass().isClass() &&
336                                 !superMethod.containingClass().isJavaLangObject()
337                         }
338 
339                     // Assume a class implements two interfaces A and B;
340                     // A provides a default super method, and B provides an abstract super method.
341                     // In such case, the child method is a required overriding method when:
342                     // - A and B do not extend each other or
343                     // - A is a super interface of B
344                     // On the other hand, the child method is not a required overriding method when:
345                     // - B is a super interface of A
346                     // Given this, we should make decisions only based on the leaf interface of each
347                     // unique hierarchy.
348                     val uniqueSuperInterfaceMethods =
349                         getUniqueSuperInterfaceMethods(
350                             it.filter { superMethod -> superMethod.containingClass().isInterface() }
351                         )
352 
353                     // If super method is non-null, whether this method is required
354                     // is determined by whether the super method requires override.
355                     // If super method is null, this method is required if there is a
356                     // unique super interface that requires override.
357                     superClassMethods?.requiresOverride()
358                         ?: uniqueSuperInterfaceMethods.any { s -> s.requiresOverride() }
359                 }
360             }) ||
361             // To inherit methods with override-equivalent signatures
362             // See https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.4.1.3
363             (containingClass().isInterface() &&
364                 superMethods().count { it.modifiers.isAbstract() || it.modifiers.isDefault() } > 1)
365     }
366 }
367