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