• 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 com.google.common.collect.Iterables.transform;
20 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
21 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
22 import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
23 import static dagger.internal.codegen.extension.DaggerStreams.stream;
24 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
27 
28 import androidx.room.compiler.processing.XExecutableElement;
29 import androidx.room.compiler.processing.XExecutableParameterElement;
30 import androidx.room.compiler.processing.XMethodElement;
31 import androidx.room.compiler.processing.XTypeElement;
32 import com.google.auto.value.AutoValue;
33 import com.google.auto.value.extension.memoized.Memoized;
34 import com.google.common.collect.ImmutableList;
35 import com.google.common.collect.ImmutableListMultimap;
36 import com.google.common.collect.ImmutableMap;
37 import com.google.common.collect.ImmutableSet;
38 import com.google.common.collect.ImmutableSetMultimap;
39 import com.google.common.collect.Maps;
40 import com.google.common.collect.Multimaps;
41 import com.google.common.collect.Sets;
42 import com.google.common.graph.ImmutableNetwork;
43 import com.google.common.graph.Traverser;
44 import dagger.internal.codegen.base.TarjanSCCs;
45 import dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge;
46 import dagger.internal.codegen.model.BindingGraph.ComponentNode;
47 import dagger.internal.codegen.model.BindingGraph.DependencyEdge;
48 import dagger.internal.codegen.model.BindingGraph.Edge;
49 import dagger.internal.codegen.model.BindingGraph.Node;
50 import dagger.internal.codegen.model.ComponentPath;
51 import dagger.internal.codegen.model.DaggerTypeElement;
52 import dagger.internal.codegen.model.DependencyRequest;
53 import dagger.internal.codegen.model.Key;
54 import java.util.Comparator;
55 import java.util.HashSet;
56 import java.util.LinkedHashMap;
57 import java.util.Map;
58 import java.util.Optional;
59 import java.util.Set;
60 import java.util.stream.Stream;
61 
62 /**
63  * A graph that represents a single component or subcomponent within a fully validated top-level
64  * binding graph.
65  */
66 @AutoValue
67 public abstract class BindingGraph {
68 
69   /**
70    * A graph that represents the entire network of nodes from all components, subcomponents and
71    * their bindings.
72    */
73   @AutoValue
74   public abstract static class TopLevelBindingGraph
75       extends dagger.internal.codegen.model.BindingGraph {
create( ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph)76     static TopLevelBindingGraph create(
77         ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph) {
78       TopLevelBindingGraph topLevelBindingGraph =
79           new AutoValue_BindingGraph_TopLevelBindingGraph(network, isFullBindingGraph);
80 
81       ImmutableMap<ComponentPath, ComponentNode> componentNodes =
82           topLevelBindingGraph.componentNodes().stream()
83               .collect(
84                   toImmutableMap(ComponentNode::componentPath, componentNode -> componentNode));
85 
86       ImmutableSetMultimap.Builder<ComponentNode, ComponentNode> subcomponentNodesBuilder =
87           ImmutableSetMultimap.builder();
88       topLevelBindingGraph.componentNodes().stream()
89           .filter(componentNode -> !componentNode.componentPath().atRoot())
90           .forEach(
91               componentNode ->
92                   subcomponentNodesBuilder.put(
93                       componentNodes.get(componentNode.componentPath().parent()), componentNode));
94 
95       // Set these fields directly on the instance rather than passing these in as input to the
96       // AutoValue to prevent exposing this data outside of the class.
97       topLevelBindingGraph.componentNodes = componentNodes;
98       topLevelBindingGraph.subcomponentNodes = subcomponentNodesBuilder.build();
99       topLevelBindingGraph.frameworkTypeBindings =
100           frameworkRequestBindingSet(network, topLevelBindingGraph.bindings());
101       return topLevelBindingGraph;
102     }
103 
104     private ImmutableMap<ComponentPath, ComponentNode> componentNodes;
105     private ImmutableSetMultimap<ComponentNode, ComponentNode> subcomponentNodes;
106     private ImmutableSet<Binding> frameworkTypeBindings;
107 
TopLevelBindingGraph()108     TopLevelBindingGraph() {}
109 
110     // This overrides dagger.internal.codegen.model.BindingGraph with a more efficient
111     // implementation.
112     @Override
componentNode(ComponentPath componentPath)113     public Optional<ComponentNode> componentNode(ComponentPath componentPath) {
114       return componentNodes.containsKey(componentPath)
115           ? Optional.of(componentNodes.get(componentPath))
116           : Optional.empty();
117     }
118 
119     /** Returns the set of subcomponent nodes of the given component node. */
subcomponentNodes(ComponentNode componentNode)120     ImmutableSet<ComponentNode> subcomponentNodes(ComponentNode componentNode) {
121       return subcomponentNodes.get(componentNode);
122     }
123 
124     @Override
125     @Memoized
nodesByClass()126     public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
127       return super.nodesByClass();
128     }
129 
130     /**
131      * Returns an index of each {@link BindingNode} by its {@link ComponentPath}. Accessing this for
132      * a component and its parent components is faster than doing a graph traversal.
133      */
134     @Memoized
bindingsByComponent()135     ImmutableListMultimap<ComponentPath, BindingNode> bindingsByComponent() {
136       return Multimaps.index(transform(bindings(), BindingNode.class::cast), Node::componentPath);
137     }
138 
139     /** Returns a {@link Comparator} in the same order as {@link Network#nodes()}. */
140     @Memoized
nodeOrder()141     Comparator<Node> nodeOrder() {
142       Map<Node, Integer> nodeOrderMap = Maps.newHashMapWithExpectedSize(network().nodes().size());
143       int i = 0;
144       for (Node node : network().nodes()) {
145         nodeOrderMap.put(node, i++);
146       }
147       return (n1, n2) -> nodeOrderMap.get(n1).compareTo(nodeOrderMap.get(n2));
148     }
149 
150     /** Returns the set of strongly connected nodes in this graph in reverse topological order. */
151     @Memoized
stronglyConnectedNodes()152     public ImmutableList<ImmutableSet<Node>> stronglyConnectedNodes() {
153       return TarjanSCCs.<Node>compute(
154           ImmutableSet.copyOf(network().nodes()),
155           // NetworkBuilder does not have a stable successor order, so we have to roll our own
156           // based on the node order, which is stable.
157           // TODO(bcorso): Fix once https://github.com/google/guava/issues/2650 is fixed.
158           node ->
159               network().successors(node).stream().sorted(nodeOrder()).collect(toImmutableList()));
160     }
161 
hasFrameworkRequest(Binding binding)162     public boolean hasFrameworkRequest(Binding binding) {
163       return frameworkTypeBindings.contains(binding);
164     }
165 
frameworkRequestBindingSet( ImmutableNetwork<Node, Edge> network, ImmutableSet<dagger.internal.codegen.model.Binding> bindings)166     private static ImmutableSet<Binding> frameworkRequestBindingSet(
167         ImmutableNetwork<Node, Edge> network,
168         ImmutableSet<dagger.internal.codegen.model.Binding> bindings) {
169       Set<Binding> frameworkRequestBindings = new HashSet<>();
170       for (dagger.internal.codegen.model.Binding binding : bindings) {
171         ImmutableList<DependencyEdge> edges =
172             network.inEdges(binding).stream()
173                 .flatMap(instancesOf(DependencyEdge.class))
174                 .collect(toImmutableList());
175         for (DependencyEdge edge : edges) {
176           DependencyRequest request = edge.dependencyRequest();
177           switch (request.kind()) {
178             case INSTANCE:
179             case FUTURE:
180               continue;
181             case PRODUCED:
182             case PRODUCER:
183             case MEMBERS_INJECTION:
184             case PROVIDER_OF_LAZY:
185             case LAZY:
186             case PROVIDER:
187               frameworkRequestBindings.add(((BindingNode) binding).delegate());
188               break;
189           }
190         }
191       }
192       return ImmutableSet.copyOf(frameworkRequestBindings);
193     }
194   }
195 
create( ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph)196   static BindingGraph create(
197       ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph) {
198     return create(Optional.empty(), componentNode, topLevelBindingGraph);
199   }
200 
create( Optional<BindingGraph> parent, ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph)201   private static BindingGraph create(
202       Optional<BindingGraph> parent,
203       ComponentNode componentNode,
204       TopLevelBindingGraph topLevelBindingGraph) {
205     // TODO(bcorso): Mapping binding nodes by key is flawed since bindings that depend on local
206     // multibindings can have multiple nodes (one in each component). In this case, we choose the
207     // node in the child-most component since this is likely the node that users of this
208     // BindingGraph will want (and to remain consistent with LegacyBindingGraph). However, ideally
209     // we would avoid this ambiguity by getting dependencies directly from the top-level network.
210     // In particular, rather than using a Binding's list of DependencyRequests (which only
211     // contains the key) we would use the top-level network to find the DependencyEdges for a
212     // particular BindingNode.
213     Map<Key, BindingNode> contributionBindings = new LinkedHashMap<>();
214     Map<Key, BindingNode> membersInjectionBindings = new LinkedHashMap<>();
215     topLevelBindingGraph.bindingsByComponent().get(componentNode.componentPath())
216         .forEach(
217             bindingNode -> {
218               if (bindingNode.delegate() instanceof ContributionBinding) {
219                 contributionBindings.putIfAbsent(bindingNode.key(), bindingNode);
220               } else if (bindingNode.delegate() instanceof MembersInjectionBinding) {
221                 membersInjectionBindings.putIfAbsent(bindingNode.key(), bindingNode);
222               } else {
223                 throw new AssertionError("Unexpected binding node type: " + bindingNode.delegate());
224               }
225             });
226 
227     BindingGraph bindingGraph = new AutoValue_BindingGraph(componentNode, topLevelBindingGraph);
228 
229     ImmutableSet<XTypeElement> modules =
230         ((ComponentNodeImpl) componentNode).componentDescriptor().modules().stream()
231             .map(ModuleDescriptor::moduleElement)
232             .collect(toImmutableSet());
233 
234     ImmutableSet<XTypeElement> inheritedModules =
235         parent.isPresent()
236             ? Sets.union(parent.get().ownedModules, parent.get().inheritedModules).immutableCopy()
237             : ImmutableSet.of();
238 
239     // Set these fields directly on the instance rather than passing these in as input to the
240     // AutoValue to prevent exposing this data outside of the class.
241     bindingGraph.parent = parent;
242     bindingGraph.inheritedModules = inheritedModules;
243     bindingGraph.ownedModules = Sets.difference(modules, inheritedModules).immutableCopy();
244     bindingGraph.contributionBindings = ImmutableMap.copyOf(contributionBindings);
245     bindingGraph.membersInjectionBindings = ImmutableMap.copyOf(membersInjectionBindings);
246     bindingGraph.bindingModules =
247         contributionBindings.values().stream()
248             .map(BindingNode::contributingModule)
249             .flatMap(presentValues())
250             .map(DaggerTypeElement::xprocessing)
251             .collect(toImmutableSet());
252 
253     return bindingGraph;
254   }
255 
256   private Optional<BindingGraph> parent;
257   private ImmutableMap<Key, BindingNode> contributionBindings;
258   private ImmutableMap<Key, BindingNode> membersInjectionBindings;
259   private ImmutableSet<XTypeElement> inheritedModules;
260   private ImmutableSet<XTypeElement> ownedModules;
261   private ImmutableSet<XTypeElement> bindingModules;
262 
BindingGraph()263   BindingGraph() {}
264 
265   /** Returns the {@link ComponentNode} for this graph. */
componentNode()266   public abstract ComponentNode componentNode();
267 
268   /** Returns the {@link ComponentPath} for this graph. */
componentPath()269   public final ComponentPath componentPath() {
270     return componentNode().componentPath();
271   }
272 
273   /** Returns the {@link TopLevelBindingGraph} from which this graph is contained. */
topLevelBindingGraph()274   public abstract TopLevelBindingGraph topLevelBindingGraph();
275 
276   /** Returns the {@link ComponentDescriptor} for this graph */
componentDescriptor()277   public final ComponentDescriptor componentDescriptor() {
278     return ((ComponentNodeImpl) componentNode()).componentDescriptor();
279   }
280 
281   /**
282    * Returns the {@link ContributionBinding} for the given {@link Key} in this component or {@link
283    * Optional#empty()} if one doesn't exist.
284    */
localContributionBinding(Key key)285   public final Optional<Binding> localContributionBinding(Key key) {
286     return contributionBindings.containsKey(key)
287         ? Optional.of(contributionBindings.get(key).delegate())
288         : Optional.empty();
289   }
290 
291   /**
292    * Returns the {@link MembersInjectionBinding} for the given {@link Key} in this component or
293    * {@link Optional#empty()} if one doesn't exist.
294    */
localMembersInjectionBinding(Key key)295   public final Optional<Binding> localMembersInjectionBinding(Key key) {
296     return membersInjectionBindings.containsKey(key)
297         ? Optional.of(membersInjectionBindings.get(key).delegate())
298         : Optional.empty();
299   }
300 
301   /** Returns the {@link ContributionBinding} for the given {@link Key}. */
contributionBinding(Key key)302   public final ContributionBinding contributionBinding(Key key) {
303     if (contributionBindings.containsKey(key)) {
304       return (ContributionBinding) contributionBindings.get(key).delegate();
305     } else if (parent.isPresent()) {
306       return parent.get().contributionBinding(key);
307     }
308     throw new AssertionError("Contribution binding not found for key: " + key);
309   }
310 
311   /**
312    * Returns the {@link MembersInjectionBinding} for the given {@link Key} or {@link
313    * Optional#empty()} if one does not exist.
314    */
membersInjectionBinding(Key key)315   public final Optional<MembersInjectionBinding> membersInjectionBinding(Key key) {
316     if (membersInjectionBindings.containsKey(key)) {
317       return Optional.of((MembersInjectionBinding) membersInjectionBindings.get(key).delegate());
318     } else if (parent.isPresent()) {
319       return parent.get().membersInjectionBinding(key);
320     }
321     return Optional.empty();
322   }
323 
324   /** Returns the {@link XTypeElement} for the component this graph represents. */
componentTypeElement()325   public final XTypeElement componentTypeElement() {
326     return componentPath().currentComponent().xprocessing();
327   }
328 
329   /**
330    * Returns the set of modules that are owned by this graph regardless of whether or not any of
331    * their bindings are used in this graph. For graphs representing top-level {@link
332    * dagger.Component components}, this set will be the same as {@linkplain
333    * ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
334    * subcomponents}, this set will be the transitive modules that are not owned by any of their
335    * ancestors.
336    */
ownedModuleTypes()337   public final ImmutableSet<XTypeElement> ownedModuleTypes() {
338     return ownedModules;
339   }
340 
341   /**
342    * Returns the factory method for this subcomponent, if it exists.
343    *
344    * <p>This factory method is the one defined in the parent component's interface.
345    *
346    * <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
347    * would return the {@link XExecutableElement}: {@code childComponent(ChildModule1)} .
348    *
349    * <pre><code>
350    *   {@literal @Component}
351    *   interface ParentComponent {
352    *     ChildComponent childComponent(ChildModule1 childModule);
353    *   }
354    * </code></pre>
355    */
356   // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
factoryMethod()357   public final Optional<XMethodElement> factoryMethod() {
358     return topLevelBindingGraph().network().inEdges(componentNode()).stream()
359         .filter(edge -> edge instanceof ChildFactoryMethodEdge)
360         .map(edge -> ((ChildFactoryMethodEdge) edge).factoryMethod().xprocessing())
361         // Factory methods are represented by XMethodElement (rather than XConstructorElement)
362         // TODO(bcorso): consider adding DaggerMethodElement so this cast isn't needed.
363         .map(XMethodElement.class::cast)
364         .collect(toOptional());
365   }
366 
367   /**
368    * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
369    * corresponding {@link XExecutableParameterElement} for each module parameter in the {@linkplain
370    * BindingGraph#factoryMethod factory method}.
371    */
372   // TODO(dpb): Consider disallowing modules if none of their bindings are used.
373   public final ImmutableMap<ComponentRequirement, XExecutableParameterElement>
factoryMethodParameters()374       factoryMethodParameters() {
375     return factoryMethod().get().getParameters().stream()
376         .collect(
377             toImmutableMap(
378                 parameter -> ComponentRequirement.forModule(parameter.getType()),
379                 parameter -> parameter));
380   }
381 
382   /**
383    * The types for which the component needs instances.
384    *
385    * <ul>
386    *   <li>component dependencies
387    *   <li>owned modules with concrete instance bindings that are used in the graph
388    *   <li>bound instances
389    * </ul>
390    */
391   @Memoized
componentRequirements()392   public ImmutableSet<ComponentRequirement> componentRequirements() {
393     ImmutableSet<XTypeElement> requiredModules =
394         stream(Traverser.forTree(BindingGraph::subgraphs).depthFirstPostOrder(this))
395             .flatMap(graph -> graph.bindingModules.stream())
396             .filter(ownedModules::contains)
397             .collect(toImmutableSet());
398     ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
399     componentDescriptor().requirements().stream()
400         .filter(
401             requirement ->
402                 !requirement.kind().isModule()
403                     || requiredModules.contains(requirement.typeElement()))
404         .forEach(requirements::add);
405     if (factoryMethod().isPresent()) {
406       requirements.addAll(factoryMethodParameters().keySet());
407     }
408     return requirements.build();
409   }
410 
411   /**
412    * Returns all {@link ComponentDescriptor}s in the {@link TopLevelBindingGraph} mapped by the
413    * component path.
414    */
415   @Memoized
componentDescriptorsByPath()416   public ImmutableMap<ComponentPath, ComponentDescriptor> componentDescriptorsByPath() {
417     return topLevelBindingGraph().componentNodes().stream()
418         .map(ComponentNodeImpl.class::cast)
419         .collect(
420             toImmutableMap(ComponentNode::componentPath, ComponentNodeImpl::componentDescriptor));
421   }
422 
423   @Memoized
subgraphs()424   public ImmutableList<BindingGraph> subgraphs() {
425     return topLevelBindingGraph().subcomponentNodes(componentNode()).stream()
426         .map(subcomponent -> create(Optional.of(this), subcomponent, topLevelBindingGraph()))
427         .collect(toImmutableList());
428   }
429 
430   /** Returns the list of all {@link BindingNode}s local to this component. */
localBindingNodes()431   public ImmutableList<BindingNode> localBindingNodes() {
432     return topLevelBindingGraph().bindingsByComponent().get(componentPath());
433   }
434 
435   // TODO(bcorso): This method can be costly. Consider removing this method and inlining it into its
436   // only usage, BindingGraphJsonGenerator.
bindingNodes()437   public ImmutableSet<BindingNode> bindingNodes() {
438     // Construct the set of bindings by iterating bindings from this component and then from each
439     // successive parent. If a binding exists in multiple components, this order ensures that the
440     // child-most binding is always chosen first.
441     Map<Key, BindingNode> bindings = new LinkedHashMap<>();
442     Stream.iterate(componentPath(), ComponentPath::parent)
443         // Stream.iterate() is infinite stream so we need limit it to the known size of the path.
444         .limit(componentPath().components().size())
445         .flatMap(path -> topLevelBindingGraph().bindingsByComponent().get(path).stream())
446         .forEach(bindingNode -> bindings.putIfAbsent(bindingNode.key(), bindingNode));
447     return ImmutableSet.copyOf(bindings.values());
448   }
449 }
450