/*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.internal.codegen;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeTraverser;
import dagger.Component;
import dagger.Subcomponent;
import dagger.internal.codegen.Binding.Type;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.producers.Producer;
import dagger.producers.ProductionComponent;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Sets.union;
import static dagger.internal.codegen.BindingKey.Kind.CONTRIBUTION;
import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT_BUILDER;
import static dagger.internal.codegen.ComponentDescriptor.Kind.PRODUCTION_COMPONENT;
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
import static dagger.internal.codegen.MembersInjectionBinding.Strategy.INJECT_MEMBERS;
import static dagger.internal.codegen.MembersInjectionBinding.Strategy.NO_OP;
import static javax.lang.model.element.Modifier.STATIC;
/**
* The canonical representation of a full-resolved graph.
*
* @author Gregory Kick
*/
@AutoValue
abstract class BindingGraph {
abstract ComponentDescriptor componentDescriptor();
abstract ImmutableMap<BindingKey, ResolvedBindings> resolvedBindings();
abstract ImmutableMap<ExecutableElement, BindingGraph> subgraphs();
/**
* Returns the set of modules that are owned by this graph regardless of whether or not any of
* their bindings are used in this graph. For graphs representing top-level {@link Component
* components}, this set will be the same as
* {@linkplain ComponentDescriptor#transitiveModules the component's transitive modules}. For
* {@linkplain Subcomponent subcomponents}, this set will be the transitive modules that are not
* owned by any of their ancestors.
*/
abstract ImmutableSet<ModuleDescriptor> ownedModules();
ImmutableSet<TypeElement> ownedModuleTypes() {
return FluentIterable.from(ownedModules())
.transform(ModuleDescriptor.getModuleElement())
.toSet();
}
private static final TreeTraverser<BindingGraph> SUBGRAPH_TRAVERSER =
new TreeTraverser<BindingGraph>() {
@Override
public Iterable<BindingGraph> children(BindingGraph node) {
return node.subgraphs().values();
}
};
/**
* Returns the set of types necessary to implement the component, but are not part of the injected
* graph. This includes modules, component dependencies and an {@link Executor} in the case of
* {@link ProductionComponent}.
*/
ImmutableSet<TypeElement> componentRequirements() {
return SUBGRAPH_TRAVERSER
.preOrderTraversal(this)
.transformAndConcat(
new Function<BindingGraph, Iterable<ResolvedBindings>>() {
@Override
public Iterable<ResolvedBindings> apply(BindingGraph input) {
return input.resolvedBindings().values();
}
})
.transformAndConcat(
new Function<ResolvedBindings, Set<ContributionBinding>>() {
@Override
public Set<ContributionBinding> apply(ResolvedBindings input) {
return (input.bindingKey().kind().equals(CONTRIBUTION))
? input.contributionBindings()
: ImmutableSet.<ContributionBinding>of();
}
})
.transformAndConcat(
new Function<ContributionBinding, Set<TypeElement>>() {
@Override
public Set<TypeElement> apply(ContributionBinding input) {
return input.bindingElement().getModifiers().contains(STATIC)
? ImmutableSet.<TypeElement>of()
: input.contributedBy().asSet();
}
})
.filter(in(ownedModuleTypes()))
.append(componentDescriptor().dependencies())
.append(componentDescriptor().executorDependency().asSet())
.toSet();
}
ImmutableSet<TypeElement> availableDependencies() {
return new ImmutableSet.Builder<TypeElement>()
.addAll(componentDescriptor().transitiveModuleTypes())
.addAll(componentDescriptor().dependencies())
.addAll(componentDescriptor().executorDependency().asSet())
.build();
}
static final class Factory {
private final Elements elements;
private final InjectBindingRegistry injectBindingRegistry;
private final Key.Factory keyFactory;
private final ProvisionBinding.Factory provisionBindingFactory;
private final ProductionBinding.Factory productionBindingFactory;
Factory(Elements elements,
InjectBindingRegistry injectBindingRegistry,
Key.Factory keyFactory,
ProvisionBinding.Factory provisionBindingFactory,
ProductionBinding.Factory productionBindingFactory) {
this.elements = elements;
this.injectBindingRegistry = injectBindingRegistry;
this.keyFactory = keyFactory;
this.provisionBindingFactory = provisionBindingFactory;
this.productionBindingFactory = productionBindingFactory;
}
BindingGraph create(ComponentDescriptor componentDescriptor) {
return create(Optional.<Resolver>absent(), componentDescriptor);
}
private BindingGraph create(
Optional<Resolver> parentResolver, ComponentDescriptor componentDescriptor) {
ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
// binding for the component itself
TypeElement componentDefinitionType = componentDescriptor.componentDefinitionType();
explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDefinitionType));
// Collect Component dependencies.
Optional<AnnotationMirror> componentMirror =
getAnnotationMirror(componentDefinitionType, Component.class)
.or(getAnnotationMirror(componentDefinitionType, ProductionComponent.class));
ImmutableSet<TypeElement> componentDependencyTypes = componentMirror.isPresent()
? MoreTypes.asTypeElements(getComponentDependencies(componentMirror.get()))
: ImmutableSet.<TypeElement>of();
for (TypeElement componentDependency : componentDependencyTypes) {
explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDependency));
List<ExecutableElement> dependencyMethods =
ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
for (ExecutableElement method : dependencyMethods) {
// MembersInjection methods aren't "provided" explicitly, so ignore them.
if (isComponentContributionMethod(elements, method)) {
explicitBindingsBuilder.add(
componentDescriptor.kind().equals(PRODUCTION_COMPONENT)
&& isComponentProductionMethod(elements, method)
? productionBindingFactory.forComponentMethod(method)
: provisionBindingFactory.forComponentMethod(method));
}
}
}
// Bindings for subcomponent builders.
for (ComponentMethodDescriptor subcomponentMethodDescriptor :
Iterables.filter(
componentDescriptor.subcomponents().keySet(), isOfKind(SUBCOMPONENT_BUILDER))) {
explicitBindingsBuilder.add(
provisionBindingFactory.forSubcomponentBuilderMethod(
subcomponentMethodDescriptor.methodElement(),
componentDescriptor.componentDefinitionType()));
}
// Collect transitive module bindings.
for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) {
for (ContributionBinding binding : moduleDescriptor.bindings()) {
explicitBindingsBuilder.add(binding);
}
}
Resolver requestResolver =
new Resolver(
parentResolver,
componentDescriptor,
explicitBindingsByKey(explicitBindingsBuilder.build()));
for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
if (componentMethodRequest.isPresent()) {
requestResolver.resolve(componentMethodRequest.get());
}
}
ImmutableMap.Builder<ExecutableElement, BindingGraph> subgraphsBuilder =
ImmutableMap.builder();
for (Entry<ComponentMethodDescriptor, ComponentDescriptor> subcomponentEntry :
componentDescriptor.subcomponents().entrySet()) {
subgraphsBuilder.put(
subcomponentEntry.getKey().methodElement(),
create(Optional.of(requestResolver), subcomponentEntry.getValue()));
}
for (ResolvedBindings resolvedBindings : requestResolver.getResolvedBindings().values()) {
verify(
resolvedBindings.owningComponent().equals(componentDescriptor),
"%s is not owned by %s",
resolvedBindings,
componentDescriptor);
}
return new AutoValue_BindingGraph(
componentDescriptor,
requestResolver.getResolvedBindings(),
subgraphsBuilder.build(),
requestResolver.getOwnedModules());
}
private <B extends ContributionBinding> ImmutableSetMultimap<Key, B> explicitBindingsByKey(
Iterable<? extends B> bindings) {
// Multimaps.index() doesn't do ImmutableSetMultimaps.
ImmutableSetMultimap.Builder<Key, B> builder = ImmutableSetMultimap.builder();
for (B binding : bindings) {
builder.put(binding.key(), binding);
}
return builder.build();
}
private final class Resolver {
final Optional<Resolver> parentResolver;
final ComponentDescriptor componentDescriptor;
final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
final ImmutableSet<ContributionBinding> explicitBindingsSet;
final Map<BindingKey, ResolvedBindings> resolvedBindings;
final Deque<BindingKey> cycleStack = new ArrayDeque<>();
final Cache<BindingKey, Boolean> dependsOnLocalMultibindingsCache =
CacheBuilder.newBuilder().<BindingKey, Boolean>build();
Resolver(
Optional<Resolver> parentResolver,
ComponentDescriptor componentDescriptor,
ImmutableSetMultimap<Key, ContributionBinding> explicitBindings) {
assert parentResolver != null;
this.parentResolver = parentResolver;
assert componentDescriptor != null;
this.componentDescriptor = componentDescriptor;
assert explicitBindings != null;
this.explicitBindings = explicitBindings;
this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
this.resolvedBindings = Maps.newLinkedHashMap();
}
/**
* Looks up the bindings associated with a given dependency request and returns them.
*
* <p>Requests for {@code Map<K, V>} for which there are only bindings for
* {@code Map<K, Provider<V>>} will resolve to a single implicit binding for the latter map
* (and similarly for {@link Producer}s).
*
* <p>If there are no explicit bindings for a contribution, looks for implicit
* {@link Inject @Inject}-annotated constructor types.
*/
ResolvedBindings lookUpBindings(DependencyRequest request) {
BindingKey bindingKey = request.bindingKey();
switch (bindingKey.kind()) {
case CONTRIBUTION:
// First, check for explicit keys (those from modules and components)
ImmutableSet<ContributionBinding> explicitBindingsForKey =
getExplicitBindings(bindingKey.key());
// If the key is Map<K, V>, get its implicit binding keys, which are either
// Map<K, Provider<V>> or Map<K, Producer<V>>, and grab their explicit bindings.
Optional<Key> mapProviderKey = keyFactory.implicitMapProviderKeyFrom(bindingKey.key());
ImmutableSet.Builder<ContributionBinding> explicitMapBindingsBuilder =
ImmutableSet.builder();
if (mapProviderKey.isPresent()) {
explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProviderKey.get()));
}
Optional<Key> mapProducerKey = keyFactory.implicitMapProducerKeyFrom(bindingKey.key());
if (mapProducerKey.isPresent()) {
explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProducerKey.get()));
}
ImmutableSet<ContributionBinding> explicitMapBindings =
explicitMapBindingsBuilder.build();
// If the key is Set<Produced<T>>, then we look up bindings by the alternate key Set<T>.
Optional<Key> setKeyFromProduced =
keyFactory.implicitSetKeyFromProduced(bindingKey.key());
ImmutableSet<ContributionBinding> explicitSetBindings =
setKeyFromProduced.isPresent()
? getExplicitBindings(setKeyFromProduced.get())
: ImmutableSet.<ContributionBinding>of();
if (!explicitBindingsForKey.isEmpty() || !explicitSetBindings.isEmpty()) {
/* If there are any explicit bindings for this key, then combine those with any
* conflicting Map<K, Provider<V>> bindings and let the validator fail. */
ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> bindings =
ImmutableSetMultimap.builder();
for (ContributionBinding binding :
union(explicitBindingsForKey, union(explicitSetBindings, explicitMapBindings))) {
bindings.put(getOwningComponent(request, binding), binding);
}
return ResolvedBindings.forContributionBindings(
bindingKey, componentDescriptor, bindings.build());
} else if (any(explicitMapBindings, Binding.Type.PRODUCTION)) {
/* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
* some explicit Map<K, Producer<V>> bindings, then this binding must have only the
* implicit dependency on Map<K, Producer<V>>. */
return ResolvedBindings.forContributionBindings(
bindingKey,
componentDescriptor,
productionBindingFactory.implicitMapOfProducerBinding(request));
} else if (any(explicitMapBindings, Binding.Type.PROVISION)) {
/* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
* some explicit Map<K, Provider<V>> bindings, then this binding must have only the
* implicit dependency on Map<K, Provider<V>>. */
return ResolvedBindings.forContributionBindings(
bindingKey,
componentDescriptor,
provisionBindingFactory.implicitMapOfProviderBinding(request));
} else {
/* If there are no explicit bindings at all, look for an implicit @Inject-constructed
* binding. */
Optional<ProvisionBinding> provisionBinding =
injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key());
ComponentDescriptor owningComponent =
provisionBinding.isPresent()
&& isResolvedInParent(request, provisionBinding.get())
&& !shouldOwnParentBinding(request, provisionBinding.get())
? getOwningResolver(provisionBinding.get()).get().componentDescriptor
: componentDescriptor;
return ResolvedBindings.forContributionBindings(
bindingKey,
componentDescriptor,
ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
.putAll(owningComponent, provisionBinding.asSet())
.build());
}
case MEMBERS_INJECTION:
// no explicit deps for members injection, so just look it up
return ResolvedBindings.forMembersInjectionBinding(
bindingKey, componentDescriptor, rollUpMembersInjectionBindings(bindingKey.key()));
default:
throw new AssertionError();
}
}
/**
* If {@code binding} should be owned by a parent component, resolves the binding in that
* component's resolver and returns that component. Otherwise returns the component for this
* resolver.
*/
private ComponentDescriptor getOwningComponent(
DependencyRequest request, ContributionBinding binding) {
return isResolvedInParent(request, binding) && !shouldOwnParentBinding(request, binding)
? getOwningResolver(binding).get().componentDescriptor
: componentDescriptor;
}
/**
* Returns {@code true} if {@code binding} is owned by a parent resolver. If so, calls
* {@link #resolve(DependencyRequest) resolve(request)} on that resolver.
*/
private boolean isResolvedInParent(DependencyRequest request, ContributionBinding binding) {
Optional<Resolver> owningResolver = getOwningResolver(binding);
if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
owningResolver.get().resolve(request);
return true;
} else {
return false;
}
}
/**
* Returns {@code true} if {@code binding}, which was previously resolved by a parent
* resolver, should be moved into this resolver's bindings for {@code request} because it is
* unscoped and {@linkplain #dependsOnLocalMultibindings(ResolvedBindings) depends on local
* multibindings}, or {@code false} if it can satisfy {@code request} as an inherited binding.
*/
private boolean shouldOwnParentBinding(
DependencyRequest request, ContributionBinding binding) {
return !binding.scope().isPresent()
&& dependsOnLocalMultibindings(
getPreviouslyResolvedBindings(request.bindingKey()).get());
}
private MembersInjectionBinding rollUpMembersInjectionBindings(Key key) {
MembersInjectionBinding membersInjectionBinding =
injectBindingRegistry.getOrFindMembersInjectionBinding(key);
if (membersInjectionBinding.parentInjectorRequest().isPresent()
&& membersInjectionBinding.injectionStrategy().equals(INJECT_MEMBERS)) {
MembersInjectionBinding parentBinding =
rollUpMembersInjectionBindings(
membersInjectionBinding.parentInjectorRequest().get().key());
if (parentBinding.injectionStrategy().equals(NO_OP)) {
return membersInjectionBinding.withoutParentInjectorRequest();
}
}
return membersInjectionBinding;
}
private Optional<Resolver> getOwningResolver(ContributionBinding provisionBinding) {
for (Resolver requestResolver : getResolverLineage().reverse()) {
if (requestResolver.explicitBindingsSet.contains(provisionBinding)) {
return Optional.of(requestResolver);
}
}
// look for scope separately. we do this for the case where @Singleton can appear twice
// in the † compatibility mode
Scope bindingScope = provisionBinding.scope();
if (bindingScope.isPresent()) {
for (Resolver requestResolver : getResolverLineage().reverse()) {
if (bindingScope.equals(requestResolver.componentDescriptor.scope())) {
return Optional.of(requestResolver);
}
}
}
return Optional.absent();
}
/** Returns the resolver lineage from parent to child. */
private ImmutableList<Resolver> getResolverLineage() {
List<Resolver> resolverList = Lists.newArrayList();
for (Optional<Resolver> currentResolver = Optional.of(this);
currentResolver.isPresent();
currentResolver = currentResolver.get().parentResolver) {
resolverList.add(currentResolver.get());
}
return ImmutableList.copyOf(Lists.reverse(resolverList));
}
private ImmutableSet<ContributionBinding> getExplicitBindings(Key requestKey) {
ImmutableSet.Builder<ContributionBinding> explicitBindingsForKey = ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
explicitBindingsForKey.addAll(resolver.explicitBindings.get(requestKey));
}
return explicitBindingsForKey.build();
}
private Optional<ResolvedBindings> getPreviouslyResolvedBindings(
final BindingKey bindingKey) {
Optional<ResolvedBindings> result = Optional.fromNullable(resolvedBindings.get(bindingKey));
if (result.isPresent()) {
return result;
} else if (parentResolver.isPresent()) {
return parentResolver.get().getPreviouslyResolvedBindings(bindingKey);
} else {
return Optional.absent();
}
}
void resolve(DependencyRequest request) {
BindingKey bindingKey = request.bindingKey();
// If we find a cycle, stop resolving. The original request will add it with all of the
// other resolved deps.
if (cycleStack.contains(bindingKey)) {
return;
}
// If the binding was previously resolved in this (sub)component, don't resolve it again.
if (resolvedBindings.containsKey(bindingKey)) {
return;
}
// If the binding was previously resolved in a supercomponent, then test to see if it
// depends on multibindings with contributions from this subcomponent. If it does, then we
// have to resolve it in this subcomponent so that it sees the local contributions. If it
// does not, then we can stop resolving it in this subcomponent and rely on the
// supercomponent resolution.
Optional<ResolvedBindings> bindingsPreviouslyResolvedInParent =
getPreviouslyResolvedBindings(bindingKey);
if (bindingsPreviouslyResolvedInParent.isPresent()
&& !dependsOnLocalMultibindings(bindingsPreviouslyResolvedInParent.get())) {
return;
}
cycleStack.push(bindingKey);
try {
ResolvedBindings bindings = lookUpBindings(request);
for (Binding binding : bindings.ownedBindings()) {
for (DependencyRequest dependency : binding.implicitDependencies()) {
resolve(dependency);
}
}
resolvedBindings.put(bindingKey, bindings);
} finally {
cycleStack.pop();
}
}
/**
* Returns {@code true} if {@code previouslyResolvedBindings} is multibindings with
* contributions declared within this (sub)component's modules, or if any of its unscoped
* provision-dependencies depend on such local multibindings.
*
* <p>We don't care about scoped dependencies or production bindings because they will never
* depend on multibindings with contributions from subcomponents.
*/
private boolean dependsOnLocalMultibindings(ResolvedBindings previouslyResolvedBindings) {
return dependsOnLocalMultibindings(previouslyResolvedBindings, new HashSet<BindingKey>());
}
private boolean dependsOnLocalMultibindings(
final ResolvedBindings previouslyResolvedBindings, final Set<BindingKey> cycleChecker) {
// Don't recur infinitely if there are valid cycles in the dependency graph.
if (!cycleChecker.add(previouslyResolvedBindings.bindingKey())) {
return false;
}
try {
return dependsOnLocalMultibindingsCache.get(
previouslyResolvedBindings.bindingKey(),
new Callable<Boolean>() {
@Override
public Boolean call() {
if (previouslyResolvedBindings.isMultibindings()
&& hasLocalContributions(previouslyResolvedBindings)) {
return true;
}
for (Binding binding : previouslyResolvedBindings.bindings()) {
if (!binding.scope().isPresent()
&& !binding.bindingType().equals(Type.PRODUCTION)) {
for (DependencyRequest dependency : binding.implicitDependencies()) {
if (dependsOnLocalMultibindings(
getPreviouslyResolvedBindings(dependency.bindingKey()).get(),
cycleChecker)) {
return true;
}
}
}
}
return false;
}
});
} catch (ExecutionException e) {
throw new AssertionError(e);
}
}
private boolean hasLocalContributions(ResolvedBindings resolvedBindings) {
return !explicitBindings.get(resolvedBindings.bindingKey().key()).isEmpty();
}
ImmutableMap<BindingKey, ResolvedBindings> getResolvedBindings() {
ImmutableMap.Builder<BindingKey, ResolvedBindings> resolvedBindingsBuilder =
ImmutableMap.builder();
resolvedBindingsBuilder.putAll(resolvedBindings);
if (parentResolver.isPresent()) {
Collection<ResolvedBindings> bindingsResolvedInParent =
Maps.difference(parentResolver.get().getResolvedBindings(), resolvedBindings)
.entriesOnlyOnLeft()
.values();
for (ResolvedBindings resolvedInParent : bindingsResolvedInParent) {
resolvedBindingsBuilder.put(
resolvedInParent.bindingKey(),
resolvedInParent.asInheritedIn(componentDescriptor));
}
}
return resolvedBindingsBuilder.build();
}
ImmutableSet<ModuleDescriptor> getInheritedModules() {
return parentResolver.isPresent()
? Sets.union(
parentResolver.get().getInheritedModules(),
parentResolver.get().componentDescriptor.transitiveModules())
.immutableCopy()
: ImmutableSet.<ModuleDescriptor>of();
}
ImmutableSet<ModuleDescriptor> getOwnedModules() {
return Sets.difference(componentDescriptor.transitiveModules(), getInheritedModules())
.immutableCopy();
}
}
}
}