• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.model;
18 
19 import static com.google.common.collect.Sets.intersection;
20 import static com.google.common.graph.Graphs.inducedSubgraph;
21 import static com.google.common.graph.Graphs.reachableNodes;
22 import static com.google.common.graph.Graphs.transpose;
23 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
24 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
26 
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.ImmutableSetMultimap;
29 import com.google.common.graph.EndpointPair;
30 import com.google.common.graph.ImmutableNetwork;
31 import com.google.common.graph.MutableNetwork;
32 import com.google.common.graph.Network;
33 import com.google.common.graph.NetworkBuilder;
34 import dagger.Module;
35 import java.util.Optional;
36 import java.util.stream.Stream;
37 
38 /**
39  * A graph of bindings, dependency requests, and components.
40  *
41  * <p>A {@link BindingGraph} represents one of the following:
42  *
43  * <ul>
44  *   <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
45  *       dagger.producers.ProductionComponent}
46  *   <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
47  *       dagger.producers.ProductionSubcomponent} (only when the value of {@code
48  *       -Adagger.fullBindingGraphValidation} is not {@code NONE})
49  *   <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
50  *       including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
51  *       dagger.producers.ProducerModule#subcomponents()} ()}
52  * </ul>
53  *
54  * In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
55  * actually represent the module type. The graph will also be a {@linkplain #isFullBindingGraph()
56  * full binding graph}, which means it will contain all bindings in all modules, as well as nodes
57  * for their dependencies. Otherwise it will contain only bindings that are reachable from at least
58  * one {@linkplain #entryPointEdges() entry point}.
59  *
60  * <h3>Nodes</h3>
61  *
62  * <p>There is a <b>{@link Binding}</b> for each owned binding in the graph. If a binding is owned
63  * by more than one component, there is one binding object for that binding for every owning
64  * component.
65  *
66  * <p>There is a <b>{@linkplain ComponentNode component node}</b> (without a binding) for each
67  * component in the graph.
68  *
69  * <h3>Edges</h3>
70  *
71  * <p>There is a <b>{@linkplain DependencyEdge dependency edge}</b> for each dependency request in
72  * the graph. Its target node is the binding for the binding that satisfies the request. For entry
73  * point dependency requests, the source node is the component node for the component for which it
74  * is an entry point. For other dependency requests, the source node is the binding for the binding
75  * that contains the request.
76  *
77  * <p>There is a <b>subcomponent edge</b> for each parent-child component relationship in the graph.
78  * The target node is the component node for the child component. For subcomponents defined by a
79  * {@linkplain SubcomponentCreatorBindingEdge subcomponent creator binding} (either a method on the
80  * component or a set of {@code @Module.subcomponents} annotation values), the source node is the
81  * binding for the {@code @Subcomponent.Builder} type. For subcomponents defined by {@linkplain
82  * ChildFactoryMethodEdge subcomponent factory methods}, the source node is the component node for
83  * the parent.
84  *
85  * <p><b>Note that this API is experimental and will change.</b>
86  */
87 public abstract class BindingGraph {
88   /** Returns the graph in its {@link Network} representation. */
network()89   public abstract ImmutableNetwork<Node, Edge> network();
90 
91   @Override
toString()92   public String toString() {
93     return network().toString();
94   }
95 
96   /**
97    * Returns {@code true} if this graph was constructed from a module for full binding graph
98    * validation.
99    *
100    * @deprecated use {@link #isFullBindingGraph()} to tell if this is a full binding graph, or
101    *     {@link ComponentNode#isRealComponent() rootComponentNode().isRealComponent()} to tell if
102    *     the root component node is really a component or derived from a module. Dagger can generate
103    *     full binding graphs for components and subcomponents as well as modules.
104    */
105   @Deprecated
isModuleBindingGraph()106   public boolean isModuleBindingGraph() {
107     return !rootComponentNode().isRealComponent();
108   }
109 
110   /**
111    * Returns {@code true} if this is a full binding graph, which contains all bindings installed in
112    * the component, or {@code false} if it is a reachable binding graph, which contains only
113    * bindings that are reachable from at least one {@linkplain #entryPointEdges() entry point}.
114    *
115    * @see <a href="https://dagger.dev/compiler-options#full-binding-graph-validation">Full binding
116    *     graph validation</a>
117    */
isFullBindingGraph()118   public abstract boolean isFullBindingGraph();
119 
120   /**
121    * Returns {@code true} if the {@link #rootComponentNode()} is a subcomponent. This occurs in
122    * when {@code -Adagger.fullBindingGraphValidation} is used in a compilation with a subcomponent.
123    *
124    * @deprecated use {@link ComponentNode#isSubcomponent() rootComponentNode().isSubcomponent()}
125    *     instead
126    */
127   @Deprecated
isPartialBindingGraph()128   public boolean isPartialBindingGraph() {
129     return rootComponentNode().isSubcomponent();
130   }
131 
132   /** Returns the bindings. */
bindings()133   public ImmutableSet<Binding> bindings() {
134     return nodes(Binding.class);
135   }
136 
137   /** Returns the bindings for a key. */
bindings(Key key)138   public ImmutableSet<Binding> bindings(Key key) {
139     return nodes(Binding.class).stream()
140         .filter(binding -> binding.key().equals(key))
141         .collect(toImmutableSet());
142   }
143 
144   /** Returns the nodes that represent missing bindings. */
missingBindings()145   public ImmutableSet<MissingBinding> missingBindings() {
146     return nodes(MissingBinding.class);
147   }
148 
149   /** Returns the component nodes. */
componentNodes()150   public ImmutableSet<ComponentNode> componentNodes() {
151     return nodes(ComponentNode.class);
152   }
153 
154   /** Returns the component node for a component. */
componentNode(ComponentPath component)155   public Optional<ComponentNode> componentNode(ComponentPath component) {
156     return componentNodes().stream()
157         .filter(node -> node.componentPath().equals(component))
158         .findFirst();
159   }
160 
161   /** Returns the component nodes for a component. */
componentNodes(DaggerTypeElement component)162   public ImmutableSet<ComponentNode> componentNodes(DaggerTypeElement component) {
163     return componentNodes().stream()
164         .filter(node -> node.componentPath().currentComponent().equals(component))
165         .collect(toImmutableSet());
166   }
167 
168   /** Returns the component node for the root component. */
rootComponentNode()169   public ComponentNode rootComponentNode() {
170     return componentNodes().stream()
171         .filter(node -> node.componentPath().atRoot())
172         .findFirst()
173         .get();
174   }
175 
176   /** Returns the dependency edges. */
dependencyEdges()177   public ImmutableSet<DependencyEdge> dependencyEdges() {
178     return dependencyEdgeStream().collect(toImmutableSet());
179   }
180 
181   /**
182    * Returns the dependency edges for the dependencies of a binding. For valid graphs, each {@link
183    * DependencyRequest} will map to a single {@link DependencyEdge}. When conflicting bindings exist
184    * for a key, the multimap will have several edges for that {@link DependencyRequest}. Graphs that
185    * have no binding for a key will have an edge whose {@linkplain EndpointPair#target() target
186    * node} is a {@link MissingBinding}.
187    */
dependencyEdges( Binding binding)188   public ImmutableSetMultimap<DependencyRequest, DependencyEdge> dependencyEdges(
189       Binding binding) {
190     return dependencyEdgeStream(binding)
191         .collect(toImmutableSetMultimap(DependencyEdge::dependencyRequest, edge -> edge));
192   }
193 
194   /** Returns the dependency edges for a dependency request. */
dependencyEdges(DependencyRequest dependencyRequest)195   public ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
196     return dependencyEdgeStream()
197         .filter(edge -> edge.dependencyRequest().equals(dependencyRequest))
198         .collect(toImmutableSet());
199   }
200 
201   /**
202    * Returns the dependency edges for the entry points of a given {@code component}. Each edge's
203    * source node is that component's component node.
204    */
entryPointEdges(ComponentPath component)205   public ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
206     return dependencyEdgeStream(componentNode(component).get()).collect(toImmutableSet());
207   }
208 
dependencyEdgeStream(Node node)209   private Stream<DependencyEdge> dependencyEdgeStream(Node node) {
210     return network().outEdges(node).stream().flatMap(instancesOf(DependencyEdge.class));
211   }
212 
213   /**
214    * Returns the dependency edges for all entry points for all components and subcomponents. Each
215    * edge's source node is a component node.
216    */
entryPointEdges()217   public ImmutableSet<DependencyEdge> entryPointEdges() {
218     return entryPointEdgeStream().collect(toImmutableSet());
219   }
220 
221   /** Returns the binding or missing binding nodes that directly satisfy entry points. */
entryPointBindings()222   public ImmutableSet<MaybeBinding> entryPointBindings() {
223     return entryPointEdgeStream()
224         .map(edge -> (MaybeBinding) network().incidentNodes(edge).target())
225         .collect(toImmutableSet());
226   }
227 
228   /**
229    * Returns the edges for entry points that transitively depend on a binding or missing binding for
230    * a key.
231    */
entryPointEdgesDependingOnBinding( MaybeBinding binding)232   public ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
233       MaybeBinding binding) {
234     ImmutableNetwork<Node, DependencyEdge> dependencyGraph = dependencyGraph();
235     Network<Node, DependencyEdge> subgraphDependingOnBinding =
236         inducedSubgraph(
237             dependencyGraph, reachableNodes(transpose(dependencyGraph).asGraph(), binding));
238     return intersection(entryPointEdges(), subgraphDependingOnBinding.edges()).immutableCopy();
239   }
240 
241   /** Returns the bindings that directly request a given binding as a dependency. */
requestingBindings(MaybeBinding binding)242   public ImmutableSet<Binding> requestingBindings(MaybeBinding binding) {
243     return network().predecessors(binding).stream()
244         .flatMap(instancesOf(Binding.class))
245         .collect(toImmutableSet());
246   }
247 
248   /**
249    * Returns the bindings that a given binding directly requests as a dependency. Does not include
250    * any {@link MissingBinding}s.
251    *
252    * @see #requestedMaybeMissingBindings(Binding)
253    */
requestedBindings(Binding binding)254   public ImmutableSet<Binding> requestedBindings(Binding binding) {
255     return network().successors(binding).stream()
256         .flatMap(instancesOf(Binding.class))
257         .collect(toImmutableSet());
258   }
259 
260   /**
261    * Returns the bindings or missing bindings that a given binding directly requests as a
262    * dependency.
263    *
264    * @see #requestedBindings(Binding)
265    */
requestedMaybeMissingBindings(Binding binding)266   public ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
267     return network().successors(binding).stream()
268         .flatMap(instancesOf(MaybeBinding.class))
269         .collect(toImmutableSet());
270   }
271 
272   /** Returns a subnetwork that contains all nodes but only {@link DependencyEdge}s. */
273   // TODO(dpb): Make public. Cache.
dependencyGraph()274   private ImmutableNetwork<Node, DependencyEdge> dependencyGraph() {
275     MutableNetwork<Node, DependencyEdge> dependencyGraph =
276         NetworkBuilder.from(network())
277             .expectedNodeCount(network().nodes().size())
278             .expectedEdgeCount((int) dependencyEdgeStream().count())
279             .build();
280     network().nodes().forEach(dependencyGraph::addNode); // include disconnected nodes
281     dependencyEdgeStream()
282         .forEach(
283             edge -> {
284               EndpointPair<Node> endpoints = network().incidentNodes(edge);
285               dependencyGraph.addEdge(endpoints.source(), endpoints.target(), edge);
286             });
287     return ImmutableNetwork.copyOf(dependencyGraph);
288   }
289 
290   @SuppressWarnings({"rawtypes", "unchecked"})
nodes(Class<N> clazz)291   private <N extends Node> ImmutableSet<N> nodes(Class<N> clazz) {
292     return (ImmutableSet) nodesByClass().get(clazz);
293   }
294 
295   private static final ImmutableSet<Class<? extends Node>> NODE_TYPES =
296       ImmutableSet.of(Binding.class, MissingBinding.class, ComponentNode.class);
297 
nodesByClass()298   protected ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
299     return network().nodes().stream()
300         .collect(
301             toImmutableSetMultimap(
302                 node ->
303                     NODE_TYPES.stream().filter(clazz -> clazz.isInstance(node)).findFirst().get(),
304                 node -> node));
305   }
306 
dependencyEdgeStream()307   private Stream<DependencyEdge> dependencyEdgeStream() {
308     return network().edges().stream().flatMap(instancesOf(DependencyEdge.class));
309   }
310 
entryPointEdgeStream()311   private Stream<DependencyEdge> entryPointEdgeStream() {
312     return dependencyEdgeStream().filter(DependencyEdge::isEntryPoint);
313   }
314 
315   /**
316    * An edge in the binding graph. Either a {@link DependencyEdge}, a {@link
317    * ChildFactoryMethodEdge}, or a {@link SubcomponentCreatorBindingEdge}.
318    */
319   public interface Edge {}
320 
321   /**
322    * An edge that represents a dependency on a binding.
323    *
324    * <p>Because one {@link DependencyRequest} may represent a dependency from two bindings (e.g., a
325    * dependency of {@code Foo<String>} and {@code Foo<Number>} may have the same key and request
326    * element), this class does not override {@link #equals(Object)} to use value semantics.
327    *
328    * <p>For entry points, the source node is the {@link ComponentNode} that contains the entry
329    * point. Otherwise the source node is a {@link Binding}.
330    *
331    * <p>For dependencies on missing bindings, the target node is a {@link MissingBinding}. Otherwise
332    * the target node is a {@link Binding}.
333    */
334   public interface DependencyEdge extends Edge {
335     /** The dependency request. */
dependencyRequest()336     DependencyRequest dependencyRequest();
337 
338     /** Returns {@code true} if this edge represents an entry point. */
isEntryPoint()339     boolean isEntryPoint();
340   }
341 
342   /**
343    * An edge that represents a subcomponent factory method linking a parent component to a child
344    * subcomponent.
345    */
346   public interface ChildFactoryMethodEdge extends Edge {
347     /** The subcomponent factory method element. */
factoryMethod()348     DaggerExecutableElement factoryMethod();
349   }
350 
351   /**
352    * An edge that represents the link between a parent component and a child subcomponent implied by
353    * a subcomponent creator ({@linkplain dagger.Subcomponent.Builder builder} or {@linkplain
354    * dagger.Subcomponent.Factory factory}) binding.
355    *
356    * <p>The {@linkplain com.google.common.graph.EndpointPair#source() source node} of this edge is a
357    * {@link Binding} for the subcomponent creator {@link Key} and the {@linkplain
358    * com.google.common.graph.EndpointPair#target() target node} is a {@link ComponentNode} for the
359    * child subcomponent.
360    */
361   public interface SubcomponentCreatorBindingEdge extends Edge {
362     /**
363      * The modules that {@linkplain Module#subcomponents() declare the subcomponent} that generated
364      * this edge. Empty if the parent component has a subcomponent creator method and there are no
365      * declaring modules.
366      */
declaringModules()367     ImmutableSet<DaggerTypeElement> declaringModules();
368   }
369 
370   /** A node in the binding graph. Either a {@link Binding} or a {@link ComponentNode}. */
371   // TODO(dpb): Make all the node/edge types top-level.
372   public interface Node {
373     /** The component this node belongs to. */
componentPath()374     ComponentPath componentPath();
375   }
376 
377   /** A node in the binding graph that is either a {@link Binding} or a {@link MissingBinding}. */
378   public interface MaybeBinding extends Node {
379 
380     /** The component that owns the binding, or in which the binding is missing. */
381     @Override
componentPath()382     ComponentPath componentPath();
383 
384     /** The key of the binding, or for which there is no binding. */
key()385     Key key();
386 
387     /** The binding, or empty if missing. */
binding()388     Optional<Binding> binding();
389   }
390 
391   /** A node in the binding graph that represents a missing binding for a key in a component. */
392   public abstract static class MissingBinding implements MaybeBinding {
393     /** The component in which the binding is missing. */
394     @Override
componentPath()395     public abstract ComponentPath componentPath();
396 
397     /** The key for which there is no binding. */
398     @Override
key()399     public abstract Key key();
400 
401     /** @deprecated This always returns {@code Optional.empty()}. */
402     @Override
403     @Deprecated
binding()404     public Optional<Binding> binding() {
405       return Optional.empty();
406     }
407 
408     @Override
toString()409     public String toString() {
410       return String.format("missing binding for %s in %s", key(), componentPath());
411     }
412   }
413 
414   /**
415    * A <b>component node</b> in the graph. Every entry point {@linkplain DependencyEdge dependency
416    * edge}'s source node is a component node for the component containing the entry point.
417    */
418   public interface ComponentNode extends Node {
419 
420     /** The component represented by this node. */
421     @Override
componentPath()422     ComponentPath componentPath();
423 
424     /**
425      * Returns {@code true} if the component is a {@code @Subcomponent} or
426      * {@code @ProductionSubcomponent}.
427      */
isSubcomponent()428     boolean isSubcomponent();
429 
430     /**
431      * Returns {@code true} if the component is a real component, or {@code false} if it is a
432      * fictional component based on a module.
433      */
isRealComponent()434     boolean isRealComponent();
435 
436     /** The entry points on this component. */
entryPoints()437     ImmutableSet<DependencyRequest> entryPoints();
438 
439     /** The scopes declared on this component. */
scopes()440     ImmutableSet<Scope> scopes();
441   }
442 }
443