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