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