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