1 /* 2 * Copyright (C) 2013 The Dagger Authors. 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 dagger.internal.codegen.langmodel; 18 19 import static androidx.room.compiler.processing.compat.XConverters.toJavac; 20 import static com.google.auto.common.MoreElements.asExecutable; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.collect.Lists.asList; 23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 24 import static java.util.Comparator.comparing; 25 26 import androidx.room.compiler.processing.XElement; 27 import androidx.room.compiler.processing.XMethodElement; 28 import com.google.auto.common.MoreElements; 29 import com.google.common.collect.ImmutableMap; 30 import com.google.common.collect.ImmutableSet; 31 import com.squareup.javapoet.ClassName; 32 import dagger.Reusable; 33 import dagger.internal.codegen.base.ClearableCache; 34 import java.io.Writer; 35 import java.util.Comparator; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Optional; 40 import java.util.stream.Collectors; 41 import javax.lang.model.element.AnnotationMirror; 42 import javax.lang.model.element.AnnotationValue; 43 import javax.lang.model.element.Element; 44 import javax.lang.model.element.ElementKind; 45 import javax.lang.model.element.ExecutableElement; 46 import javax.lang.model.element.Name; 47 import javax.lang.model.element.PackageElement; 48 import javax.lang.model.element.QualifiedNameable; 49 import javax.lang.model.element.TypeElement; 50 import javax.lang.model.element.VariableElement; 51 import javax.lang.model.type.ArrayType; 52 import javax.lang.model.type.DeclaredType; 53 import javax.lang.model.type.ErrorType; 54 import javax.lang.model.type.ExecutableType; 55 import javax.lang.model.type.IntersectionType; 56 import javax.lang.model.type.NoType; 57 import javax.lang.model.type.PrimitiveType; 58 import javax.lang.model.type.TypeMirror; 59 import javax.lang.model.type.TypeVariable; 60 import javax.lang.model.type.WildcardType; 61 import javax.lang.model.util.Elements; 62 import javax.lang.model.util.SimpleTypeVisitor8; 63 import javax.lang.model.util.Types; 64 65 /** Extension of {@link Elements} that adds Dagger-specific methods. */ 66 @Reusable 67 public final class DaggerElements implements Elements, ClearableCache { 68 private final Map<TypeElement, ImmutableSet<ExecutableElement>> getLocalAndInheritedMethodsCache = 69 new HashMap<>(); 70 private final Elements elements; 71 private final Types types; 72 DaggerElements(Elements elements, Types types)73 public DaggerElements(Elements elements, Types types) { 74 this.elements = checkNotNull(elements); 75 this.types = checkNotNull(types); 76 } 77 78 /** 79 * Returns {@code true} if {@code encloser} is equal to or recursively encloses {@code enclosed}. 80 */ transitivelyEncloses(XElement encloser, XElement enclosed)81 public static boolean transitivelyEncloses(XElement encloser, XElement enclosed) { 82 return transitivelyEncloses(toJavac(encloser), toJavac(enclosed)); 83 } 84 85 /** 86 * Returns {@code true} if {@code encloser} is equal to or recursively encloses {@code enclosed}. 87 */ transitivelyEncloses(Element encloser, Element enclosed)88 public static boolean transitivelyEncloses(Element encloser, Element enclosed) { 89 Element current = enclosed; 90 while (current != null) { 91 if (current.equals(encloser)) { 92 return true; 93 } 94 current = current.getEnclosingElement(); 95 } 96 return false; 97 } 98 getLocalAndInheritedMethods(TypeElement type)99 public ImmutableSet<ExecutableElement> getLocalAndInheritedMethods(TypeElement type) { 100 return getLocalAndInheritedMethodsCache.computeIfAbsent( 101 type, k -> MoreElements.getLocalAndInheritedMethods(type, types, elements)); 102 } 103 104 @Override getTypeElement(CharSequence name)105 public TypeElement getTypeElement(CharSequence name) { 106 return elements.getTypeElement(name); 107 } 108 109 /** Returns the type element for a class name. */ getTypeElement(ClassName className)110 public TypeElement getTypeElement(ClassName className) { 111 return getTypeElement(className.canonicalName()); 112 } 113 114 /** Returns the argument or the closest enclosing element that is a {@link TypeElement}. */ closestEnclosingTypeElement(Element element)115 public static TypeElement closestEnclosingTypeElement(Element element) { 116 Element current = element; 117 while (current != null) { 118 if (MoreElements.isType(current)) { 119 return MoreElements.asType(current); 120 } 121 current = current.getEnclosingElement(); 122 } 123 throw new IllegalStateException("There is no enclosing TypeElement for: " + element); 124 } 125 126 /** 127 * Compares elements according to their declaration order among siblings. Only valid to compare 128 * elements enclosed by the same parent. 129 */ 130 public static final Comparator<Element> DECLARATION_ORDER = 131 comparing(element -> siblings(element).indexOf(element)); 132 133 // For parameter elements, element.getEnclosingElement().getEnclosedElements() is empty. So 134 // instead look at the parameter list of the enclosing executable. siblings(Element element)135 private static List<? extends Element> siblings(Element element) { 136 return element.getKind().equals(ElementKind.PARAMETER) 137 ? asExecutable(element.getEnclosingElement()).getParameters() 138 : element.getEnclosingElement().getEnclosedElements(); 139 } 140 141 /** 142 * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain 143 * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as any of 144 * that of {@code annotationClasses}. 145 */ isAnyAnnotationPresent( Element element, Iterable<ClassName> annotationClasses)146 public static boolean isAnyAnnotationPresent( 147 Element element, Iterable<ClassName> annotationClasses) { 148 for (ClassName annotation : annotationClasses) { 149 if (isAnnotationPresent(element, annotation)) { 150 return true; 151 } 152 } 153 return false; 154 } 155 156 @SafeVarargs isAnyAnnotationPresent( Element element, ClassName first, ClassName... otherAnnotations)157 public static boolean isAnyAnnotationPresent( 158 Element element, ClassName first, ClassName... otherAnnotations) { 159 return isAnyAnnotationPresent(element, asList(first, otherAnnotations)); 160 } 161 162 /** 163 * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@link 164 * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of 165 * {@code annotationClass}. This method is a safer alternative to calling {@link 166 * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with 167 * annotation proxies. 168 */ isAnnotationPresent(Element element, ClassName annotationName)169 public static boolean isAnnotationPresent(Element element, ClassName annotationName) { 170 return getAnnotationMirror(element, annotationName).isPresent(); 171 } 172 173 // Note: This is similar to auto-common's MoreElements except using ClassName rather than Class. 174 // TODO(bcorso): Contribute a String version to auto-common's MoreElements? 175 /** 176 * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on 177 * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a 178 * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with 179 * annotation proxies. 180 */ getAnnotationMirror( Element element, ClassName annotationName)181 public static Optional<AnnotationMirror> getAnnotationMirror( 182 Element element, ClassName annotationName) { 183 String annotationClassName = annotationName.canonicalName(); 184 for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { 185 TypeElement annotationTypeElement = 186 MoreElements.asType(annotationMirror.getAnnotationType().asElement()); 187 if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) { 188 return Optional.of(annotationMirror); 189 } 190 } 191 return Optional.empty(); 192 } 193 getAnnotatedAnnotations( Element element, ClassName annotationName)194 public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations( 195 Element element, ClassName annotationName) { 196 return element.getAnnotationMirrors().stream() 197 .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName)) 198 .collect(toImmutableSet()); 199 } 200 201 /** 202 * Returns the field descriptor of the given {@code element}. 203 * 204 * <p>This is useful for matching Kotlin Metadata JVM Signatures with elements from the AST. 205 * 206 * <p>For reference, see the <a 207 * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2">JVM 208 * specification, section 4.3.2</a>. 209 */ getFieldDescriptor(VariableElement element)210 public static String getFieldDescriptor(VariableElement element) { 211 return element.getSimpleName() + ":" + getDescriptor(element.asType()); 212 } 213 214 /** 215 * Returns the method descriptor of the given {@code element}. 216 * 217 * <p>This is useful for matching Kotlin Metadata JVM Signatures with elements from the AST. 218 * 219 * <p>For reference, see the <a 220 * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3">JVM 221 * specification, section 4.3.3</a>. 222 */ 223 // TODO(bcorso): Expose getMethodDescriptor() method in XProcessing instead. getMethodDescriptor(XMethodElement element)224 public static String getMethodDescriptor(XMethodElement element) { 225 return getMethodDescriptor(toJavac(element)); 226 } 227 228 /** 229 * Returns the method descriptor of the given {@code element}. 230 * 231 * <p>This is useful for matching Kotlin Metadata JVM Signatures with elements from the AST. 232 * 233 * <p>For reference, see the <a 234 * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3">JVM 235 * specification, section 4.3.3</a>. 236 */ getMethodDescriptor(ExecutableElement element)237 public static String getMethodDescriptor(ExecutableElement element) { 238 return element.getSimpleName() + getDescriptor(element.asType()); 239 } 240 getDescriptor(TypeMirror t)241 private static String getDescriptor(TypeMirror t) { 242 return t.accept(JVM_DESCRIPTOR_TYPE_VISITOR, null); 243 } 244 245 private static final SimpleTypeVisitor8<String, Void> JVM_DESCRIPTOR_TYPE_VISITOR = 246 new SimpleTypeVisitor8<String, Void>() { 247 248 @Override 249 public String visitArray(ArrayType arrayType, Void v) { 250 return "[" + getDescriptor(arrayType.getComponentType()); 251 } 252 253 @Override 254 public String visitDeclared(DeclaredType declaredType, Void v) { 255 return "L" + getInternalName(declaredType.asElement()) + ";"; 256 } 257 258 @Override 259 public String visitError(ErrorType errorType, Void v) { 260 // For descriptor generating purposes we don't need a fully modeled type since we are 261 // only interested in obtaining the class name in its "internal form". 262 return visitDeclared(errorType, v); 263 } 264 265 @Override 266 public String visitExecutable(ExecutableType executableType, Void v) { 267 String parameterDescriptors = 268 executableType.getParameterTypes().stream() 269 .map(DaggerElements::getDescriptor) 270 .collect(Collectors.joining()); 271 String returnDescriptor = getDescriptor(executableType.getReturnType()); 272 return "(" + parameterDescriptors + ")" + returnDescriptor; 273 } 274 275 @Override 276 public String visitIntersection(IntersectionType intersectionType, Void v) { 277 // For a type variable with multiple bounds: "the erasure of a type variable is determined 278 // by the first type in its bound" - JVM Spec Sec 4.4 279 return getDescriptor(intersectionType.getBounds().get(0)); 280 } 281 282 @Override 283 public String visitNoType(NoType noType, Void v) { 284 return "V"; 285 } 286 287 @Override 288 public String visitPrimitive(PrimitiveType primitiveType, Void v) { 289 switch (primitiveType.getKind()) { 290 case BOOLEAN: 291 return "Z"; 292 case BYTE: 293 return "B"; 294 case SHORT: 295 return "S"; 296 case INT: 297 return "I"; 298 case LONG: 299 return "J"; 300 case CHAR: 301 return "C"; 302 case FLOAT: 303 return "F"; 304 case DOUBLE: 305 return "D"; 306 default: 307 throw new IllegalArgumentException("Unknown primitive type."); 308 } 309 } 310 311 @Override 312 public String visitTypeVariable(TypeVariable typeVariable, Void v) { 313 // The erasure of a type variable is the erasure of its leftmost bound. - JVM Spec Sec 4.6 314 return getDescriptor(typeVariable.getUpperBound()); 315 } 316 317 @Override 318 public String defaultAction(TypeMirror typeMirror, Void v) { 319 throw new IllegalArgumentException("Unsupported type: " + typeMirror); 320 } 321 322 @Override 323 public String visitWildcard(WildcardType wildcardType, Void v) { 324 return ""; 325 } 326 327 /** 328 * Returns the name of this element in its "internal form". 329 * 330 * <p>For reference, see the <a 331 * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.2">JVM 332 * specification, section 4.2</a>. 333 */ 334 private String getInternalName(Element element) { 335 try { 336 TypeElement typeElement = MoreElements.asType(element); 337 switch (typeElement.getNestingKind()) { 338 case TOP_LEVEL: 339 return typeElement.getQualifiedName().toString().replace('.', '/'); 340 case MEMBER: 341 return getInternalName(typeElement.getEnclosingElement()) 342 + "$" 343 + typeElement.getSimpleName(); 344 default: 345 throw new IllegalArgumentException("Unsupported nesting kind."); 346 } 347 } catch (IllegalArgumentException e) { 348 // Not a TypeElement, try something else... 349 } 350 351 if (element instanceof QualifiedNameable) { 352 QualifiedNameable qualifiedNameElement = (QualifiedNameable) element; 353 return qualifiedNameElement.getQualifiedName().toString().replace('.', '/'); 354 } 355 356 return element.getSimpleName().toString(); 357 } 358 }; 359 360 @Override getPackageElement(CharSequence name)361 public PackageElement getPackageElement(CharSequence name) { 362 return elements.getPackageElement(name); 363 } 364 365 @Override getElementValuesWithDefaults( AnnotationMirror a)366 public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults( 367 AnnotationMirror a) { 368 return elements.getElementValuesWithDefaults(a); 369 } 370 371 /** Returns a map of annotation values keyed by attribute name. */ getElementValuesWithDefaultsByName( AnnotationMirror a)372 public Map<String, ? extends AnnotationValue> getElementValuesWithDefaultsByName( 373 AnnotationMirror a) { 374 ImmutableMap.Builder<String, AnnotationValue> builder = ImmutableMap.builder(); 375 getElementValuesWithDefaults(a).forEach((k, v) -> builder.put(k.getSimpleName().toString(), v)); 376 return builder.build(); 377 } 378 379 @Override getDocComment(Element e)380 public String getDocComment(Element e) { 381 return elements.getDocComment(e); 382 } 383 384 @Override isDeprecated(Element e)385 public boolean isDeprecated(Element e) { 386 return elements.isDeprecated(e); 387 } 388 389 @Override getBinaryName(TypeElement type)390 public Name getBinaryName(TypeElement type) { 391 return elements.getBinaryName(type); 392 } 393 394 @Override getPackageOf(Element type)395 public PackageElement getPackageOf(Element type) { 396 return elements.getPackageOf(type); 397 } 398 399 @Override getAllMembers(TypeElement type)400 public List<? extends Element> getAllMembers(TypeElement type) { 401 return elements.getAllMembers(type); 402 } 403 404 @Override getAllAnnotationMirrors(Element e)405 public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) { 406 return elements.getAllAnnotationMirrors(e); 407 } 408 409 @Override hides(Element hider, Element hidden)410 public boolean hides(Element hider, Element hidden) { 411 return elements.hides(hider, hidden); 412 } 413 414 @Override overrides( ExecutableElement overrider, ExecutableElement overridden, TypeElement type)415 public boolean overrides( 416 ExecutableElement overrider, ExecutableElement overridden, TypeElement type) { 417 return elements.overrides(overrider, overridden, type); 418 } 419 420 @Override getConstantExpression(Object value)421 public String getConstantExpression(Object value) { 422 return elements.getConstantExpression(value); 423 } 424 425 @Override printElements(Writer w, Element... elements)426 public void printElements(Writer w, Element... elements) { 427 this.elements.printElements(w, elements); 428 } 429 430 @Override getName(CharSequence cs)431 public Name getName(CharSequence cs) { // SUPPRESS_GET_NAME_CHECK: This is not xprocessing usage. 432 return elements.getName(cs); // SUPPRESS_GET_NAME_CHECK: This is not xprocessing usage. 433 } 434 435 @Override isFunctionalInterface(TypeElement type)436 public boolean isFunctionalInterface(TypeElement type) { 437 return elements.isFunctionalInterface(type); 438 } 439 440 @Override clearCache()441 public void clearCache() { 442 getLocalAndInheritedMethodsCache.clear(); 443 } 444 } 445