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.psi 18 19 import com.android.tools.metalava.compatibility 20 import com.android.tools.metalava.model.AnnotationTarget 21 import com.android.tools.metalava.model.ClassItem 22 import com.android.tools.metalava.model.MethodItem 23 import com.android.tools.metalava.model.ModifierList 24 import com.android.tools.metalava.model.ParameterItem 25 import com.android.tools.metalava.model.TypeItem 26 import com.android.tools.metalava.model.TypeParameterList 27 import com.intellij.psi.PsiAnnotationMethod 28 import com.intellij.psi.PsiArrayType 29 import com.intellij.psi.PsiClass 30 import com.intellij.psi.PsiMethod 31 import com.intellij.psi.util.PsiTypesUtil 32 import com.intellij.psi.util.TypeConversionUtil 33 import org.intellij.lang.annotations.Language 34 import org.jetbrains.kotlin.psi.KtNamedFunction 35 import org.jetbrains.kotlin.psi.KtParameter 36 import org.jetbrains.kotlin.psi.KtProperty 37 import org.jetbrains.kotlin.psi.KtPropertyAccessor 38 import org.jetbrains.uast.UClass 39 import org.jetbrains.uast.UElement 40 import org.jetbrains.uast.UExpression 41 import org.jetbrains.uast.UMethod 42 import org.jetbrains.uast.UThrowExpression 43 import org.jetbrains.uast.UTryExpression 44 import org.jetbrains.uast.UastFacade 45 import org.jetbrains.uast.getParentOfType 46 import org.jetbrains.uast.kotlin.declarations.KotlinUMethod 47 import org.jetbrains.uast.visitor.AbstractUastVisitor 48 import java.io.StringWriter 49 50 open class PsiMethodItem( 51 override val codebase: PsiBasedCodebase, 52 val psiMethod: PsiMethod, 53 private val containingClass: PsiClassItem, 54 private val name: String, 55 modifiers: PsiModifierItem, 56 documentation: String, 57 private val returnType: PsiTypeItem, 58 private val parameters: List<PsiParameterItem> 59 ) : 60 PsiItem( 61 codebase = codebase, 62 modifiers = modifiers, 63 documentation = documentation, 64 element = psiMethod 65 ), MethodItem { 66 67 init { 68 for (parameter in parameters) { 69 @Suppress("LeakingThis") 70 parameter.containingMethod = this 71 } 72 } 73 74 /** 75 * If this item was created by filtering down a different codebase, this temporarily 76 * points to the original item during construction. This is used to let us initialize 77 * for example throws lists later, when all classes in the codebase have been 78 * initialized. 79 */ 80 internal var source: PsiMethodItem? = null 81 82 override var inheritedMethod: Boolean = false 83 override var inheritedFrom: ClassItem? = null 84 85 override fun name(): String = name 86 override fun containingClass(): PsiClassItem = containingClass 87 88 override fun equals(other: Any?): Boolean { 89 // TODO: Allow mix and matching with other MethodItems? 90 if (this === other) return true 91 if (javaClass != other?.javaClass) return false 92 93 other as PsiMethodItem 94 95 if (psiMethod != other.psiMethod) return false 96 97 return true 98 } 99 100 override fun hashCode(): Int { 101 return psiMethod.hashCode() 102 } 103 104 override fun isConstructor(): Boolean = false 105 106 override fun isImplicitConstructor(): Boolean = false 107 108 override fun returnType(): TypeItem? = returnType 109 110 override fun parameters(): List<ParameterItem> = parameters 111 112 override val synthetic: Boolean get() = isEnumSyntheticMethod() 113 114 private var superMethods: List<MethodItem>? = null 115 override fun superMethods(): List<MethodItem> { 116 if (superMethods == null) { 117 val result = mutableListOf<MethodItem>() 118 psiMethod.findSuperMethods().mapTo(result) { codebase.findMethod(it) } 119 superMethods = result 120 } 121 122 return superMethods!! 123 } 124 125 override fun typeParameterList(): TypeParameterList { 126 if (psiMethod.hasTypeParameters()) { 127 return PsiTypeParameterList( 128 codebase, psiMethod.typeParameterList 129 ?: return TypeParameterList.NONE 130 ) 131 } else { 132 return TypeParameterList.NONE 133 } 134 } 135 136 override fun typeArgumentClasses(): List<ClassItem> { 137 return PsiTypeItem.typeParameterClasses(codebase, psiMethod.typeParameterList) 138 } 139 140 // private var throwsTypes: List<ClassItem>? = null 141 private lateinit var throwsTypes: List<ClassItem> 142 143 fun setThrowsTypes(throwsTypes: List<ClassItem>) { 144 this.throwsTypes = throwsTypes 145 } 146 147 override fun throwsTypes(): List<ClassItem> = throwsTypes 148 149 override fun isCloned(): Boolean { 150 val psiClass = run { 151 val p = containingClass().psi() as? PsiClass ?: return false 152 if (p is UClass) { 153 p.sourcePsi as? PsiClass ?: return false 154 } else { 155 p 156 } 157 } 158 return psiMethod.containingClass != psiClass 159 } 160 161 override fun isExtensionMethod(): Boolean { 162 if (isKotlin()) { 163 val ktParameters = 164 ((psiMethod as? KotlinUMethod)?.sourcePsi as? KtNamedFunction)?.valueParameters 165 ?: return false 166 return ktParameters.size < parameters.size 167 } 168 169 return false 170 } 171 172 override fun isKotlinProperty(): Boolean { 173 return psiMethod is KotlinUMethod && ( 174 psiMethod.sourcePsi is KtProperty || 175 psiMethod.sourcePsi is KtPropertyAccessor || 176 psiMethod.sourcePsi is KtParameter && (psiMethod.sourcePsi as KtParameter).hasValOrVar()) 177 } 178 179 override fun findThrownExceptions(): Set<ClassItem> { 180 val method = psiMethod as? UMethod ?: return emptySet() 181 if (!isKotlin()) { 182 return emptySet() 183 } 184 185 val exceptions = mutableSetOf<ClassItem>() 186 187 method.accept(object : AbstractUastVisitor() { 188 override fun visitThrowExpression(node: UThrowExpression): Boolean { 189 val type = node.thrownExpression.getExpressionType() 190 if (type != null) { 191 val exceptionClass = codebase.getType(type).asClass() 192 if (exceptionClass != null && !isCaught(exceptionClass, node)) { 193 exceptions.add(exceptionClass) 194 } 195 } 196 return super.visitThrowExpression(node) 197 } 198 199 private fun isCaught(exceptionClass: ClassItem, node: UThrowExpression): Boolean { 200 var current: UElement = node 201 while (true) { 202 val tryExpression = current.getParentOfType<UTryExpression>( 203 UTryExpression::class.java, true, UMethod::class.java 204 ) ?: return false 205 206 for (catchClause in tryExpression.catchClauses) { 207 for (type in catchClause.types) { 208 val qualifiedName = type.canonicalText 209 if (exceptionClass.extends(qualifiedName)) { 210 return true 211 } 212 } 213 } 214 215 current = tryExpression 216 } 217 } 218 }) 219 220 return exceptions 221 } 222 223 fun areAllParametersOptional(): Boolean { 224 for (param in parameters) { 225 if (!param.hasDefaultValue()) { 226 return false 227 } 228 } 229 return true 230 } 231 232 override fun defaultValue(): String { 233 if (psiMethod is PsiAnnotationMethod) { 234 val value = psiMethod.defaultValue 235 if (value != null) { 236 if (isKotlin(value)) { 237 val defaultExpression: UExpression = UastFacade.convertElement( 238 value, null, 239 UExpression::class.java 240 ) as? UExpression ?: return "" 241 val constant = defaultExpression.evaluate() 242 return if (constant != null) { 243 CodePrinter.constantToSource(constant) 244 } else { 245 // Expression: Compute from UAST rather than just using the source text 246 // such that we can ensure references are fully qualified etc. 247 codebase.printer.toSourceString(defaultExpression) ?: "" 248 } 249 } else { 250 return codebase.printer.toSourceExpression(value, this) 251 } 252 } 253 } 254 255 return super.defaultValue() 256 } 257 258 override fun duplicate(targetContainingClass: ClassItem): PsiMethodItem { 259 val duplicated = create(codebase, targetContainingClass as PsiClassItem, psiMethod) 260 261 duplicated.inheritedFrom = containingClass 262 263 // Preserve flags that may have been inherited (propagated) from surrounding packages 264 if (targetContainingClass.hidden) { 265 duplicated.hidden = true 266 } 267 if (targetContainingClass.removed) { 268 duplicated.removed = true 269 } 270 if (targetContainingClass.docOnly) { 271 duplicated.docOnly = true 272 } 273 if (targetContainingClass.deprecated && compatibility.propagateDeprecatedMembers) { 274 duplicated.deprecated = true 275 } 276 duplicated.throwsTypes = throwsTypes 277 return duplicated 278 } 279 280 /* Call corresponding PSI utility method -- if I can find it! 281 override fun matches(other: MethodItem): Boolean { 282 if (other !is PsiMethodItem) { 283 return super.matches(other) 284 } 285 286 // TODO: Find better API: this also checks surrounding class which we don't want! 287 return psiMethod.isEquivalentTo(other.psiMethod) 288 } 289 */ 290 291 /** 292 * Converts the method to a stub that can be converted back to a PsiMethod. 293 * 294 * Note: This must not be used for emitting stub jars. For that, see 295 * [com.android.tools.metalava.stub.StubWriter]. 296 * 297 * @param replacementMap a map that specifies replacement types for formal type parameters. 298 */ 299 @Language("JAVA") 300 fun toStubForCloning(replacementMap: Map<String, String> = emptyMap()): String { 301 val method = this 302 // There are type variables; we have to recreate the method signature 303 val sb = StringBuilder(100) 304 305 val modifierString = StringWriter() 306 ModifierList.write( 307 modifierString, method.modifiers, method, 308 target = AnnotationTarget.INTERNAL, 309 removeAbstract = false, 310 removeFinal = false, 311 addPublic = true 312 ) 313 sb.append(modifierString.toString()) 314 315 val typeParameters = typeParameterList().toString() 316 if (typeParameters.isNotEmpty()) { 317 sb.append(' ') 318 sb.append(TypeItem.convertTypeString(typeParameters, replacementMap)) 319 } 320 321 val returnType = method.returnType() 322 sb.append(returnType?.convertTypeString(replacementMap)) 323 324 sb.append(' ') 325 sb.append(method.name()) 326 327 sb.append("(") 328 method.parameters().asSequence().forEachIndexed { i, parameter -> 329 if (i > 0) { 330 sb.append(", ") 331 } 332 333 val parameterModifierString = StringWriter() 334 ModifierList.write( 335 parameterModifierString, parameter.modifiers, parameter, 336 target = AnnotationTarget.INTERNAL 337 ) 338 sb.append(parameterModifierString.toString()) 339 sb.append(parameter.type().convertTypeString(replacementMap)) 340 sb.append(' ') 341 sb.append(parameter.name()) 342 } 343 sb.append(")") 344 345 val throws = method.throwsTypes().asSequence().sortedWith(ClassItem.fullNameComparator) 346 if (throws.any()) { 347 sb.append(" throws ") 348 throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEachIndexed { i, type -> 349 if (i > 0) { 350 sb.append(", ") 351 } 352 // No need to replace variables; we can't have type arguments for exceptions 353 sb.append(type.qualifiedName()) 354 } 355 } 356 357 sb.append(" { return ") 358 val defaultValue = PsiTypesUtil.getDefaultValueOfType(method.psiMethod.returnType) 359 sb.append(defaultValue) 360 sb.append("; }") 361 362 return sb.toString() 363 } 364 365 override fun finishInitialization() { 366 super.finishInitialization() 367 368 throwsTypes = throwsTypes(codebase, psiMethod) 369 } 370 371 companion object { 372 fun create( 373 codebase: PsiBasedCodebase, 374 containingClass: PsiClassItem, 375 psiMethod: PsiMethod 376 ): PsiMethodItem { 377 assert(!psiMethod.isConstructor) 378 val name = psiMethod.name 379 val commentText = javadoc(psiMethod) 380 val modifiers = modifiers(codebase, psiMethod, commentText) 381 if (modifiers.isFinal() && containingClass.modifiers.isFinal()) { 382 // The containing class is final, so it is implied that every method is final as well. 383 // No need to apply 'final' to each method. (We do it here rather than just in the 384 // signature emit code since we want to make sure that the signature comparison 385 // methods with super methods also consider this method non-final.) 386 modifiers.setFinal(false) 387 } 388 val parameters = parameterList(codebase, psiMethod) 389 var psiReturnType = psiMethod.returnType 390 391 // UAST workaround: the enum synthetic methods are sometimes missing return types, 392 // see https://youtrack.jetbrains.com/issue/KT-39560 393 if (psiReturnType == null && containingClass.isEnum()) { 394 if (name == "valueOf") { 395 psiReturnType = codebase.getClassType(containingClass.psiClass) 396 } else if (name == "values") { 397 psiReturnType = PsiArrayType(codebase.getClassType(containingClass.psiClass)) 398 } 399 } 400 401 val returnType = codebase.getType(psiReturnType!!) 402 val method = PsiMethodItem( 403 codebase = codebase, 404 psiMethod = psiMethod, 405 containingClass = containingClass, 406 name = name, 407 documentation = commentText, 408 modifiers = modifiers, 409 returnType = returnType, 410 parameters = parameters 411 ) 412 method.modifiers.setOwner(method) 413 return method 414 } 415 416 fun create( 417 codebase: PsiBasedCodebase, 418 containingClass: PsiClassItem, 419 original: PsiMethodItem 420 ): PsiMethodItem { 421 val method = PsiMethodItem( 422 codebase = codebase, 423 psiMethod = original.psiMethod, 424 containingClass = containingClass, 425 name = original.name(), 426 documentation = original.documentation, 427 modifiers = PsiModifierItem.create(codebase, original.modifiers), 428 returnType = PsiTypeItem.create(codebase, original.returnType), 429 parameters = PsiParameterItem.create(codebase, original.parameters()) 430 ) 431 method.modifiers.setOwner(method) 432 method.source = original 433 method.inheritedMethod = original.inheritedMethod 434 435 return method 436 } 437 438 internal fun parameterList( 439 codebase: PsiBasedCodebase, 440 psiMethod: PsiMethod 441 ): List<PsiParameterItem> { 442 return if (psiMethod is UMethod) { 443 psiMethod.uastParameters.mapIndexed { index, parameter -> 444 PsiParameterItem.create(codebase, parameter, index) 445 } 446 } else { 447 psiMethod.parameterList.parameters.mapIndexed { index, parameter -> 448 PsiParameterItem.create(codebase, parameter, index) 449 } 450 } 451 } 452 453 private fun throwsTypes(codebase: PsiBasedCodebase, psiMethod: PsiMethod): List<ClassItem> { 454 val interfaces = psiMethod.throwsList.referencedTypes 455 if (interfaces.isEmpty()) { 456 return emptyList() 457 } 458 459 val result = ArrayList<ClassItem>(interfaces.size) 460 for (cls in interfaces) { 461 if (compatibility.useErasureInThrows) { 462 val erased = TypeConversionUtil.erasure(cls) 463 result.add(codebase.findClass(erased) ?: continue) 464 continue 465 } 466 467 result.add(codebase.findClass(cls) ?: continue) 468 } 469 470 // We're sorting the names here even though outputs typically do their own sorting, 471 // since for example the MethodItem.sameSignature check wants to do an element-by-element 472 // comparison to see if the signature matches, and that should match overrides even if 473 // they specify their elements in different orders. 474 result.sortWith(ClassItem.fullNameComparator) 475 return result 476 } 477 } 478 479 override fun toString(): String = "${if (isConstructor()) "constructor" else "method"} ${ 480 containingClass.qualifiedName()}.${name()}(${parameters().joinToString { it.type().toSimpleType() }})" 481 } 482