blob: f390d65c0d0fb39340a7f020db74a0955bc96caa [file] [log] [blame]
/*
* Copyright (C) 2014 The Dagger Authors.
*
* 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 static com.google.auto.common.MoreTypes.isType;
import static com.google.auto.common.MoreTypes.isTypeOf;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.isEmpty;
import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
import static dagger.internal.codegen.ComponentRequirement.Kind.BOUND_INSTANCE;
import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.RequestKinds.getRequestKind;
import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
import static dagger.model.BindingKind.OPTIONAL;
import static dagger.model.BindingKind.SUBCOMPONENT_BUILDER;
import static java.util.function.Predicate.isEqual;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.util.ElementFilter.methodsIn;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
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.Multimaps;
import com.google.common.collect.Sets;
import com.google.common.graph.Traverser;
import dagger.MembersInjector;
import dagger.Reusable;
import dagger.Subcomponent;
import dagger.internal.codegen.ComponentDescriptor.BuilderRequirementMethod;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind;
import dagger.model.DependencyRequest;
import dagger.model.Key;
import dagger.model.RequestKind;
import dagger.model.Scope;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.releasablereferences.CanReleaseReferences;
import dagger.releasablereferences.ReleasableReferenceManager;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
/**
* The canonical representation of a full-resolved graph.
*
* @author Gregory Kick
*/
@AutoValue
abstract class BindingGraph {
abstract ComponentDescriptor componentDescriptor();
/**
* The resolved bindings for all {@link ContributionBinding}s in this graph, keyed by {@link Key}.
*/
// TODO(ronshapiro): when MembersInjectionBinding no longer extends Binding, rename this to
// bindings()
abstract ImmutableMap<Key, ResolvedBindings> contributionBindings();
/**
* The resolved bindings for all {@link MembersInjectionBinding}s in this graph, keyed by {@link
* Key}.
*/
abstract ImmutableMap<Key, ResolvedBindings> membersInjectionBindings();
/**
* Returns the {@link ResolvedBindings resolved bindings} instance for {@code key}. If {@code
* requestKind} is {@link RequestKind#MEMBERS_INJECTION}, a {@link ResolvedBindings} with
* {@linkplain #membersInjectionBindings() members injection bindings} will be returned, otherwise
* a {@link ResolvedBindings} with {@link #contributionBindings()} will be returned.
*/
final ResolvedBindings resolvedBindings(RequestKind requestKind, Key key) {
return requestKind.equals(RequestKind.MEMBERS_INJECTION)
? membersInjectionBindings().get(key)
: contributionBindings().get(key);
}
@Memoized
ImmutableSet<ResolvedBindings> resolvedBindings() {
return ImmutableSet.<ResolvedBindings>builder()
.addAll(membersInjectionBindings().values())
.addAll(contributionBindings().values())
.build();
}
abstract ImmutableSet<BindingGraph> subgraphs();
/**
* The scopes in the graph that {@linkplain CanReleaseReferences can release their references} for
* which there is a dependency request for any of the following:
*
* <ul>
* <li>{@code @ForReleasableReferences(scope)} {@link ReleasableReferenceManager}
* <li>{@code @ForReleasableReferences(scope)} {@code TypedReleasableReferenceManager<M>}, where
* {@code M} is the releasable-references metatadata type for {@code scope}
* <li>{@code Set<ReleasableReferenceManager>}
* <li>{@code Set<TypedReleasableReferenceManager<M>>}, where {@code M} is the metadata type for
* the scope
* </ul>
*
* <p>This set is always empty for subcomponent graphs.
*/
abstract ImmutableSet<Scope> scopesRequiringReleasableReferenceManagers();
/** Returns the resolved bindings for the dependencies of {@code binding}. */
ImmutableSet<ResolvedBindings> resolvedDependencies(ContributionBinding binding) {
return binding
.dependencies()
.stream()
.map(DependencyRequest::key)
.map(
key ->
contributionBindings()
.getOrDefault(key, ResolvedBindings.noBindings(key, componentDescriptor())))
.collect(toImmutableSet());
}
/**
* The type that defines the component for this graph.
*
* @see ComponentDescriptor#componentDefinitionType()
*/
TypeElement componentType() {
return componentDescriptor().componentDefinitionType();
}
/**
* 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
* dagger.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::moduleElement).toSet();
}
private static final Traverser<BindingGraph> SUBGRAPH_TRAVERSER =
Traverser.forTree(BindingGraph::subgraphs);
/**
* The types for which the component needs instances.
*
* <ul>
* <li>component dependencies
* <li>{@linkplain #ownedModules() owned modules} with concrete instance bindings that are used
* in the graph
* <li>bound instances
* </ul>
*/
@Memoized
ImmutableSet<ComponentRequirement> componentRequirements() {
ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
StreamSupport.stream(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this).spliterator(), false)
.flatMap(graph -> graph.contributionBindings().values().stream())
.flatMap(bindings -> bindings.contributionBindings().stream())
.filter(ContributionBinding::requiresModuleInstance)
.map(bindingDeclaration -> bindingDeclaration.contributingModule())
.filter(Optional::isPresent)
.map(Optional::get)
.filter(module -> ownedModuleTypes().contains(module))
.map(module -> ComponentRequirement.forModule(module.asType()))
.forEach(requirements::add);
componentDescriptor()
.dependencies()
.stream()
.forEach(requirements::add);
if (componentDescriptor().builderSpec().isPresent()) {
componentDescriptor()
.builderSpec()
.get()
.requirementMethods()
.stream()
.map(BuilderRequirementMethod::requirement)
.filter(req -> req.kind().equals(BOUND_INSTANCE))
.forEach(requirements::add);
}
return requirements.build();
}
/** Returns the {@link ComponentDescriptor}s for this component and its subcomponents. */
ImmutableSet<ComponentDescriptor> componentDescriptors() {
return FluentIterable.from(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this))
.transform(BindingGraph::componentDescriptor)
.toSet();
}
ImmutableSet<ComponentRequirement> availableDependencies() {
return Stream.concat(
componentDescriptor()
.transitiveModuleTypes()
.stream()
.filter(dep -> !dep.getModifiers().contains(ABSTRACT))
.map(module -> ComponentRequirement.forModule(module.asType())),
componentDescriptor().dependencies().stream())
.collect(toImmutableSet());
}
@Memoized
@Override
public abstract int hashCode();
@Override // Suppresses ErrorProne warning that hashCode was overridden w/o equals
public abstract boolean equals(Object other);
static final class Factory {
private final Elements elements;
private final InjectBindingRegistry injectBindingRegistry;
private final KeyFactory keyFactory;
private final BindingFactory bindingFactory;
@Inject
Factory(
Elements elements,
InjectBindingRegistry injectBindingRegistry,
KeyFactory keyFactory,
BindingFactory bindingFactory) {
this.elements = elements;
this.injectBindingRegistry = injectBindingRegistry;
this.keyFactory = keyFactory;
this.bindingFactory = bindingFactory;
}
BindingGraph create(ComponentDescriptor componentDescriptor) {
return create(Optional.empty(), componentDescriptor);
}
private BindingGraph create(
Optional<Resolver> parentResolver, ComponentDescriptor componentDescriptor) {
ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
ImmutableSet.Builder<DelegateDeclaration> delegatesBuilder = ImmutableSet.builder();
ImmutableSet.Builder<OptionalBindingDeclaration> optionalsBuilder = ImmutableSet.builder();
// binding for the component itself
explicitBindingsBuilder.add(
bindingFactory.componentBinding(componentDescriptor.componentDefinitionType()));
// Collect Component dependencies.
for (ComponentRequirement dependency : componentDescriptor.dependencies()) {
explicitBindingsBuilder.add(bindingFactory.componentDependencyBinding(dependency));
List<ExecutableElement> dependencyMethods =
methodsIn(elements.getAllMembers(dependency.typeElement()));
for (ExecutableElement method : dependencyMethods) {
// MembersInjection methods aren't "provided" explicitly, so ignore them.
if (isComponentContributionMethod(elements, method)) {
explicitBindingsBuilder.add(
bindingFactory.componentDependencyMethodBinding(componentDescriptor, method));
}
}
}
// Collect bindings on the builder.
if (componentDescriptor.builderSpec().isPresent()) {
for (BuilderRequirementMethod method :
componentDescriptor.builderSpec().get().requirementMethods()) {
if (method.requirement().kind().equals(BOUND_INSTANCE)) {
explicitBindingsBuilder.add(bindingFactory.boundInstanceBinding(method));
}
}
}
for (Map.Entry<ComponentMethodDescriptor, ComponentDescriptor>
componentMethodAndSubcomponent :
componentDescriptor.subcomponentsByBuilderMethod().entrySet()) {
ComponentMethodDescriptor componentMethod = componentMethodAndSubcomponent.getKey();
ComponentDescriptor subcomponentDescriptor = componentMethodAndSubcomponent.getValue();
if (!componentDescriptor.subcomponentsFromModules().contains(subcomponentDescriptor)) {
explicitBindingsBuilder.add(
bindingFactory.subcomponentBuilderBinding(
componentMethod.methodElement(), componentDescriptor.componentDefinitionType()));
}
}
ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
ImmutableSet.builder();
ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations =
ImmutableSet.builder();
// Collect transitive module bindings and multibinding declarations.
for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) {
explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
delegatesBuilder.addAll(moduleDescriptor.delegateDeclarations());
optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
}
ImmutableSetMultimap<Scope, ProvisionBinding> releasableReferenceManagerBindings =
getReleasableReferenceManagerBindings(componentDescriptor);
explicitBindingsBuilder.addAll(releasableReferenceManagerBindings.values());
final Resolver requestResolver =
new Resolver(
parentResolver,
componentDescriptor,
indexByKey(explicitBindingsBuilder.build()),
indexByKey(multibindingDeclarations.build()),
indexByKey(subcomponentDeclarations.build()),
indexByKey(delegatesBuilder.build()),
indexByKey(optionalsBuilder.build()));
for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
if (componentMethod.kind().equals(ComponentMethodKind.MEMBERS_INJECTION)) {
requestResolver.resolveMembersInjectionMethod(componentMethod);
} else {
Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
if (componentMethodRequest.isPresent()) {
requestResolver.resolve(componentMethodRequest.get().key());
}
}
}
// Resolve all bindings for subcomponents, creating subgraphs for all subcomponents that have
// been detected during binding resolution. If a binding for a subcomponent is never resolved,
// no BindingGraph will be created for it and no implementation will be generated. This is
// done in a queue since resolving one subcomponent might resolve a key for a subcomponent
// from a parent graph. This is done until no more new subcomponents are resolved.
Set<ComponentDescriptor> resolvedSubcomponents = new HashSet<>();
ImmutableSet.Builder<BindingGraph> subgraphs = ImmutableSet.builder();
for (ComponentDescriptor subcomponent :
Iterables.consumingIterable(requestResolver.subcomponentsToResolve)) {
if (resolvedSubcomponents.add(subcomponent)) {
subgraphs.add(create(Optional.of(requestResolver), subcomponent));
}
}
ImmutableMap<Key, ResolvedBindings> resolvedContributionBindingsMap =
requestResolver.getResolvedContributionBindings();
for (ResolvedBindings resolvedBindings : resolvedContributionBindingsMap.values()) {
verify(
resolvedBindings.owningComponent().equals(componentDescriptor),
"%s is not owned by %s",
resolvedBindings,
componentDescriptor);
}
return new AutoValue_BindingGraph(
componentDescriptor,
resolvedContributionBindingsMap,
requestResolver.getResolvedMembersInjectionBindings(),
subgraphs.build(),
getScopesRequiringReleasableReferenceManagers(
releasableReferenceManagerBindings, resolvedContributionBindingsMap.keySet()),
requestResolver.getOwnedModules());
}
/**
* Returns the bindings for {@link ReleasableReferenceManager}s for all {@link
* CanReleaseReferences @CanReleaseReferences} scopes.
*/
private ImmutableSetMultimap<Scope, ProvisionBinding> getReleasableReferenceManagerBindings(
ComponentDescriptor componentDescriptor) {
ImmutableSetMultimap.Builder<Scope, ProvisionBinding> bindings =
ImmutableSetMultimap.builder();
// TODO(dpb,gak): Do we need to bind an empty Set<ReleasableReferenceManager> if there are
// none?
for (Scope scope : componentDescriptor.releasableReferencesScopes()) {
// Add a binding for @ForReleasableReferences(scope) ReleasableReferenceManager.
bindings.put(scope, bindingFactory.releasableReferenceManagerBinding(scope));
/* Add a binding for Set<ReleasableReferenceManager>. Even if these are added more than
* once, each instance will be equal to the rest. Since they're being added to a set, there
* will be only one instance. */
bindings.put(scope, bindingFactory.setOfReleasableReferenceManagersBinding());
for (AnnotationMirror metadata : scope.releasableReferencesMetadata()) {
// Add a binding for @ForReleasableReferences(scope) TypedReleasableReferenceManager<M>.
bindings.put(
scope,
bindingFactory.typedReleasableReferenceManagerBinding(
scope, metadata.getAnnotationType()));
/* Add a binding for Set<TypedReleasableReferenceManager<M>>. Even if these are added more
* than once, each instance will be equal to the rest. Since they're being added to a set,
* there will be only one instance. */
bindings.put(
scope,
bindingFactory.setOfTypedReleasableReferenceManagersBinding(
metadata.getAnnotationType()));
}
}
return bindings.build();
}
/**
* Returns the set of scopes that will be returned by {@link
* BindingGraph#scopesRequiringReleasableReferenceManagers()}.
*
* @param releasableReferenceManagerBindings the {@link ReleasableReferenceManager} bindings for
* each scope
* @param resolvedContributionKeys the keys of the resolved bindings for the component
*/
private ImmutableSet<Scope> getScopesRequiringReleasableReferenceManagers(
ImmutableSetMultimap<Scope, ProvisionBinding> releasableReferenceManagerBindings,
ImmutableSet<Key> resolvedContributionKeys) {
ImmutableSet.Builder<Scope> scopes = ImmutableSet.builder();
releasableReferenceManagerBindings
.asMap()
.forEach(
(scope, bindings) -> {
for (Binding binding : bindings) {
if (resolvedContributionKeys.contains(binding.key())) {
scopes.add(scope);
return;
}
}
});
return scopes.build();
}
private final class Resolver {
final Optional<Resolver> parentResolver;
final ComponentDescriptor componentDescriptor;
final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
final ImmutableSet<ContributionBinding> explicitBindingsSet;
final ImmutableSetMultimap<Key, ContributionBinding> explicitMultibindings;
final ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations;
final ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations;
final ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations;
final ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations;
final ImmutableSetMultimap<Key, DelegateDeclaration> delegateMultibindingDeclarations;
final Map<Key, ResolvedBindings> resolvedContributionBindings = new LinkedHashMap<>();
final Map<Key, ResolvedBindings> resolvedMembersInjectionBindings = new LinkedHashMap<>();
final Deque<Key> cycleStack = new ArrayDeque<>();
final Map<Key, Boolean> keyDependsOnLocalBindingsCache = new HashMap<>();
final Map<Binding, Boolean> bindingDependsOnLocalBindingsCache = new HashMap<>();
final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
Resolver(
Optional<Resolver> parentResolver,
ComponentDescriptor componentDescriptor,
ImmutableSetMultimap<Key, ContributionBinding> explicitBindings,
ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations,
ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations,
ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations,
ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations) {
this.parentResolver = checkNotNull(parentResolver);
this.componentDescriptor = checkNotNull(componentDescriptor);
this.explicitBindings = checkNotNull(explicitBindings);
this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
this.multibindingDeclarations = checkNotNull(multibindingDeclarations);
this.subcomponentDeclarations = checkNotNull(subcomponentDeclarations);
this.delegateDeclarations = checkNotNull(delegateDeclarations);
this.optionalBindingDeclarations = checkNotNull(optionalBindingDeclarations);
this.explicitMultibindings =
multibindingContributionsByMultibindingKey(explicitBindingsSet);
this.delegateMultibindingDeclarations =
multibindingContributionsByMultibindingKey(delegateDeclarations.values());
subcomponentsToResolve.addAll(componentDescriptor.subcomponentsFromEntryPoints());
}
/**
* Returns the resolved contribution bindings for the given {@link Key}:
*
* <ul>
* <li>All explicit bindings for:
* <ul>
* <li>the requested key
* <li>{@code Set<T>} if the requested key's type is {@code Set<Produced<T>>}
* <li>{@code Map<K, Provider<V>>} if the requested key's type is {@code Map<K,
* Producer<V>>}.
* </ul>
*
* <li>A synthetic binding that depends on {@code Map<K, Producer<V>>} if the requested key's
* type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
* Producer<V>>}.
* <li>A synthetic binding that depends on {@code Map<K, Provider<V>>} if the requested key's
* type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
* Provider<V>>} but no explicit bindings for {@code Map<K, Producer<V>>}.
* <li>An implicit {@link Inject @Inject}-annotated constructor binding if there is one and
* there are no explicit bindings or synthetic bindings.
* </ul>
*/
ResolvedBindings lookUpBindings(Key requestKey) {
Set<ContributionBinding> bindings = new LinkedHashSet<>();
bindings.addAll(getExplicitBindings(requestKey));
ImmutableSet<ContributionBinding> multibindingContributions =
getAllMatchingBindingDeclarations(requestKey, this::getExplicitMultibindings);
ImmutableSet<MultibindingDeclaration> multibindingDeclarations =
getAllMatchingBindingDeclarations(requestKey, this::getMultibindingDeclarations);
syntheticMultibinding(requestKey, multibindingContributions, multibindingDeclarations)
.ifPresent(bindings::add);
ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations =
getAllMatchingBindingDeclarations(requestKey, this::getOptionalBindingDeclarations);
syntheticOptionalBinding(requestKey, optionalBindingDeclarations).ifPresent(bindings::add);
ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations =
getSubcomponentDeclarations(requestKey);
syntheticSubcomponentBuilderBinding(subcomponentDeclarations)
.ifPresent(
binding -> {
bindings.add(binding);
addSubcomponentToOwningResolver(binding);
});
if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
injectBindingRegistry
.getOrFindMembersInjectorProvisionBinding(requestKey)
.ifPresent(bindings::add);
}
// If there are no bindings, add the implicit @Inject-constructed binding if there is one.
if (bindings.isEmpty()) {
injectBindingRegistry.getOrFindProvisionBinding(requestKey).ifPresent(bindings::add);
}
return ResolvedBindings.forContributionBindings(
requestKey,
componentDescriptor,
indexBindingsByOwningComponent(requestKey, ImmutableSet.copyOf(bindings)),
multibindingDeclarations,
subcomponentDeclarations,
optionalBindingDeclarations);
}
/** Returns the resolved members injection bindings for the given {@link Key}. */
ResolvedBindings lookUpMembersInjectionBinding(Key requestKey) {
// no explicit deps for members injection, so just look it up
Optional<MembersInjectionBinding> binding =
injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
return binding.isPresent()
? ResolvedBindings.forMembersInjectionBinding(
requestKey, componentDescriptor, binding.get())
: ResolvedBindings.noBindings(requestKey, componentDescriptor);
}
/**
* When a binding is resolved for a {@link SubcomponentDeclaration}, adds corresponding
* {@link ComponentDescriptor subcomponent} to a queue in the owning component's resolver.
* The queue will be used to detect which subcomponents need to be resolved.
*/
private void addSubcomponentToOwningResolver(ProvisionBinding subcomponentBuilderBinding) {
checkArgument(subcomponentBuilderBinding.kind().equals(SUBCOMPONENT_BUILDER));
Resolver owningResolver = getOwningResolver(subcomponentBuilderBinding).get();
TypeElement builderType = MoreTypes.asTypeElement(subcomponentBuilderBinding.key().type());
owningResolver.subcomponentsToResolve.add(
owningResolver.componentDescriptor.subcomponentsByBuilderType().get(builderType));
}
private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
keys.add(requestKey);
keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
return keys.build();
}
/**
* Returns a synthetic binding that depends on individual multibinding contributions.
*
* <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
* returns {@link Optional#empty()}.
*
* <p>If there are production {@code multibindingContributions} or the request is for any of
* the following types, returns a {@link ProductionBinding}.
*
* <ul>
* <li>{@code Set<Produced<T>>}
* <li>{@code Map<K, Producer<V>>}
* <li>{@code Map<K, Produced<V>>}
* </ul>
*
* Otherwise, returns a {@link ProvisionBinding}.
*/
private Optional<ContributionBinding> syntheticMultibinding(
Key key,
Iterable<ContributionBinding> multibindingContributions,
Iterable<MultibindingDeclaration> multibindingDeclarations) {
return isEmpty(multibindingContributions) && isEmpty(multibindingDeclarations)
? Optional.empty()
: Optional.of(bindingFactory.syntheticMultibinding(key, multibindingContributions));
}
private Optional<ProvisionBinding> syntheticSubcomponentBuilderBinding(
ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
return subcomponentDeclarations.isEmpty()
? Optional.empty()
: Optional.of(bindingFactory.subcomponentBuilderBinding(subcomponentDeclarations));
}
/**
* Returns a synthetic binding for {@code @Qualifier Optional<Type>} if there are any {@code
* optionalBindingDeclarations}.
*
* <p>If there are no bindings for the underlying key (the key for dependency requests for
* {@code Type}), returns a provision binding that always returns {@link Optional#empty()}.
*
* <p>If there are any production bindings for the underlying key, returns a production
* binding. Otherwise returns a provision binding.
*/
private Optional<ContributionBinding> syntheticOptionalBinding(
Key key, ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations) {
return optionalBindingDeclarations.isEmpty()
? Optional.empty()
: Optional.of(
bindingFactory.syntheticOptionalBinding(
key,
getRequestKind(OptionalType.from(key).valueType()),
lookUpBindings(keyFactory.unwrapOptional(key).get())));
}
private ImmutableSet<ContributionBinding> createDelegateBindings(
ImmutableSet<DelegateDeclaration> delegateDeclarations) {
ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
for (DelegateDeclaration delegateDeclaration : delegateDeclarations) {
builder.add(createDelegateBinding(delegateDeclaration));
}
return builder.build();
}
/**
* Creates one (and only one) delegate binding for a delegate declaration, based on the
* resolved bindings of the right-hand-side of a {@link dagger.Binds} method. If there are
* duplicate bindings for the dependency key, there should still be only one binding for the
* delegate key.
*/
private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDeclaration) {
Key delegateKey = delegateDeclaration.delegateRequest().key();
if (cycleStack.contains(delegateKey)) {
return bindingFactory.missingDelegateBinding(delegateDeclaration);
}
ResolvedBindings resolvedDelegate;
try {
cycleStack.push(delegateKey);
resolvedDelegate = lookUpBindings(delegateKey);
} finally {
cycleStack.pop();
}
if (resolvedDelegate.contributionBindings().isEmpty()) {
// This is guaranteed to result in a missing binding error, so it doesn't matter if the
// binding is a Provision or Production, except if it is a @IntoMap method, in which
// case the key will be of type Map<K, Provider<V>>, which will be "upgraded" into a
// Map<K, Producer<V>> if it's requested in a ProductionComponent. This may result in a
// strange error, that the RHS needs to be provided with an @Inject or @Provides
// annotated method, but a user should be able to figure out if a @Produces annotation
// is needed.
// TODO(gak): revisit how we model missing delegates if/when we clean up how we model
// binding declarations
return bindingFactory.missingDelegateBinding(delegateDeclaration);
}
// It doesn't matter which of these is selected, since they will later on produce a
// duplicate binding error.
ContributionBinding explicitDelegate =
resolvedDelegate.contributionBindings().iterator().next();
return bindingFactory.delegateBinding(delegateDeclaration, explicitDelegate);
}
// TODO(dpb,ronshapiro): requestKey appears to be interchangeable with each binding's .key(),
// but should it? We're currently conflating the two all over the place and it would be good
// to unify, or if it's necessary, clarify why with docs+tests. Specifically, should we also
// be checking these for keysMatchingRequest?
private ImmutableSetMultimap<ComponentDescriptor, ContributionBinding>
indexBindingsByOwningComponent(
Key requestKey, Iterable<? extends ContributionBinding> bindings) {
ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> index =
ImmutableSetMultimap.builder();
for (ContributionBinding binding : bindings) {
index.put(getOwningComponent(requestKey, binding), binding);
}
return index.build();
}
/**
* Returns the component that should contain the framework field for {@code binding}.
*
* <p>If {@code binding} is either not bound in an ancestor component or depends transitively
* on bindings in this component, returns this component.
*
* <p>Otherwise, resolves {@code request} in this component's parent in order to resolve any
* multibinding contributions in the parent, and returns the parent-resolved {@link
* ResolvedBindings#owningComponent(ContributionBinding)}.
*/
private ComponentDescriptor getOwningComponent(Key requestKey, ContributionBinding binding) {
if (isResolvedInParent(requestKey, binding)
&& !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
ResolvedBindings parentResolvedBindings =
parentResolver.get().resolvedContributionBindings.get(requestKey);
return parentResolvedBindings.owningComponent(binding);
} else {
return componentDescriptor;
}
}
/**
* Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain
* #resolve resolves} the {@link Key} in this component's parent. Don't resolve directly in
* the owning component in case it depends on multibindings in any of its descendants.
*/
private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
Optional<Resolver> owningResolver = getOwningResolver(binding);
if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
parentResolver.get().resolve(requestKey);
return true;
} else {
return false;
}
}
private Optional<Resolver> getOwningResolver(ContributionBinding binding) {
if (binding.scope().isPresent() && binding.scope().get().isReusable()) {
for (Resolver requestResolver : getResolverLineage().reverse()) {
// If a @Reusable binding was resolved in an ancestor, use that component.
if (requestResolver.resolvedContributionBindings.containsKey(binding.key())) {
return Optional.of(requestResolver);
}
}
// If a @Reusable binding was not resolved in any ancestor, resolve it here.
return Optional.empty();
}
for (Resolver requestResolver : getResolverLineage().reverse()) {
if (requestResolver.explicitBindingsSet.contains(binding)
|| requestResolver.subcomponentDeclarations.containsKey(binding.key())) {
return Optional.of(requestResolver);
}
}
// look for scope separately. we do this for the case where @Singleton can appear twice
// in the † compatibility mode
Optional<Scope> bindingScope = binding.scope();
if (bindingScope.isPresent()) {
for (Resolver requestResolver : getResolverLineage().reverse()) {
if (requestResolver.componentDescriptor.scopes().contains(bindingScope.get())) {
return Optional.of(requestResolver);
}
}
}
return Optional.empty();
}
/** 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));
}
/**
* For all {@linkplain #keysMatchingRequest(Key) keys matching {@code requestKey}}, applies
* {@code getDeclarationsPerKey} and collects the values into an {@link ImmutableSet}.
*/
private <T extends BindingDeclaration> ImmutableSet<T> getAllMatchingBindingDeclarations(
Key requestKey, Function<Key, Collection<T>> getDeclarationsPerKey) {
return keysMatchingRequest(requestKey)
.stream()
.flatMap(key -> getDeclarationsPerKey.apply(key).stream())
.collect(toImmutableSet());
}
/**
* Returns the explicit {@link ContributionBinding}s that match the {@code key} from this and
* all ancestor resolvers.
*/
private ImmutableSet<ContributionBinding> getExplicitBindings(Key key) {
ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
bindings.addAll(resolver.getLocalExplicitBindings(key));
}
return bindings.build();
}
/**
* Returns the explicit {@link ContributionBinding}s that match the {@code key} from this
* resolver.
*/
private ImmutableSet<ContributionBinding> getLocalExplicitBindings(Key key) {
return new ImmutableSet.Builder<ContributionBinding>()
.addAll(explicitBindings.get(key))
.addAll(
createDelegateBindings(
delegateDeclarations.get(keyFactory.convertToDelegateKey(key))))
.build();
}
/**
* Returns the explicit multibinding contributions that contribute to the map or set requested
* by {@code key} from this and all ancestor resolvers.
*/
private ImmutableSet<ContributionBinding> getExplicitMultibindings(Key key) {
ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
multibindings.addAll(resolver.getLocalExplicitMultibindings(key));
}
return multibindings.build();
}
/**
* Returns the explicit multibinding contributions that contribute to the map or set requested
* by {@code key} from this resolver.
*/
private ImmutableSet<ContributionBinding> getLocalExplicitMultibindings(Key key) {
ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
multibindings.addAll(explicitMultibindings.get(key));
if (!MapType.isMap(key)
|| MapType.from(key).isRawType()
|| MapType.from(key).valuesAreFrameworkType()) {
// There are no @Binds @IntoMap delegate declarations for Map<K, V> requests. All
// @IntoMap requests must be for Map<K, Framework<V>>.
multibindings.addAll(
createDelegateBindings(
delegateMultibindingDeclarations.get(keyFactory.convertToDelegateKey(key))));
}
return multibindings.build();
}
/**
* Returns the {@link MultibindingDeclaration}s that match the {@code key} from this and all
* ancestor resolvers.
*/
private ImmutableSet<MultibindingDeclaration> getMultibindingDeclarations(Key key) {
ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
multibindingDeclarations.addAll(resolver.multibindingDeclarations.get(key));
}
return multibindingDeclarations.build();
}
/**
* Returns the {@link SubcomponentDeclaration}s that match the {@code key} from this and all
* ancestor resolvers.
*/
private ImmutableSet<SubcomponentDeclaration> getSubcomponentDeclarations(Key key) {
ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations =
ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
subcomponentDeclarations.addAll(resolver.subcomponentDeclarations.get(key));
}
return subcomponentDeclarations.build();
}
/**
* Returns the {@link OptionalBindingDeclaration}s that match the {@code key} from this and
* all ancestor resolvers.
*/
private ImmutableSet<OptionalBindingDeclaration> getOptionalBindingDeclarations(Key key) {
Optional<Key> unwrapped = keyFactory.unwrapOptional(key);
if (!unwrapped.isPresent()) {
return ImmutableSet.of();
}
ImmutableSet.Builder<OptionalBindingDeclaration> declarations = ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
declarations.addAll(resolver.optionalBindingDeclarations.get(unwrapped.get()));
}
return declarations.build();
}
/**
* Returns the {@link ResolvedBindings} for {@code key} that was resolved in this resolver or
* an ancestor resolver. Only checks for {@link ContributionBinding}s as {@link
* MembersInjectionBinding}s are not inherited.
*/
private Optional<ResolvedBindings> getPreviouslyResolvedBindings(Key key) {
Optional<ResolvedBindings> result =
Optional.ofNullable(resolvedContributionBindings.get(key));
if (result.isPresent()) {
return result;
} else if (parentResolver.isPresent()) {
return parentResolver.get().getPreviouslyResolvedBindings(key);
} else {
return Optional.empty();
}
}
private void resolveMembersInjectionMethod(ComponentMethodDescriptor componentMethod) {
checkArgument(componentMethod.kind().equals(ComponentMethodKind.MEMBERS_INJECTION));
Key key = componentMethod.dependencyRequest().get().key();
ResolvedBindings bindings = lookUpMembersInjectionBinding(key);
resolveDependencies(bindings);
resolvedMembersInjectionBindings.put(key, bindings);
}
void resolve(Key key) {
// If we find a cycle, stop resolving. The original request will add it with all of the
// other resolved deps.
if (cycleStack.contains(key)) {
return;
}
// If the binding was previously resolved in this (sub)component, don't resolve it again.
if (resolvedContributionBindings.containsKey(key)) {
return;
}
/* If the binding was previously resolved in a supercomponent, then we may be able to avoid
* resolving it here and just depend on the supercomponent resolution.
*
* 1. If it depends transitively on multibinding contributions or optional bindings with
* bindings from this subcomponent, then we have to resolve it in this subcomponent so
* that it sees the local bindings.
*
* 2. If there are any explicit bindings in this component, they may conflict with those in
* the supercomponent, so resolve them here so that conflicts can be caught.
*/
if (getPreviouslyResolvedBindings(key).isPresent()) {
/* Resolve in the parent in case there are multibinding contributions or conflicts in some
* component between this one and the previously-resolved one. */
parentResolver.get().resolve(key);
if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
&& getLocalExplicitBindings(key).isEmpty()) {
/* Cache the inherited parent component's bindings in case resolving at the parent found
* bindings in some component between this one and the previously-resolved one. */
ResolvedBindings inheritedBindings =
getPreviouslyResolvedBindings(key).get().asInheritedIn(componentDescriptor);
resolvedContributionBindings.put(key, inheritedBindings);
return;
}
}
cycleStack.push(key);
try {
ResolvedBindings bindings = lookUpBindings(key);
resolveDependencies(bindings);
resolvedContributionBindings.put(key, bindings);
} finally {
cycleStack.pop();
}
}
/**
* {@link #resolve(Key) Resolves} each of the dependencies of the {@link
* ResolvedBindings#ownedBindings() owned bindings} of {@code resolvedBindings}.
*/
private void resolveDependencies(ResolvedBindings resolvedBindings) {
for (Binding binding : resolvedBindings.ownedBindings()) {
for (DependencyRequest dependency : binding.dependencies()) {
resolve(dependency.key());
}
}
}
/**
* Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
* all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
*/
ImmutableMap<Key, ResolvedBindings> getResolvedContributionBindings() {
ImmutableMap.Builder<Key, ResolvedBindings> builder =
ImmutableMap.builder();
builder.putAll(resolvedContributionBindings);
if (parentResolver.isPresent()) {
ImmutableMap<Key, ResolvedBindings> parentBindings =
parentResolver.get().getResolvedContributionBindings();
Collection<ResolvedBindings> bindingsResolvedInParent =
Maps.difference(parentBindings, resolvedContributionBindings)
.entriesOnlyOnLeft()
.values();
for (ResolvedBindings resolvedInParent : bindingsResolvedInParent) {
builder.put(
resolvedInParent.key(), resolvedInParent.asInheritedIn(componentDescriptor));
}
}
return builder.build();
}
/**
* Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
* resolvers, indexed by {@link ResolvedBindings#key()}.
*/
ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
}
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();
}
private final class LocalDependencyChecker {
private final Set<Object> cycleChecker = new HashSet<>();
/**
* Returns {@code true} if any of the bindings resolved for {@code key} are multibindings
* with contributions declared within this component's modules or optional bindings with
* present values declared within this component's modules, or if any of its unscoped
* dependencies depend on such bindings.
*
* <p>We don't care about scoped dependencies because they will never depend on bindings
* from subcomponents.
*
* @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
*/
boolean dependsOnLocalBindings(Key key) {
// Don't recur infinitely if there are valid cycles in the dependency graph.
// http://b/23032377
if (!cycleChecker.add(key)) {
return false;
}
return reentrantComputeIfAbsent(
keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
}
private boolean dependsOnLocalBindingsUncached(Key key) {
checkArgument(
getPreviouslyResolvedBindings(key).isPresent(),
"no previously resolved bindings in %s for %s",
Resolver.this,
key);
ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
if (hasLocalMultibindingContributions(previouslyResolvedBindings)
|| hasLocallyPresentOptionalBinding(previouslyResolvedBindings)) {
return true;
}
for (Binding binding : previouslyResolvedBindings.bindings()) {
if (dependsOnLocalBindings(binding)) {
return true;
}
}
return false;
}
/**
* Returns {@code true} if {@code binding} is unscoped (or has {@link Reusable @Reusable}
* scope) and depends on multibindings with contributions declared within this component's
* modules, or if any of its unscoped or {@link Reusable @Reusable} scoped dependencies
* depend on such local multibindings.
*
* <p>We don't care about non-reusable scoped dependencies because they will never depend on
* multibindings with contributions from subcomponents.
*/
boolean dependsOnLocalBindings(Binding binding) {
if (!cycleChecker.add(binding)) {
return false;
}
return reentrantComputeIfAbsent(
bindingDependsOnLocalBindingsCache, binding, this::dependsOnLocalBindingsUncached);
}
private boolean dependsOnLocalBindingsUncached(Binding binding) {
if ((!binding.scope().isPresent() || binding.scope().get().isReusable())
// TODO(beder): Figure out what happens with production subcomponents.
&& !binding.bindingType().equals(BindingType.PRODUCTION)) {
for (DependencyRequest dependency : binding.dependencies()) {
if (dependsOnLocalBindings(dependency.key())) {
return true;
}
}
}
return false;
}
/**
* Returns {@code true} if {@code resolvedBindings} contains a synthetic multibinding with
* at least one contribution declared within this component's modules.
*/
private boolean hasLocalMultibindingContributions(ResolvedBindings resolvedBindings) {
return any(
resolvedBindings.contributionBindings(),
ContributionBinding::isSyntheticMultibinding)
&& keysMatchingRequest(resolvedBindings.key())
.stream()
.anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
}
/**
* Returns {@code true} if {@code resolvedBindings} contains a synthetic optional binding
* for which there is an explicit present binding in this component.
*/
private boolean hasLocallyPresentOptionalBinding(ResolvedBindings resolvedBindings) {
return resolvedBindings
.contributionBindings()
.stream()
.map(ContributionBinding::kind)
.anyMatch(isEqual(OPTIONAL))
&& !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
.isEmpty();
}
}
}
/**
* A multimap of those {@code declarations} that are multibinding contribution declarations,
* indexed by the key of the set or map to which they contribute.
*/
static <T extends BindingDeclaration>
ImmutableSetMultimap<Key, T> multibindingContributionsByMultibindingKey(
Iterable<T> declarations) {
ImmutableSetMultimap.Builder<Key, T> builder = ImmutableSetMultimap.builder();
for (T declaration : declarations) {
if (declaration.key().multibindingContributionIdentifier().isPresent()) {
builder.put(
declaration
.key()
.toBuilder()
.multibindingContributionIdentifier(Optional.empty())
.build(),
declaration);
}
}
return builder.build();
}
}
/** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
private static <T extends BindingDeclaration> ImmutableSetMultimap<Key, T> indexByKey(
Iterable<T> declarations) {
return ImmutableSetMultimap.copyOf(Multimaps.index(declarations, BindingDeclaration::key));
}
}