• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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