• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.binding;
18 
19 import static androidx.room.compiler.processing.XElementKt.isMethod;
20 import static androidx.room.compiler.processing.XTypeKt.isVoid;
21 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.base.Preconditions.checkNotNull;
24 import static com.google.common.base.Preconditions.checkState;
25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
27 import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
28 import static dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf;
29 import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
30 import static javax.lang.model.type.TypeKind.VOID;
31 
32 import androidx.room.compiler.processing.XElement;
33 import androidx.room.compiler.processing.XMethodElement;
34 import androidx.room.compiler.processing.XType;
35 import androidx.room.compiler.processing.XTypeElement;
36 import com.google.auto.value.AutoValue;
37 import com.google.auto.value.extension.memoized.Memoized;
38 import com.google.common.base.Supplier;
39 import com.google.common.base.Suppliers;
40 import com.google.common.collect.ImmutableBiMap;
41 import com.google.common.collect.ImmutableMap;
42 import com.google.common.collect.ImmutableSet;
43 import com.google.common.collect.Maps;
44 import com.google.errorprone.annotations.CanIgnoreReturnValue;
45 import com.google.errorprone.annotations.CheckReturnValue;
46 import com.squareup.javapoet.TypeName;
47 import dagger.Component;
48 import dagger.Module;
49 import dagger.Subcomponent;
50 import dagger.internal.codegen.base.ComponentAnnotation;
51 import dagger.internal.codegen.langmodel.DaggerTypes;
52 import dagger.producers.CancellationPolicy;
53 import dagger.spi.model.DependencyRequest;
54 import dagger.spi.model.Scope;
55 import java.util.HashMap;
56 import java.util.Map;
57 import java.util.Objects;
58 import java.util.Optional;
59 import java.util.stream.Stream;
60 import javax.lang.model.element.ExecutableElement;
61 import javax.lang.model.type.TypeMirror;
62 
63 /**
64  * A component declaration.
65  *
66  * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent},
67  * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}.
68  *
69  * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also
70  * represent a synthetic component for the module, where there is an entry point for each binding in
71  * the module.
72  */
73 @AutoValue
74 public abstract class ComponentDescriptor {
75   /** Creates a {@link ComponentDescriptor}. */
create( ComponentAnnotation componentAnnotation, XTypeElement component, ImmutableSet<ComponentRequirement> componentDependencies, ImmutableSet<ModuleDescriptor> transitiveModules, ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod, ImmutableSet<Scope> scopes, ImmutableSet<ComponentDescriptor> subcomponentsFromModules, ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod, ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod, ImmutableSet<ComponentMethodDescriptor> componentMethods, Optional<ComponentCreatorDescriptor> creator)76   static ComponentDescriptor create(
77       ComponentAnnotation componentAnnotation,
78       XTypeElement component,
79       ImmutableSet<ComponentRequirement> componentDependencies,
80       ImmutableSet<ModuleDescriptor> transitiveModules,
81       ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod,
82       ImmutableSet<Scope> scopes,
83       ImmutableSet<ComponentDescriptor> subcomponentsFromModules,
84       ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod,
85       ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod,
86       ImmutableSet<ComponentMethodDescriptor> componentMethods,
87       Optional<ComponentCreatorDescriptor> creator) {
88     ComponentDescriptor descriptor =
89         new AutoValue_ComponentDescriptor(
90             componentAnnotation,
91             component,
92             componentDependencies,
93             transitiveModules,
94             dependenciesByDependencyMethod,
95             scopes,
96             subcomponentsFromModules,
97             subcomponentsByFactoryMethod,
98             subcomponentsByBuilderMethod,
99             componentMethods,
100             creator);
101     return descriptor;
102   }
103 
104   /** The annotation that specifies that {@link #typeElement()} is a component. */
annotation()105   public abstract ComponentAnnotation annotation();
106 
107   /** Returns {@code true} if this is a subcomponent. */
isSubcomponent()108   public final boolean isSubcomponent() {
109     return annotation().isSubcomponent();
110   }
111 
112   /**
113    * Returns {@code true} if this is a production component or subcomponent, or a
114    * {@code @ProducerModule} when doing module binding validation.
115    */
isProduction()116   public final boolean isProduction() {
117     return annotation().isProduction();
118   }
119 
120   /**
121    * Returns {@code true} if this is a real component, and not a fictional one used to validate
122    * module bindings.
123    */
isRealComponent()124   public final boolean isRealComponent() {
125     return annotation().isRealComponent();
126   }
127 
128   /**
129    * The element that defines the component. This is the element to which the {@link #annotation()}
130    * was applied.
131    */
typeElement()132   public abstract XTypeElement typeElement();
133 
134   /**
135    * The set of component dependencies listed in {@link Component#dependencies} or {@link
136    * dagger.producers.ProductionComponent#dependencies()}.
137    */
dependencies()138   public abstract ImmutableSet<ComponentRequirement> dependencies();
139 
140   /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
dependenciesAndConcreteModules()141   public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
142     return Stream.concat(
143             moduleTypes().stream()
144                 .filter(dep -> !dep.isAbstract())
145                 .map(module -> ComponentRequirement.forModule(module.getType())),
146             dependencies().stream())
147         .collect(toImmutableSet());
148   }
149 
150   /**
151    * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
152    * traversing {@link Module#includes()}.
153    */
modules()154   public abstract ImmutableSet<ModuleDescriptor> modules();
155 
156   /** The types of the {@link #modules()}. */
moduleTypes()157   public final ImmutableSet<XTypeElement> moduleTypes() {
158     return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
159   }
160 
161   /**
162    * The types for which the component will need instances if all of its bindings are used. For the
163    * types the component will need in a given binding graph, use {@link
164    * BindingGraph#componentRequirements()}.
165    *
166    * <ul>
167    *   <li>{@linkplain #modules()} modules} with concrete instance bindings
168    *   <li>Bound instances
169    *   <li>{@linkplain #dependencies() dependencies}
170    * </ul>
171    */
172   @Memoized
requirements()173   ImmutableSet<ComponentRequirement> requirements() {
174     ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
175     modules().stream()
176         .filter(
177             module ->
178                 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
179         .map(module -> ComponentRequirement.forModule(module.moduleElement().getType()))
180         .forEach(requirements::add);
181     requirements.addAll(dependencies());
182     requirements.addAll(
183         creatorDescriptor()
184             .map(ComponentCreatorDescriptor::boundInstanceRequirements)
185             .orElse(ImmutableSet.of()));
186     return requirements.build();
187   }
188 
189   /**
190    * This component's {@linkplain #dependencies() dependencies} keyed by each provision or
191    * production method defined by that dependency. Note that the dependencies' types are not simply
192    * the enclosing type of the method; a method may be declared by a supertype of the actual
193    * dependency.
194    */
195   public abstract ImmutableMap<XMethodElement, ComponentRequirement>
dependenciesByDependencyMethod()196       dependenciesByDependencyMethod();
197 
198   /** The {@linkplain #dependencies() component dependency} that defines a method. */
getDependencyThatDefinesMethod(XElement method)199   public final ComponentRequirement getDependencyThatDefinesMethod(XElement method) {
200     checkArgument(isMethod(method), "method must be an executable element: %s", method);
201     checkState(
202         dependenciesByDependencyMethod().containsKey(method),
203         "no dependency implements %s",
204         method);
205     return dependenciesByDependencyMethod().get(method);
206   }
207 
208   /** The scopes of the component. */
scopes()209   public abstract ImmutableSet<Scope> scopes();
210 
211   /**
212    * All {@link Subcomponent}s which are direct children of this component. This includes
213    * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
214    * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain
215    * #childComponentsDeclaredByBuilderEntryPoints() builder methods}.
216    */
childComponents()217   public final ImmutableSet<ComponentDescriptor> childComponents() {
218     return ImmutableSet.<ComponentDescriptor>builder()
219         .addAll(childComponentsDeclaredByFactoryMethods().values())
220         .addAll(childComponentsDeclaredByBuilderEntryPoints().values())
221         .addAll(childComponentsDeclaredByModules())
222         .build();
223   }
224 
225   /**
226    * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
227    * Module#subcomponents() module's subcomponents}.
228    */
childComponentsDeclaredByModules()229   abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
230 
231   /**
232    * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
233    * factory method.
234    */
235   public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
childComponentsDeclaredByFactoryMethods()236       childComponentsDeclaredByFactoryMethods();
237 
238   /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
239   @Memoized
childComponentsByElement()240   public ImmutableMap<XTypeElement, ComponentDescriptor> childComponentsByElement() {
241     return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
242   }
243 
244   /** Returns the factory method that declares a child component. */
getFactoryMethodForChildComponent( ComponentDescriptor childComponent)245   final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
246       ComponentDescriptor childComponent) {
247     return Optional.ofNullable(
248         childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
249   }
250 
251   /**
252    * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
253    * builder method.
254    */
255   abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
childComponentsDeclaredByBuilderEntryPoints()256       childComponentsDeclaredByBuilderEntryPoints();
257 
258   private final Supplier<ImmutableMap<XTypeElement, ComponentDescriptor>>
259       childComponentsByBuilderType =
260           Suppliers.memoize(
261               () ->
262                   childComponents().stream()
263                       .filter(child -> child.creatorDescriptor().isPresent())
264                       .collect(
265                           toImmutableMap(
266                               child -> child.creatorDescriptor().get().typeElement(),
267                               child -> child)));
268 
269   /** Returns the child component with the given builder type. */
getChildComponentWithBuilderType(XTypeElement builderType)270   final ComponentDescriptor getChildComponentWithBuilderType(XTypeElement builderType) {
271     return checkNotNull(
272         childComponentsByBuilderType.get().get(builderType),
273         "no child component found for builder type %s",
274         builderType.getQualifiedName());
275   }
276 
componentMethods()277   public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
278 
279   /** Returns the first component method associated with this binding request, if one exists. */
firstMatchingComponentMethod(BindingRequest request)280   public Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) {
281     return Optional.ofNullable(firstMatchingComponentMethods().get(request));
282   }
283 
284   @Memoized
285   ImmutableMap<BindingRequest, ComponentMethodDescriptor>
firstMatchingComponentMethods()286       firstMatchingComponentMethods() {
287     Map<BindingRequest, ComponentMethodDescriptor> methods = new HashMap<>();
288     for (ComponentMethodDescriptor method : entryPointMethods()) {
289       methods.putIfAbsent(BindingRequest.bindingRequest(method.dependencyRequest().get()), method);
290     }
291     return ImmutableMap.copyOf(methods);
292   }
293 
294   /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */
entryPointMethods()295   public final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() {
296     return componentMethods()
297         .stream()
298         .filter(method -> method.dependencyRequest().isPresent())
299         .collect(toImmutableSet());
300   }
301 
302   // TODO(gak): Consider making this non-optional and revising the
303   // interaction between the spec & generation
304   /** Returns a descriptor for the creator type for this component type, if the user defined one. */
creatorDescriptor()305   public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
306 
307   /**
308    * Returns {@code true} for components that have a creator, either because the user {@linkplain
309    * #creatorDescriptor() specified one} or because it's a top-level component with an implicit
310    * builder.
311    */
hasCreator()312   public final boolean hasCreator() {
313     return !isSubcomponent() || creatorDescriptor().isPresent();
314   }
315 
316   /**
317    * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the
318    * component is not a production component or no {@code CancellationPolicy} annotation is present.
319    */
cancellationPolicy()320   public final Optional<CancellationPolicy> cancellationPolicy() {
321     return isProduction()
322         // TODO(bcorso): Get values from XAnnotation instead of using CancellationPolicy directly.
323         ? Optional.ofNullable(toJavac(typeElement()).getAnnotation(CancellationPolicy.class))
324         : Optional.empty();
325   }
326 
327   @Memoized
328   @Override
hashCode()329   public int hashCode() {
330     // TODO(b/122962745): Only use typeElement().hashCode()
331     return Objects.hash(typeElement(), annotation());
332   }
333 
334   // TODO(ronshapiro): simplify the equality semantics
335   @Override
equals(Object obj)336   public abstract boolean equals(Object obj);
337 
338   /** A component method. */
339   @AutoValue
340   public abstract static class ComponentMethodDescriptor {
341     /** The method itself. Note that this may be declared on a supertype of the component. */
methodElement()342     public abstract XMethodElement methodElement();
343 
344     /**
345      * The dependency request for production, provision, and subcomponent creator methods. Absent
346      * for subcomponent factory methods.
347      */
dependencyRequest()348     public abstract Optional<DependencyRequest> dependencyRequest();
349 
350     /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */
subcomponent()351     public abstract Optional<ComponentDescriptor> subcomponent();
352 
353     /**
354      * Returns the return type of {@link #methodElement()} as resolved in the {@link
355      * ComponentDescriptor#typeElement() component type}. If there are no type variables in the
356      * return type, this is the equivalent of {@code methodElement().getReturnType()}.
357      */
resolvedReturnType(DaggerTypes types)358     public TypeMirror resolvedReturnType(DaggerTypes types) {
359       checkState(dependencyRequest().isPresent());
360 
361       XType returnType = methodElement().getReturnType();
362       if (isPrimitive(returnType) || isVoid(returnType)) {
363         return toJavac(returnType);
364       }
365       return BindingRequest.bindingRequest(dependencyRequest().get())
366           .requestedType(dependencyRequest().get().key().type().java(), types);
367     }
368 
369     /** A {@link ComponentMethodDescriptor}builder for a method. */
builder(XMethodElement method)370     public static Builder builder(XMethodElement method) {
371       return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
372           .methodElement(method);
373     }
374 
375     /** A builder of {@link ComponentMethodDescriptor}s. */
376     @AutoValue.Builder
377     @CanIgnoreReturnValue
378     public interface Builder {
379       /** @see ComponentMethodDescriptor#methodElement() */
methodElement(XMethodElement methodElement)380       Builder methodElement(XMethodElement methodElement);
381 
382       /** @see ComponentMethodDescriptor#dependencyRequest() */
dependencyRequest(DependencyRequest dependencyRequest)383       Builder dependencyRequest(DependencyRequest dependencyRequest);
384 
385       /** @see ComponentMethodDescriptor#subcomponent() */
subcomponent(ComponentDescriptor subcomponent)386       Builder subcomponent(ComponentDescriptor subcomponent);
387 
388       /** Builds the descriptor. */
389       @CheckReturnValue
build()390       ComponentMethodDescriptor build();
391     }
392   }
393 
394   /** No-argument methods defined on {@link Object} that are ignored for contribution. */
395   private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES =
396       ImmutableSet.of("toString", "hashCode", "clone", "getClass");
397 
398   /**
399    * Returns {@code true} if a method could be a component entry point but not a members-injection
400    * method.
401    */
isComponentContributionMethod(XMethodElement method)402   static boolean isComponentContributionMethod(XMethodElement method) {
403     return isComponentContributionMethod(toJavac(method));
404   }
405 
406   /**
407    * Returns {@code true} if a method could be a component entry point but not a members-injection
408    * method.
409    */
isComponentContributionMethod(ExecutableElement method)410   static boolean isComponentContributionMethod(ExecutableElement method) {
411     return method.getParameters().isEmpty()
412         && !method.getReturnType().getKind().equals(VOID)
413         && !isTypeOf(TypeName.OBJECT, method.getEnclosingElement().asType())
414         && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(method.getSimpleName().toString());
415   }
416 
417   /** Returns {@code true} if a method could be a component production entry point. */
isComponentProductionMethod(XMethodElement method)418   static boolean isComponentProductionMethod(XMethodElement method) {
419     return isComponentContributionMethod(method) && isFutureType(method.getReturnType());
420   }
421 }
422