• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 Google, Inc.
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 package dagger.internal.codegen;
17 
18 import com.google.auto.common.MoreTypes;
19 import com.google.auto.value.AutoValue;
20 import com.google.common.base.Function;
21 import com.google.common.base.Optional;
22 import com.google.common.cache.Cache;
23 import com.google.common.cache.CacheBuilder;
24 import com.google.common.collect.FluentIterable;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.ImmutableSetMultimap;
29 import com.google.common.collect.Iterables;
30 import com.google.common.collect.Lists;
31 import com.google.common.collect.Maps;
32 import com.google.common.collect.Sets;
33 import com.google.common.collect.TreeTraverser;
34 import dagger.Component;
35 import dagger.Subcomponent;
36 import dagger.internal.codegen.Binding.Type;
37 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
38 import dagger.producers.Producer;
39 import dagger.producers.ProductionComponent;
40 import java.util.ArrayDeque;
41 import java.util.Collection;
42 import java.util.Deque;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Map.Entry;
47 import java.util.Set;
48 import java.util.concurrent.Callable;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.Executor;
51 import javax.inject.Inject;
52 import javax.lang.model.element.AnnotationMirror;
53 import javax.lang.model.element.ExecutableElement;
54 import javax.lang.model.element.TypeElement;
55 import javax.lang.model.util.ElementFilter;
56 import javax.lang.model.util.Elements;
57 
58 import static com.google.auto.common.MoreElements.getAnnotationMirror;
59 import static com.google.common.base.Predicates.in;
60 import static com.google.common.base.Verify.verify;
61 import static com.google.common.collect.Iterables.any;
62 import static com.google.common.collect.Sets.union;
63 import static dagger.internal.codegen.BindingKey.Kind.CONTRIBUTION;
64 import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
65 import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
66 import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
67 import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT_BUILDER;
68 import static dagger.internal.codegen.ComponentDescriptor.Kind.PRODUCTION_COMPONENT;
69 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
70 import static dagger.internal.codegen.MembersInjectionBinding.Strategy.INJECT_MEMBERS;
71 import static dagger.internal.codegen.MembersInjectionBinding.Strategy.NO_OP;
72 import static javax.lang.model.element.Modifier.STATIC;
73 
74 /**
75  * The canonical representation of a full-resolved graph.
76  *
77  * @author Gregory Kick
78  */
79 @AutoValue
80 abstract class BindingGraph {
componentDescriptor()81   abstract ComponentDescriptor componentDescriptor();
resolvedBindings()82   abstract ImmutableMap<BindingKey, ResolvedBindings> resolvedBindings();
subgraphs()83   abstract ImmutableMap<ExecutableElement, BindingGraph> subgraphs();
84 
85   /**
86    * Returns the set of modules that are owned by this graph regardless of whether or not any of
87    * their bindings are used in this graph. For graphs representing top-level {@link Component
88    * components}, this set will be the same as
89    * {@linkplain ComponentDescriptor#transitiveModules the component's transitive modules}. For
90    * {@linkplain Subcomponent subcomponents}, this set will be the transitive modules that are not
91    * owned by any of their ancestors.
92    */
ownedModules()93   abstract ImmutableSet<ModuleDescriptor> ownedModules();
94 
ownedModuleTypes()95   ImmutableSet<TypeElement> ownedModuleTypes() {
96     return FluentIterable.from(ownedModules())
97         .transform(ModuleDescriptor.getModuleElement())
98         .toSet();
99   }
100 
101   private static final TreeTraverser<BindingGraph> SUBGRAPH_TRAVERSER =
102       new TreeTraverser<BindingGraph>() {
103         @Override
104         public Iterable<BindingGraph> children(BindingGraph node) {
105           return node.subgraphs().values();
106         }
107       };
108 
109   /**
110    * Returns the set of types necessary to implement the component, but are not part of the injected
111    * graph.  This includes modules, component dependencies and an {@link Executor} in the case of
112    * {@link ProductionComponent}.
113    */
componentRequirements()114   ImmutableSet<TypeElement> componentRequirements() {
115     return SUBGRAPH_TRAVERSER
116         .preOrderTraversal(this)
117         .transformAndConcat(
118             new Function<BindingGraph, Iterable<ResolvedBindings>>() {
119               @Override
120               public Iterable<ResolvedBindings> apply(BindingGraph input) {
121                 return input.resolvedBindings().values();
122               }
123             })
124         .transformAndConcat(
125             new Function<ResolvedBindings, Set<ContributionBinding>>() {
126               @Override
127               public Set<ContributionBinding> apply(ResolvedBindings input) {
128                 return (input.bindingKey().kind().equals(CONTRIBUTION))
129                     ? input.contributionBindings()
130                     : ImmutableSet.<ContributionBinding>of();
131               }
132             })
133         .transformAndConcat(
134             new Function<ContributionBinding, Set<TypeElement>>() {
135               @Override
136               public Set<TypeElement> apply(ContributionBinding input) {
137                 return input.bindingElement().getModifiers().contains(STATIC)
138                     ? ImmutableSet.<TypeElement>of()
139                     : input.contributedBy().asSet();
140               }
141             })
142         .filter(in(ownedModuleTypes()))
143         .append(componentDescriptor().dependencies())
144         .append(componentDescriptor().executorDependency().asSet())
145         .toSet();
146   }
147 
148   ImmutableSet<TypeElement> availableDependencies() {
149     return new ImmutableSet.Builder<TypeElement>()
150         .addAll(componentDescriptor().transitiveModuleTypes())
151         .addAll(componentDescriptor().dependencies())
152         .addAll(componentDescriptor().executorDependency().asSet())
153         .build();
154   }
155 
156   static final class Factory {
157     private final Elements elements;
158     private final InjectBindingRegistry injectBindingRegistry;
159     private final Key.Factory keyFactory;
160     private final ProvisionBinding.Factory provisionBindingFactory;
161     private final ProductionBinding.Factory productionBindingFactory;
162 
163     Factory(Elements elements,
164         InjectBindingRegistry injectBindingRegistry,
165         Key.Factory keyFactory,
166         ProvisionBinding.Factory provisionBindingFactory,
167         ProductionBinding.Factory productionBindingFactory) {
168       this.elements = elements;
169       this.injectBindingRegistry = injectBindingRegistry;
170       this.keyFactory = keyFactory;
171       this.provisionBindingFactory = provisionBindingFactory;
172       this.productionBindingFactory = productionBindingFactory;
173     }
174 
175     BindingGraph create(ComponentDescriptor componentDescriptor) {
176       return create(Optional.<Resolver>absent(), componentDescriptor);
177     }
178 
179     private BindingGraph create(
180         Optional<Resolver> parentResolver, ComponentDescriptor componentDescriptor) {
181       ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
182 
183       // binding for the component itself
184       TypeElement componentDefinitionType = componentDescriptor.componentDefinitionType();
185       explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDefinitionType));
186 
187       // Collect Component dependencies.
188       Optional<AnnotationMirror> componentMirror =
189           getAnnotationMirror(componentDefinitionType, Component.class)
190               .or(getAnnotationMirror(componentDefinitionType, ProductionComponent.class));
191       ImmutableSet<TypeElement> componentDependencyTypes = componentMirror.isPresent()
192           ? MoreTypes.asTypeElements(getComponentDependencies(componentMirror.get()))
193           : ImmutableSet.<TypeElement>of();
194       for (TypeElement componentDependency : componentDependencyTypes) {
195         explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDependency));
196         List<ExecutableElement> dependencyMethods =
197             ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
198         for (ExecutableElement method : dependencyMethods) {
199           // MembersInjection methods aren't "provided" explicitly, so ignore them.
200           if (isComponentContributionMethod(elements, method)) {
201             explicitBindingsBuilder.add(
202                 componentDescriptor.kind().equals(PRODUCTION_COMPONENT)
203                         && isComponentProductionMethod(elements, method)
204                     ? productionBindingFactory.forComponentMethod(method)
205                     : provisionBindingFactory.forComponentMethod(method));
206           }
207         }
208       }
209 
210       // Bindings for subcomponent builders.
211       for (ComponentMethodDescriptor subcomponentMethodDescriptor :
212           Iterables.filter(
213               componentDescriptor.subcomponents().keySet(), isOfKind(SUBCOMPONENT_BUILDER))) {
214         explicitBindingsBuilder.add(
215             provisionBindingFactory.forSubcomponentBuilderMethod(
216                 subcomponentMethodDescriptor.methodElement(),
217                 componentDescriptor.componentDefinitionType()));
218       }
219 
220       // Collect transitive module bindings.
221       for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) {
222         for (ContributionBinding binding : moduleDescriptor.bindings()) {
223           explicitBindingsBuilder.add(binding);
224         }
225       }
226 
227       Resolver requestResolver =
228           new Resolver(
229               parentResolver,
230               componentDescriptor,
231               explicitBindingsByKey(explicitBindingsBuilder.build()));
232       for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
233         Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
234         if (componentMethodRequest.isPresent()) {
235           requestResolver.resolve(componentMethodRequest.get());
236         }
237       }
238 
239       ImmutableMap.Builder<ExecutableElement, BindingGraph> subgraphsBuilder =
240           ImmutableMap.builder();
241       for (Entry<ComponentMethodDescriptor, ComponentDescriptor> subcomponentEntry :
242           componentDescriptor.subcomponents().entrySet()) {
243         subgraphsBuilder.put(
244             subcomponentEntry.getKey().methodElement(),
245             create(Optional.of(requestResolver), subcomponentEntry.getValue()));
246       }
247 
248       for (ResolvedBindings resolvedBindings : requestResolver.getResolvedBindings().values()) {
249         verify(
250             resolvedBindings.owningComponent().equals(componentDescriptor),
251             "%s is not owned by %s",
252             resolvedBindings,
253             componentDescriptor);
254       }
255 
256       return new AutoValue_BindingGraph(
257           componentDescriptor,
258           requestResolver.getResolvedBindings(),
259           subgraphsBuilder.build(),
260           requestResolver.getOwnedModules());
261     }
262 
263     private <B extends ContributionBinding> ImmutableSetMultimap<Key, B> explicitBindingsByKey(
264         Iterable<? extends B> bindings) {
265       // Multimaps.index() doesn't do ImmutableSetMultimaps.
266       ImmutableSetMultimap.Builder<Key, B> builder = ImmutableSetMultimap.builder();
267       for (B binding : bindings) {
268         builder.put(binding.key(), binding);
269       }
270       return builder.build();
271     }
272 
273     private final class Resolver {
274       final Optional<Resolver> parentResolver;
275       final ComponentDescriptor componentDescriptor;
276       final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
277       final ImmutableSet<ContributionBinding> explicitBindingsSet;
278       final Map<BindingKey, ResolvedBindings> resolvedBindings;
279       final Deque<BindingKey> cycleStack = new ArrayDeque<>();
280       final Cache<BindingKey, Boolean> dependsOnLocalMultibindingsCache =
281           CacheBuilder.newBuilder().<BindingKey, Boolean>build();
282 
283       Resolver(
284           Optional<Resolver> parentResolver,
285           ComponentDescriptor componentDescriptor,
286           ImmutableSetMultimap<Key, ContributionBinding> explicitBindings) {
287         assert parentResolver != null;
288         this.parentResolver = parentResolver;
289         assert componentDescriptor != null;
290         this.componentDescriptor = componentDescriptor;
291         assert explicitBindings != null;
292         this.explicitBindings = explicitBindings;
293         this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
294         this.resolvedBindings = Maps.newLinkedHashMap();
295       }
296 
297       /**
298        * Looks up the bindings associated with a given dependency request and returns them.
299        *
300        * <p>Requests for {@code Map<K, V>} for which there are only bindings for
301        * {@code Map<K, Provider<V>>} will resolve to a single implicit binding for the latter map
302        * (and similarly for {@link Producer}s).
303        *
304        * <p>If there are no explicit bindings for a contribution, looks for implicit
305        * {@link Inject @Inject}-annotated constructor types.
306        */
307       ResolvedBindings lookUpBindings(DependencyRequest request) {
308         BindingKey bindingKey = request.bindingKey();
309         switch (bindingKey.kind()) {
310           case CONTRIBUTION:
311             // First, check for explicit keys (those from modules and components)
312             ImmutableSet<ContributionBinding> explicitBindingsForKey =
313                 getExplicitBindings(bindingKey.key());
314 
315             // If the key is Map<K, V>, get its implicit binding keys, which are either
316             // Map<K, Provider<V>> or Map<K, Producer<V>>, and grab their explicit bindings.
317             Optional<Key> mapProviderKey = keyFactory.implicitMapProviderKeyFrom(bindingKey.key());
318             ImmutableSet.Builder<ContributionBinding> explicitMapBindingsBuilder =
319                 ImmutableSet.builder();
320             if (mapProviderKey.isPresent()) {
321               explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProviderKey.get()));
322             }
323 
324             Optional<Key> mapProducerKey = keyFactory.implicitMapProducerKeyFrom(bindingKey.key());
325             if (mapProducerKey.isPresent()) {
326               explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProducerKey.get()));
327             }
328             ImmutableSet<ContributionBinding> explicitMapBindings =
329                 explicitMapBindingsBuilder.build();
330 
331             // If the key is Set<Produced<T>>, then we look up bindings by the alternate key Set<T>.
332             Optional<Key> setKeyFromProduced =
333                 keyFactory.implicitSetKeyFromProduced(bindingKey.key());
334             ImmutableSet<ContributionBinding> explicitSetBindings =
335                 setKeyFromProduced.isPresent()
336                     ? getExplicitBindings(setKeyFromProduced.get())
337                     : ImmutableSet.<ContributionBinding>of();
338 
339             if (!explicitBindingsForKey.isEmpty() || !explicitSetBindings.isEmpty()) {
340               /* If there are any explicit bindings for this key, then combine those with any
341                * conflicting Map<K, Provider<V>> bindings and let the validator fail. */
342               ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> bindings =
343                   ImmutableSetMultimap.builder();
344               for (ContributionBinding binding :
345                   union(explicitBindingsForKey, union(explicitSetBindings, explicitMapBindings))) {
346                 bindings.put(getOwningComponent(request, binding), binding);
347               }
348               return ResolvedBindings.forContributionBindings(
349                   bindingKey, componentDescriptor, bindings.build());
350             } else if (any(explicitMapBindings, Binding.Type.PRODUCTION)) {
351               /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
352                * some explicit Map<K, Producer<V>> bindings, then this binding must have only the
353                * implicit dependency on Map<K, Producer<V>>. */
354               return ResolvedBindings.forContributionBindings(
355                   bindingKey,
356                   componentDescriptor,
357                   productionBindingFactory.implicitMapOfProducerBinding(request));
358             } else if (any(explicitMapBindings, Binding.Type.PROVISION)) {
359               /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
360                * some explicit Map<K, Provider<V>> bindings, then this binding must have only the
361                * implicit dependency on Map<K, Provider<V>>. */
362               return ResolvedBindings.forContributionBindings(
363                   bindingKey,
364                   componentDescriptor,
365                   provisionBindingFactory.implicitMapOfProviderBinding(request));
366             } else {
367               /* If there are no explicit bindings at all, look for an implicit @Inject-constructed
368                * binding. */
369               Optional<ProvisionBinding> provisionBinding =
370                   injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key());
371               ComponentDescriptor owningComponent =
372                   provisionBinding.isPresent()
373                           && isResolvedInParent(request, provisionBinding.get())
374                           && !shouldOwnParentBinding(request, provisionBinding.get())
375                       ? getOwningResolver(provisionBinding.get()).get().componentDescriptor
376                       : componentDescriptor;
377               return ResolvedBindings.forContributionBindings(
378                   bindingKey,
379                   componentDescriptor,
380                   ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
381                       .putAll(owningComponent, provisionBinding.asSet())
382                       .build());
383             }
384 
385           case MEMBERS_INJECTION:
386             // no explicit deps for members injection, so just look it up
387             return ResolvedBindings.forMembersInjectionBinding(
388                 bindingKey, componentDescriptor, rollUpMembersInjectionBindings(bindingKey.key()));
389           default:
390             throw new AssertionError();
391         }
392       }
393 
394       /**
395        * If {@code binding} should be owned by a parent component, resolves the binding in that
396        * component's resolver and returns that component. Otherwise returns the component for this
397        * resolver.
398        */
399       private ComponentDescriptor getOwningComponent(
400           DependencyRequest request, ContributionBinding binding) {
401         return isResolvedInParent(request, binding) && !shouldOwnParentBinding(request, binding)
402             ? getOwningResolver(binding).get().componentDescriptor
403             : componentDescriptor;
404       }
405 
406       /**
407        * Returns {@code true} if {@code binding} is owned by a parent resolver. If so, calls
408        * {@link #resolve(DependencyRequest) resolve(request)} on that resolver.
409        */
410       private boolean isResolvedInParent(DependencyRequest request, ContributionBinding binding) {
411         Optional<Resolver> owningResolver = getOwningResolver(binding);
412         if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
413           owningResolver.get().resolve(request);
414           return true;
415         } else {
416           return false;
417         }
418       }
419 
420       /**
421        * Returns {@code true} if {@code binding}, which was previously resolved by a parent
422        * resolver, should be moved into this resolver's bindings for {@code request} because it is
423        * unscoped and {@linkplain #dependsOnLocalMultibindings(ResolvedBindings) depends on local
424        * multibindings}, or {@code false} if it can satisfy {@code request} as an inherited binding.
425        */
426       private boolean shouldOwnParentBinding(
427           DependencyRequest request, ContributionBinding binding) {
428         return !binding.scope().isPresent()
429             && dependsOnLocalMultibindings(
430                 getPreviouslyResolvedBindings(request.bindingKey()).get());
431       }
432 
433       private MembersInjectionBinding rollUpMembersInjectionBindings(Key key) {
434         MembersInjectionBinding membersInjectionBinding =
435             injectBindingRegistry.getOrFindMembersInjectionBinding(key);
436 
437         if (membersInjectionBinding.parentInjectorRequest().isPresent()
438             && membersInjectionBinding.injectionStrategy().equals(INJECT_MEMBERS)) {
439           MembersInjectionBinding parentBinding =
440               rollUpMembersInjectionBindings(
441                   membersInjectionBinding.parentInjectorRequest().get().key());
442           if (parentBinding.injectionStrategy().equals(NO_OP)) {
443             return membersInjectionBinding.withoutParentInjectorRequest();
444           }
445         }
446 
447         return membersInjectionBinding;
448       }
449 
450       private Optional<Resolver> getOwningResolver(ContributionBinding provisionBinding) {
451         for (Resolver requestResolver : getResolverLineage().reverse()) {
452           if (requestResolver.explicitBindingsSet.contains(provisionBinding)) {
453             return Optional.of(requestResolver);
454           }
455         }
456 
457         // look for scope separately.  we do this for the case where @Singleton can appear twice
458         // in the † compatibility mode
459         Scope bindingScope = provisionBinding.scope();
460         if (bindingScope.isPresent()) {
461           for (Resolver requestResolver : getResolverLineage().reverse()) {
462             if (bindingScope.equals(requestResolver.componentDescriptor.scope())) {
463               return Optional.of(requestResolver);
464             }
465           }
466         }
467         return Optional.absent();
468       }
469 
470       /** Returns the resolver lineage from parent to child. */
471       private ImmutableList<Resolver> getResolverLineage() {
472         List<Resolver> resolverList = Lists.newArrayList();
473         for (Optional<Resolver> currentResolver = Optional.of(this);
474             currentResolver.isPresent();
475             currentResolver = currentResolver.get().parentResolver) {
476           resolverList.add(currentResolver.get());
477         }
478         return ImmutableList.copyOf(Lists.reverse(resolverList));
479       }
480 
481       private ImmutableSet<ContributionBinding> getExplicitBindings(Key requestKey) {
482         ImmutableSet.Builder<ContributionBinding> explicitBindingsForKey = ImmutableSet.builder();
483         for (Resolver resolver : getResolverLineage()) {
484           explicitBindingsForKey.addAll(resolver.explicitBindings.get(requestKey));
485         }
486         return explicitBindingsForKey.build();
487       }
488 
489       private Optional<ResolvedBindings> getPreviouslyResolvedBindings(
490           final BindingKey bindingKey) {
491         Optional<ResolvedBindings> result = Optional.fromNullable(resolvedBindings.get(bindingKey));
492         if (result.isPresent()) {
493           return result;
494         } else if (parentResolver.isPresent()) {
495           return parentResolver.get().getPreviouslyResolvedBindings(bindingKey);
496         } else {
497           return Optional.absent();
498         }
499       }
500 
501       void resolve(DependencyRequest request) {
502         BindingKey bindingKey = request.bindingKey();
503 
504         // If we find a cycle, stop resolving. The original request will add it with all of the
505         // other resolved deps.
506         if (cycleStack.contains(bindingKey)) {
507           return;
508         }
509 
510         // If the binding was previously resolved in this (sub)component, don't resolve it again.
511         if (resolvedBindings.containsKey(bindingKey)) {
512           return;
513         }
514 
515         // If the binding was previously resolved in a supercomponent, then test to see if it
516         // depends on multibindings with contributions from this subcomponent. If it does, then we
517         // have to resolve it in this subcomponent so that it sees the local contributions. If it
518         // does not, then we can stop resolving it in this subcomponent and rely on the
519         // supercomponent resolution.
520         Optional<ResolvedBindings> bindingsPreviouslyResolvedInParent =
521             getPreviouslyResolvedBindings(bindingKey);
522         if (bindingsPreviouslyResolvedInParent.isPresent()
523             && !dependsOnLocalMultibindings(bindingsPreviouslyResolvedInParent.get())) {
524           return;
525         }
526 
527         cycleStack.push(bindingKey);
528         try {
529           ResolvedBindings bindings = lookUpBindings(request);
530           for (Binding binding : bindings.ownedBindings()) {
531             for (DependencyRequest dependency : binding.implicitDependencies()) {
532               resolve(dependency);
533             }
534           }
535           resolvedBindings.put(bindingKey, bindings);
536         } finally {
537           cycleStack.pop();
538         }
539       }
540 
541       /**
542        * Returns {@code true} if {@code previouslyResolvedBindings} is multibindings with
543        * contributions declared within this (sub)component's modules, or if any of its unscoped
544        * provision-dependencies depend on such local multibindings.
545        *
546        * <p>We don't care about scoped dependencies or production bindings because they will never
547        * depend on multibindings with contributions from subcomponents.
548        */
549       private boolean dependsOnLocalMultibindings(ResolvedBindings previouslyResolvedBindings) {
550         return dependsOnLocalMultibindings(previouslyResolvedBindings, new HashSet<BindingKey>());
551       }
552 
553       private boolean dependsOnLocalMultibindings(
554           final ResolvedBindings previouslyResolvedBindings, final Set<BindingKey> cycleChecker) {
555         // Don't recur infinitely if there are valid cycles in the dependency graph.
556         if (!cycleChecker.add(previouslyResolvedBindings.bindingKey())) {
557           return false;
558         }
559         try {
560           return dependsOnLocalMultibindingsCache.get(
561               previouslyResolvedBindings.bindingKey(),
562               new Callable<Boolean>() {
563                 @Override
564                 public Boolean call() {
565                   if (previouslyResolvedBindings.isMultibindings()
566                       && hasLocalContributions(previouslyResolvedBindings)) {
567                     return true;
568                   }
569 
570                   for (Binding binding : previouslyResolvedBindings.bindings()) {
571                     if (!binding.scope().isPresent()
572                         && !binding.bindingType().equals(Type.PRODUCTION)) {
573                       for (DependencyRequest dependency : binding.implicitDependencies()) {
574                         if (dependsOnLocalMultibindings(
575                             getPreviouslyResolvedBindings(dependency.bindingKey()).get(),
576                             cycleChecker)) {
577                           return true;
578                         }
579                       }
580                     }
581                   }
582                   return false;
583                 }
584               });
585         } catch (ExecutionException e) {
586           throw new AssertionError(e);
587         }
588       }
589 
590       private boolean hasLocalContributions(ResolvedBindings resolvedBindings) {
591         return !explicitBindings.get(resolvedBindings.bindingKey().key()).isEmpty();
592       }
593 
594       ImmutableMap<BindingKey, ResolvedBindings> getResolvedBindings() {
595         ImmutableMap.Builder<BindingKey, ResolvedBindings> resolvedBindingsBuilder =
596             ImmutableMap.builder();
597         resolvedBindingsBuilder.putAll(resolvedBindings);
598         if (parentResolver.isPresent()) {
599           Collection<ResolvedBindings> bindingsResolvedInParent =
600               Maps.difference(parentResolver.get().getResolvedBindings(), resolvedBindings)
601                   .entriesOnlyOnLeft()
602                   .values();
603           for (ResolvedBindings resolvedInParent : bindingsResolvedInParent) {
604             resolvedBindingsBuilder.put(
605                 resolvedInParent.bindingKey(),
606                 resolvedInParent.asInheritedIn(componentDescriptor));
607           }
608         }
609         return resolvedBindingsBuilder.build();
610       }
611 
612       ImmutableSet<ModuleDescriptor> getInheritedModules() {
613         return parentResolver.isPresent()
614             ? Sets.union(
615                     parentResolver.get().getInheritedModules(),
616                     parentResolver.get().componentDescriptor.transitiveModules())
617                 .immutableCopy()
618             : ImmutableSet.<ModuleDescriptor>of();
619       }
620 
621       ImmutableSet<ModuleDescriptor> getOwnedModules() {
622         return Sets.difference(componentDescriptor.transitiveModules(), getInheritedModules())
623             .immutableCopy();
624       }
625     }
626   }
627 }
628