dagger.internal.codegen.BindingGraphFactory.java Source code

Java tutorial

Introduction

Here is the source code for dagger.internal.codegen.BindingGraphFactory.java

Source

/*
 * 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.isEmpty;
import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.RequestKinds.getRequestKind;
import static dagger.internal.codegen.SourceFiles.generatedMonitoringModuleName;
import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
import static dagger.model.BindingKind.DELEGATE;
import static dagger.model.BindingKind.INJECTION;
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.util.ElementFilter.methodsIn;

import com.google.auto.common.MoreTypes;
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.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import dagger.MembersInjector;
import dagger.Reusable;
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.producers.internal.ProductionExecutorModule;
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 javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;

/** A factory for {@link BindingGraph} objects. */
final class BindingGraphFactory {
    private final DaggerElements elements;
    private final InjectBindingRegistry injectBindingRegistry;
    private final KeyFactory keyFactory;
    private final BindingFactory bindingFactory;
    private final CompilerOptions compilerOptions;
    private final ModuleDescriptor.Factory moduleDescriptorFactory;

    @Inject
    BindingGraphFactory(DaggerElements elements, InjectBindingRegistry injectBindingRegistry, KeyFactory keyFactory,
            BindingFactory bindingFactory, ModuleDescriptor.Factory moduleDescriptorFactory,
            CompilerOptions compilerOptions) {
        this.elements = elements;
        this.injectBindingRegistry = injectBindingRegistry;
        this.keyFactory = keyFactory;
        this.bindingFactory = bindingFactory;
        this.moduleDescriptorFactory = moduleDescriptorFactory;
        this.compilerOptions = compilerOptions;
    }

    /** Creates a binding graph for a root component. */
    BindingGraph create(ComponentDescriptor componentDescriptor) {
        checkArgument(componentDescriptor.kind().isTopLevel() || compilerOptions.aheadOfTimeSubcomponents());
        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();

        if (!componentDescriptor.kind().isForModuleValidation()) {
            // binding for the component itself
            explicitBindingsBuilder.add(bindingFactory.componentBinding(componentDescriptor.typeElement()));
        }

        // 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 creator.
        componentDescriptor.creatorDescriptor()
                .ifPresent(creatorDescriptor -> creatorDescriptor.boundInstanceRequirements().stream()
                        .map(requirement -> bindingFactory.boundInstanceBinding(requirement,
                                creatorDescriptor.elementForRequirement(requirement)))
                        .forEach(explicitBindingsBuilder::add));

        componentDescriptor.childComponentsDeclaredByBuilderEntryPoints()
                .forEach((builderEntryPoint, childComponent) -> {
                    if (!componentDescriptor.childComponentsDeclaredByModules().contains(childComponent)) {
                        explicitBindingsBuilder.add(bindingFactory.subcomponentCreatorBinding(
                                builderEntryPoint.methodElement(), componentDescriptor.typeElement()));
                    }
                });

        ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = ImmutableSet.builder();
        ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();

        // Collect transitive module bindings and multibinding declarations.
        for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
            explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
            multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
            subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
            delegatesBuilder.addAll(moduleDescriptor.delegateDeclarations());
            optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
        }

        final Resolver requestResolver = new Resolver(parentResolver, componentDescriptor,
                indexBindingDeclarationsByKey(explicitBindingsBuilder.build()),
                indexBindingDeclarationsByKey(multibindingDeclarations.build()),
                indexBindingDeclarationsByKey(subcomponentDeclarations.build()),
                indexBindingDeclarationsByKey(delegatesBuilder.build()),
                indexBindingDeclarationsByKey(optionalsBuilder.build()));
        for (DependencyRequest entryPoint : componentDescriptor.entryPoints()) {
            if (entryPoint.kind().equals(RequestKind.MEMBERS_INJECTION)) {
                requestResolver.resolveMembersInjection(entryPoint.key());
            } else {
                requestResolver.resolve(entryPoint.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<>();
        ImmutableList.Builder<BindingGraph> subgraphs = ImmutableList.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 BindingGraph.create(componentDescriptor, resolvedContributionBindingsMap,
                requestResolver.getResolvedMembersInjectionBindings(), subgraphs.build(),
                requestResolver.getOwnedModules(), requestResolver.getFactoryMethod());
    }

    /**
     * Returns all the modules that should be installed in the component. For production components
     * and production subcomponents that have a parent that is not a production component or
     * subcomponent, also includes the production monitoring module for the component and the
     * production executor module.
     */
    private ImmutableSet<ModuleDescriptor> modules(ComponentDescriptor componentDescriptor,
            Optional<Resolver> parentResolver) {
        return shouldIncludeImplicitProductionModules(componentDescriptor, parentResolver)
                ? new ImmutableSet.Builder<ModuleDescriptor>().addAll(componentDescriptor.modules())
                        .add(descriptorForMonitoringModule(componentDescriptor.typeElement()))
                        .add(descriptorForProductionExecutorModule()).build()
                : componentDescriptor.modules();
    }

    private boolean shouldIncludeImplicitProductionModules(ComponentDescriptor componentDescriptor,
            Optional<Resolver> parentResolver) {
        ComponentKind kind = componentDescriptor.kind();
        return kind.isProducer() && ((kind.isTopLevel() && !kind.isForModuleValidation())
                || (parentResolver.isPresent() && !parentResolver.get().componentDescriptor.kind().isProducer()));
    }

    /**
     * Returns a descriptor for a generated module that handles monitoring for production components.
     * This module is generated in the {@link MonitoringModuleProcessingStep}.
     *
     * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
     *     processor to retry in a later processing round.
     */
    private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
        return moduleDescriptorFactory.create(
                elements.checkTypePresent(generatedMonitoringModuleName(componentDefinitionType).toString()));
    }

    /** Returns a descriptor {@link ProductionExecutorModule}. */
    private ModuleDescriptor descriptorForProductionExecutorModule() {
        return moduleDescriptorFactory.create(elements.getTypeElement(ProductionExecutorModule.class));
    }

    /** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
    private static <T extends BindingDeclaration> ImmutableSetMultimap<Key, T> indexBindingDeclarationsByKey(
            Iterable<T> declarations) {
        return ImmutableSetMultimap.copyOf(Multimaps.index(declarations, BindingDeclaration::key));
    }

    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 = 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.childComponentsDeclaredByFactoryMethods().values());
            subcomponentsToResolve
                    .addAll(componentDescriptor.childComponentsDeclaredByBuilderEntryPoints().values());
        }

        /** Returns the optional factory method for this component. */
        Optional<ExecutableElement> getFactoryMethod() {
            return parentResolver.flatMap(
                    parent -> parent.componentDescriptor.getFactoryMethodForChildComponent(componentDescriptor))
                    .map(method -> method.methodElement());
        }

        /**
         * 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)
                        .filter(binding -> !isIncorrectlyScopedInPartialGraph(binding)).ifPresent(bindings::add);
            }

            return ResolvedBindings.forContributionBindings(requestKey, componentDescriptor,
                    indexBindingsByOwningComponent(requestKey, ImmutableSet.copyOf(bindings)),
                    multibindingDeclarations, subcomponentDeclarations, optionalBindingDeclarations);
        }

        /**
         * Returns true if this binding graph resolution is for a partial graph and the {@code @Inject}
         * binding's scope doesn't match any of the components in the current component ancestry. If so,
         * the binding is not owned by any of the currently known components, and will be owned by a
         * future ancestor (or, if never owned, will result in an incompatibly scoped binding error at
         * the root component).
         */
        private boolean isIncorrectlyScopedInPartialGraph(ProvisionBinding binding) {
            checkArgument(binding.kind().equals(INJECTION));
            Resolver owningResolver = getOwningResolver(binding).orElse(this);
            ComponentDescriptor owningComponent = owningResolver.componentDescriptor;
            return !rootComponent().kind().isTopLevel() && binding.scope().isPresent()
                    && !owningComponent.scopes().contains(binding.scope().get());
        }

        private ComponentDescriptor rootComponent() {
            return parentResolver.map(Resolver::rootComponent).orElse(componentDescriptor);
        }

        /** 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 subcomponentCreatorBinding) {
            checkArgument(subcomponentCreatorBinding.kind().equals(SUBCOMPONENT_BUILDER));
            Resolver owningResolver = getOwningResolver(subcomponentCreatorBinding).get();

            TypeElement builderType = MoreTypes.asTypeElement(subcomponentCreatorBinding.key().type());
            owningResolver.subcomponentsToResolve
                    .add(owningResolver.componentDescriptor.getChildComponentWithBuilderType(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.subcomponentCreatorBinding(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.unresolvedDelegateBinding(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.unresolvedDelegateBinding(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) {
            // TODO(ronshapiro): extract the different pieces of this method into their own methods
            if ((binding.scope().isPresent() && binding.scope().get().isProductionScope())
                    || binding.bindingType().equals(BindingType.PRODUCTION)) {
                for (Resolver requestResolver : getResolverLineage()) {
                    // Resolve @Inject @ProductionScope bindings at the highest production component.
                    if (binding.kind().equals(INJECTION)
                            && requestResolver.componentDescriptor.kind().isProducer()) {
                        return Optional.of(requestResolver);
                    }

                    // Resolve explicit @Produces and @ProductionScope bindings at the highest component that
                    // installs the binding.
                    if (requestResolver.containsExplicitBinding(binding)) {
                        return Optional.of(requestResolver);
                    }
                }
            }

            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.
                    ResolvedBindings resolvedBindings = requestResolver.resolvedContributionBindings
                            .get(binding.key());
                    if (resolvedBindings != null && resolvedBindings.bindings().contains(binding)) {
                        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.containsExplicitBinding(binding)) {
                    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();
        }

        private boolean containsExplicitBinding(ContributionBinding binding) {
            return explicitBindingsSet.contains(binding) || resolverContainsDelegateDeclarationForBinding(binding)
                    || subcomponentDeclarations.containsKey(binding.key());
        }

        /** Returns true if {@code binding} was installed in a module in this resolver's component. */
        private boolean resolverContainsDelegateDeclarationForBinding(ContributionBinding binding) {
            return binding.kind().equals(DELEGATE) && delegateDeclarations.get(binding.key()).stream()
                    .anyMatch(declaration -> declaration.contributingModule().equals(binding.contributingModule())
                            && declaration.bindingElement().equals(binding.bindingElement()));
        }

        /** Returns the resolver lineage from parent to child. */
        private ImmutableList<Resolver> getResolverLineage() {
            ImmutableList.Builder<Resolver> resolverList = ImmutableList.builder();
            for (Optional<Resolver> currentResolver = Optional.of(this); currentResolver
                    .isPresent(); currentResolver = currentResolver.get().parentResolver) {
                resolverList.add(currentResolver.get());
            }
            return resolverList.build().reverse();
        }

        /**
         * 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))
                    // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
                    // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
                    // value type if it's a Map<K, Provider/Producer<V>> before looking in
                    // delegateDeclarations. createDelegateBindings() will create bindings with the properly
                    // wrapped key type.
                    .addAll(createDelegateBindings(delegateDeclarations.get(keyFactory.unwrapMapValueType(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()) {
                // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
                // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
                // value type if it's a Map<K, Provider/Producer<V>> before looking in
                // delegateMultibindingDeclarations. createDelegateBindings() will create bindings with the
                // properly wrapped key type.
                multibindings.addAll(createDelegateBindings(
                        delegateMultibindingDeclarations.get(keyFactory.unwrapMapValueType(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 resolveMembersInjection(Key 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 an ancestor component, then we may be able to
             * avoid resolving it here and just depend on the ancestor component 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 ancestor component, 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);
                resolvedContributionBindings.put(key, bindings);
                resolveDependencies(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.modules()).immutableCopy()
                    : ImmutableSet.<ModuleDescriptor>of();
        }

        ImmutableSet<ModuleDescriptor> getOwnedModules() {
            return Sets.difference(componentDescriptor.modules(), 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(key)
                        || hasLocalOptionalBindingContribution(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 there is at least one multibinding contribution declared within
             * this component's modules that matches the key.
             */
            private boolean hasLocalMultibindingContributions(Key requestKey) {
                return keysMatchingRequest(requestKey).stream()
                        .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
            }

            /**
             * Returns {@code true} if there is a contribution in this component for an {@code
             * Optional<Foo>} key that has not been contributed in a parent.
             */
            private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
                if (resolvedBindings.contributionBindings().stream().map(ContributionBinding::kind)
                        .anyMatch(isEqual(OPTIONAL))) {
                    return !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
                            .isEmpty();
                } else {
                    // If a parent contributes a @Provides Optional<Foo> binding and a child has a
                    // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
                    // Foo on its own
                    return !getOptionalBindingDeclarations(resolvedBindings.key()).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();
    }
}