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