• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.kotlin;
18 
19 
20 import static com.google.auto.common.MoreElements.isAnnotationPresent;
21 import static com.google.common.base.Preconditions.checkState;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
23 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
24 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations;
25 import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
26 import static kotlinx.metadata.Flag.Class.IS_DATA;
27 import static kotlinx.metadata.Flag.Class.IS_OBJECT;
28 import static kotlinx.metadata.Flag.IS_PRIVATE;
29 
30 import com.google.common.collect.ImmutableCollection;
31 import com.google.common.collect.ImmutableList;
32 import com.google.common.collect.ImmutableMap;
33 import com.squareup.javapoet.ClassName;
34 import dagger.internal.codegen.extension.DaggerCollectors;
35 import dagger.internal.codegen.kotlin.KotlinMetadata.FunctionMetadata;
36 import java.util.Optional;
37 import javax.inject.Inject;
38 import javax.lang.model.element.AnnotationMirror;
39 import javax.lang.model.element.Element;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.TypeElement;
42 import javax.lang.model.element.VariableElement;
43 import javax.lang.model.util.ElementFilter;
44 import kotlin.Metadata;
45 import kotlinx.metadata.Flag;
46 
47 /** Utility class for interacting with Kotlin Metadata. */
48 public final class KotlinMetadataUtil {
49 
50   private final KotlinMetadataFactory metadataFactory;
51 
52   @Inject
KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)53   KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
54     this.metadataFactory = metadataFactory;
55   }
56 
57   /**
58    * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
59    * an element that does.
60    */
hasMetadata(Element element)61   public boolean hasMetadata(Element element) {
62     return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class);
63   }
64 
65   /**
66    * Returns the synthetic annotations of a Kotlin property.
67    *
68    * <p>Note that this method only looks for additional annotations in the synthetic property
69    * method, if any, of a Kotlin property and not for annotations in its backing field.
70    */
getSyntheticPropertyAnnotations( VariableElement fieldElement, ClassName annotationType)71   public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations(
72       VariableElement fieldElement, ClassName annotationType) {
73     return metadataFactory
74         .create(fieldElement)
75         .getSyntheticAnnotationMethod(fieldElement)
76         .map(methodElement ->
77             getAnnotatedAnnotations(methodElement, annotationType).asList())
78         .orElse(ImmutableList.of());
79   }
80 
81   /**
82    * Returns {@code true} if the synthetic method for annotations is missing. This can occur when
83    * the Kotlin metadata of the property reports that it contains a synthetic method for annotations
84    * but such method is not found since it is synthetic and ignored by the processor.
85    */
isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement)86   public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) {
87     return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement);
88   }
89 
90   /** Returns {@code true} if this type element is a Kotlin Object. */
isObjectClass(TypeElement typeElement)91   public boolean isObjectClass(TypeElement typeElement) {
92     return hasMetadata(typeElement)
93         && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT);
94   }
95 
96   /** Returns {@code true} if this type element is a Kotlin data class. */
isDataClass(TypeElement typeElement)97   public boolean isDataClass(TypeElement typeElement) {
98     return hasMetadata(typeElement)
99         && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA);
100   }
101 
102   /* Returns {@code true} if this type element is a Kotlin Companion Object. */
isCompanionObjectClass(TypeElement typeElement)103   public boolean isCompanionObjectClass(TypeElement typeElement) {
104     return hasMetadata(typeElement)
105         && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT);
106   }
107 
108   /** Returns {@code true} if this type element is a Kotlin object or companion object. */
isObjectOrCompanionObjectClass(TypeElement typeElement)109   public boolean isObjectOrCompanionObjectClass(TypeElement typeElement) {
110     return isObjectClass(typeElement) || isCompanionObjectClass(typeElement);
111   }
112 
113   /* Returns {@code true} if this type element has a Kotlin Companion Object. */
hasEnclosedCompanionObject(TypeElement typeElement)114   public boolean hasEnclosedCompanionObject(TypeElement typeElement) {
115     return hasMetadata(typeElement)
116         && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent();
117   }
118 
119   /* Returns the Companion Object element enclosed by the given type element. */
getEnclosedCompanionObject(TypeElement typeElement)120   public TypeElement getEnclosedCompanionObject(TypeElement typeElement) {
121     return metadataFactory
122         .create(typeElement)
123         .classMetadata()
124         .companionObjectName()
125         .map(
126             companionObjectName ->
127                 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
128                     .filter(
129                         innerType -> innerType.getSimpleName().contentEquals(companionObjectName))
130                     .collect(DaggerCollectors.onlyElement()))
131         .get();
132   }
133 
134   /**
135    * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin
136    * source.
137    */
isVisibilityPrivate(TypeElement typeElement)138   public boolean isVisibilityPrivate(TypeElement typeElement) {
139     return hasMetadata(typeElement)
140         && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE);
141   }
142 
143   /**
144    * Returns {@code true} if the given type element was declared {@code internal} in its Kotlin
145    * source.
146    */
isVisibilityInternal(TypeElement type)147   public boolean isVisibilityInternal(TypeElement type) {
148     return hasMetadata(type)
149         && metadataFactory.create(type).classMetadata().flags(Flag.IS_INTERNAL);
150   }
151 
152   /**
153    * Returns {@code true} if the given executable element was declared {@code internal} in its
154    * Kotlin source.
155    */
isVisibilityInternal(ExecutableElement method)156   public boolean isVisibilityInternal(ExecutableElement method) {
157     return hasMetadata(method)
158         && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL);
159   }
160 
getPropertyGetter(VariableElement fieldElement)161   public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
162     return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
163   }
164 
containsConstructorWithDefaultParam(TypeElement typeElement)165   public boolean containsConstructorWithDefaultParam(TypeElement typeElement) {
166     return hasMetadata(typeElement)
167         && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
168   }
169 
170   /**
171    * Returns a map mapping all method signatures within the given class element, including methods
172    * that it inherits from its ancestors, to their method names.
173    */
getAllMethodNamesBySignature(TypeElement element)174   public ImmutableMap<String, String> getAllMethodNamesBySignature(TypeElement element) {
175     checkState(
176         hasMetadata(element), "Can not call getAllMethodNamesBySignature for non-Kotlin class");
177     return metadataFactory.create(element).classMetadata().functionsBySignature().values().stream()
178         .collect(toImmutableMap(FunctionMetadata::signature, FunctionMetadata::name));
179   }
180 }
181