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