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