/* * Copyright (C) 2017 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.binding; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.MoreTypes.asDeclared; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getOnlyElement; import static dagger.internal.codegen.base.MoreAnnotationMirrors.wrapOptionalInEquivalence; import static dagger.internal.codegen.base.Scopes.uniqueScopeOf; import static dagger.internal.codegen.binding.Binding.hasNonDefaultTypeParameters; import static dagger.internal.codegen.binding.ComponentDescriptor.isComponentProductionMethod; import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType; import static dagger.internal.codegen.binding.ContributionBinding.bindingKindForMultibindingKey; import static dagger.internal.codegen.binding.MapKeys.getMapKey; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static dagger.model.BindingKind.ASSISTED_FACTORY; import static dagger.model.BindingKind.ASSISTED_INJECTION; import static dagger.model.BindingKind.BOUND_INSTANCE; import static dagger.model.BindingKind.COMPONENT; import static dagger.model.BindingKind.COMPONENT_DEPENDENCY; import static dagger.model.BindingKind.COMPONENT_PRODUCTION; import static dagger.model.BindingKind.COMPONENT_PROVISION; import static dagger.model.BindingKind.DELEGATE; import static dagger.model.BindingKind.INJECTION; import static dagger.model.BindingKind.MEMBERS_INJECTOR; import static dagger.model.BindingKind.OPTIONAL; import static dagger.model.BindingKind.PRODUCTION; import static dagger.model.BindingKind.PROVISION; import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR; import static javax.lang.model.element.ElementKind.CONSTRUCTOR; import static javax.lang.model.element.ElementKind.METHOD; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import dagger.Module; import dagger.assisted.AssistedInject; import dagger.internal.codegen.base.ContributionType; import dagger.internal.codegen.base.MapType; import dagger.internal.codegen.base.SetType; import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; import dagger.internal.codegen.binding.ProductionBinding.ProductionKind; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import dagger.internal.codegen.langmodel.DaggerElements; import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.model.DependencyRequest; import dagger.model.Key; import dagger.model.RequestKind; import dagger.producers.Produced; import dagger.producers.Producer; import java.util.Optional; import java.util.function.BiFunction; import javax.inject.Inject; import javax.inject.Provider; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; /** A factory for {@link Binding} objects. */ public final class BindingFactory { private final DaggerTypes types; private final KeyFactory keyFactory; private final DependencyRequestFactory dependencyRequestFactory; private final InjectionSiteFactory injectionSiteFactory; private final DaggerElements elements; private final InjectionAnnotations injectionAnnotations; private final KotlinMetadataUtil metadataUtil; @Inject BindingFactory( DaggerTypes types, DaggerElements elements, KeyFactory keyFactory, DependencyRequestFactory dependencyRequestFactory, InjectionSiteFactory injectionSiteFactory, InjectionAnnotations injectionAnnotations, KotlinMetadataUtil metadataUtil) { this.types = types; this.elements = elements; this.keyFactory = keyFactory; this.dependencyRequestFactory = dependencyRequestFactory; this.injectionSiteFactory = injectionSiteFactory; this.injectionAnnotations = injectionAnnotations; this.metadataUtil = metadataUtil; } /** * Returns an {@link dagger.model.BindingKind#INJECTION} binding. * * @param constructorElement the {@code @Inject}-annotated constructor * @param resolvedType the parameterized type if the constructor is for a generic class and the * binding should be for the parameterized type */ // TODO(dpb): See if we can just pass the parameterized type and not also the constructor. public ProvisionBinding injectionBinding( ExecutableElement constructorElement, Optional resolvedType) { checkArgument(constructorElement.getKind().equals(CONSTRUCTOR)); checkArgument( isAnnotationPresent(constructorElement, Inject.class) || isAnnotationPresent(constructorElement, AssistedInject.class)); checkArgument(!injectionAnnotations.getQualifier(constructorElement).isPresent()); ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType()); DeclaredType constructedType = MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType()); // If the class this is constructing has some type arguments, resolve everything. if (!constructedType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get()); // Validate that we're resolving from the correct type. checkState( types.isSameType(types.erasure(resolved), types.erasure(constructedType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(constructedType)); constructorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement)); constructedType = resolved; } // Collect all dependency requests within the provision method. // Note: we filter out @Assisted parameters since these aren't considered dependency requests. ImmutableSet.Builder provisionDependencies = ImmutableSet.builder(); for (int i = 0; i < constructorElement.getParameters().size(); i++) { VariableElement parameter = constructorElement.getParameters().get(i); TypeMirror parameterType = constructorType.getParameterTypes().get(i); if (!AssistedInjectionAnnotations.isAssistedParameter(parameter)) { provisionDependencies.add( dependencyRequestFactory.forRequiredResolvedVariable(parameter, parameterType)); } } Key key = keyFactory.forInjectConstructorWithResolvedType(constructedType); ProvisionBinding.Builder builder = ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .bindingElement(constructorElement) .key(key) .provisionDependencies(provisionDependencies.build()) .injectionSites(injectionSiteFactory.getInjectionSites(constructedType)) .kind( isAnnotationPresent(constructorElement, AssistedInject.class) ? ASSISTED_INJECTION : INJECTION) .scope(uniqueScopeOf(constructorElement.getEnclosingElement())); TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement()); if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) { builder.unresolved(injectionBinding(constructorElement, Optional.empty())); } return builder.build(); } public ProvisionBinding assistedFactoryBinding( TypeElement factory, Optional resolvedType) { // If the class this is constructing has some type arguments, resolve everything. DeclaredType factoryType = MoreTypes.asDeclared(factory.asType()); if (!factoryType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get()); // Validate that we're resolving from the correct type by checking that the erasure of the // resolvedType is the same as the erasure of the factoryType. checkState( types.isSameType(types.erasure(resolved), types.erasure(factoryType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(factoryType)); factoryType = resolved; } ExecutableElement factoryMethod = AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements, types); ExecutableType factoryMethodType = MoreTypes.asExecutable(types.asMemberOf(factoryType, factoryMethod)); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .key(Key.builder(factoryType).build()) .bindingElement(factory) .provisionDependencies( ImmutableSet.of( DependencyRequest.builder() .key(Key.builder(factoryMethodType.getReturnType()).build()) .kind(RequestKind.PROVIDER) .build())) .kind(ASSISTED_FACTORY) .build(); } /** * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated * method. * * @param contributedBy the installed module that declares or inherits the method */ public ProvisionBinding providesMethodBinding( ExecutableElement providesMethod, TypeElement contributedBy) { return setMethodBindingProperties( ProvisionBinding.builder(), providesMethod, contributedBy, keyFactory.forProvidesMethod(providesMethod, contributedBy), this::providesMethodBinding) .kind(PROVISION) .scope(uniqueScopeOf(providesMethod)) .nullableType(getNullableType(providesMethod)) .build(); } /** * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated * method. * * @param contributedBy the installed module that declares or inherits the method */ public ProductionBinding producesMethodBinding( ExecutableElement producesMethod, TypeElement contributedBy) { // TODO(beder): Add nullability checking with Java 8. ProductionBinding.Builder builder = setMethodBindingProperties( ProductionBinding.builder(), producesMethod, contributedBy, keyFactory.forProducesMethod(producesMethod, contributedBy), this::producesMethodBinding) .kind(PRODUCTION) .productionKind(ProductionKind.fromProducesMethod(producesMethod)) .thrownTypes(producesMethod.getThrownTypes()) .executorRequest(dependencyRequestFactory.forProductionImplementationExecutor()) .monitorRequest(dependencyRequestFactory.forProductionComponentMonitor()); return builder.build(); } private > B setMethodBindingProperties( B builder, ExecutableElement method, TypeElement contributedBy, Key key, BiFunction create) { checkArgument(method.getKind().equals(METHOD)); ExecutableType methodType = MoreTypes.asExecutable( types.asMemberOf(MoreTypes.asDeclared(contributedBy.asType()), method)); if (!types.isSameType(methodType, method.asType())) { builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement()))); } boolean isKotlinObject = metadataUtil.isObjectClass(contributedBy) || metadataUtil.isCompanionObjectClass(contributedBy); return builder .contributionType(ContributionType.fromBindingElement(method)) .bindingElement(method) .contributingModule(contributedBy) .isContributingModuleKotlinObject(isKotlinObject) .key(key) .dependencies( dependencyRequestFactory.forRequiredResolvedVariables( method.getParameters(), methodType.getParameterTypes())) .wrappedMapKeyAnnotation(wrapOptionalInEquivalence(getMapKey(method))); } /** * Returns a {@link dagger.model.BindingKind#MULTIBOUND_MAP} or {@link * dagger.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution * bindings. * * @param key a key that may be satisfied by a multibinding */ public ContributionBinding syntheticMultibinding( Key key, Iterable multibindingContributions) { ContributionBinding.Builder builder = multibindingRequiresProduction(key, multibindingContributions) ? ProductionBinding.builder() : ProvisionBinding.builder(); return builder .contributionType(ContributionType.UNIQUE) .key(key) .dependencies( dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions)) .kind(bindingKindForMultibindingKey(key)) .build(); } private boolean multibindingRequiresProduction( Key key, Iterable multibindingContributions) { if (MapType.isMap(key)) { MapType mapType = MapType.from(key); if (mapType.valuesAreTypeOf(Producer.class) || mapType.valuesAreTypeOf(Produced.class)) { return true; } } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) { return true; } return Iterables.any( multibindingContributions, binding -> binding.bindingType().equals(BindingType.PRODUCTION)); } /** Returns a {@link dagger.model.BindingKind#COMPONENT} binding for the component. */ public ProvisionBinding componentBinding(TypeElement componentDefinitionType) { checkNotNull(componentDefinitionType); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .bindingElement(componentDefinitionType) .key(keyFactory.forType(componentDefinitionType.asType())) .kind(COMPONENT) .build(); } /** * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's * dependency. */ public ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) { checkNotNull(dependency); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .bindingElement(dependency.typeElement()) .key(keyFactory.forType(dependency.type())) .kind(COMPONENT_DEPENDENCY) .build(); } /** * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's * dependency. * * @param componentDescriptor the component with the dependency, not the dependency that has the * method */ public ContributionBinding componentDependencyMethodBinding( ComponentDescriptor componentDescriptor, ExecutableElement dependencyMethod) { checkArgument(dependencyMethod.getKind().equals(METHOD)); checkArgument(dependencyMethod.getParameters().isEmpty()); ContributionBinding.Builder builder; if (componentDescriptor.isProduction() && isComponentProductionMethod(elements, dependencyMethod)) { builder = ProductionBinding.builder() .key(keyFactory.forProductionComponentMethod(dependencyMethod)) .kind(COMPONENT_PRODUCTION) .thrownTypes(dependencyMethod.getThrownTypes()); } else { builder = ProvisionBinding.builder() .key(keyFactory.forComponentMethod(dependencyMethod)) .nullableType(getNullableType(dependencyMethod)) .kind(COMPONENT_PROVISION) .scope(uniqueScopeOf(dependencyMethod)); } return builder .contributionType(ContributionType.UNIQUE) .bindingElement(dependencyMethod) .build(); } /** * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a * {@code @BindsInstance}-annotated builder setter method or factory method parameter. */ ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, Element element) { checkArgument(element instanceof VariableElement || element instanceof ExecutableElement); VariableElement parameterElement = element instanceof VariableElement ? MoreElements.asVariable(element) : getOnlyElement(MoreElements.asExecutable(element).getParameters()); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .bindingElement(element) .key(requirement.key().get()) .nullableType(getNullableType(parameterElement)) .kind(BOUND_INSTANCE) .build(); } /** * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared by a component * method that returns a subcomponent builder. Use {{@link * #subcomponentCreatorBinding(ImmutableSet)}} for bindings declared using {@link * Module#subcomponents()}. * * @param component the component that declares or inherits the method */ ProvisionBinding subcomponentCreatorBinding( ExecutableElement subcomponentCreatorMethod, TypeElement component) { checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD)); checkArgument(subcomponentCreatorMethod.getParameters().isEmpty()); Key key = keyFactory.forSubcomponentCreatorMethod( subcomponentCreatorMethod, asDeclared(component.asType())); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .bindingElement(subcomponentCreatorMethod) .key(key) .kind(SUBCOMPONENT_CREATOR) .build(); } /** * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using {@link * Module#subcomponents()}. */ ProvisionBinding subcomponentCreatorBinding( ImmutableSet subcomponentDeclarations) { SubcomponentDeclaration subcomponentDeclaration = subcomponentDeclarations.iterator().next(); return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .key(subcomponentDeclaration.key()) .kind(SUBCOMPONENT_CREATOR) .build(); } /** * Returns a {@link dagger.model.BindingKind#DELEGATE} binding. * * @param delegateDeclaration the {@code @Binds}-annotated declaration * @param actualBinding the binding that satisfies the {@code @Binds} declaration */ ContributionBinding delegateBinding( DelegateDeclaration delegateDeclaration, ContributionBinding actualBinding) { switch (actualBinding.bindingType()) { case PRODUCTION: return buildDelegateBinding( ProductionBinding.builder().nullableType(actualBinding.nullableType()), delegateDeclaration, Producer.class); case PROVISION: return buildDelegateBinding( ProvisionBinding.builder() .scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())) .nullableType(actualBinding.nullableType()), delegateDeclaration, Provider.class); case MEMBERS_INJECTION: // fall-through to throw } throw new AssertionError("bindingType: " + actualBinding); } /** * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that * satisfies the {@code @Binds} declaration. */ public ContributionBinding unresolvedDelegateBinding(DelegateDeclaration delegateDeclaration) { return buildDelegateBinding( ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())), delegateDeclaration, Provider.class); } private ContributionBinding buildDelegateBinding( ContributionBinding.Builder builder, DelegateDeclaration delegateDeclaration, Class frameworkType) { boolean isKotlinObject = metadataUtil.isObjectClass(delegateDeclaration.contributingModule().get()) || metadataUtil.isCompanionObjectClass(delegateDeclaration.contributingModule().get()); return builder .contributionType(delegateDeclaration.contributionType()) .bindingElement(delegateDeclaration.bindingElement().get()) .contributingModule(delegateDeclaration.contributingModule().get()) .isContributingModuleKotlinObject(isKotlinObject) .key(keyFactory.forDelegateBinding(delegateDeclaration, frameworkType)) .dependencies(delegateDeclaration.delegateRequest()) .wrappedMapKeyAnnotation(delegateDeclaration.wrappedMapKey()) .kind(DELEGATE) .build(); } /** * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}. * * @param requestKind the kind of request for the optional binding * @param underlyingKeyBindings the possibly empty set of bindings that exist in the component for * the underlying (non-optional) key */ ContributionBinding syntheticOptionalBinding( Key key, RequestKind requestKind, ImmutableCollection underlyingKeyBindings) { if (underlyingKeyBindings.isEmpty()) { return ProvisionBinding.builder() .contributionType(ContributionType.UNIQUE) .key(key) .kind(OPTIONAL) .build(); } boolean requiresProduction = underlyingKeyBindings.stream() .anyMatch(binding -> binding.bindingType() == BindingType.PRODUCTION) || requestKind.equals(RequestKind.PRODUCER) // handles producerFromProvider cases || requestKind.equals(RequestKind.PRODUCED); // handles producerFromProvider cases return (requiresProduction ? ProductionBinding.builder() : ProvisionBinding.builder()) .contributionType(ContributionType.UNIQUE) .key(key) .kind(OPTIONAL) .dependencies(dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, requestKind)) .build(); } /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */ public ProvisionBinding membersInjectorBinding( Key key, MembersInjectionBinding membersInjectionBinding) { return ProvisionBinding.builder() .key(key) .contributionType(ContributionType.UNIQUE) .kind(MEMBERS_INJECTOR) .bindingElement(MoreTypes.asTypeElement(membersInjectionBinding.key().type())) .provisionDependencies(membersInjectionBinding.dependencies()) .injectionSites(membersInjectionBinding.injectionSites()) .build(); } /** * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding. * * @param resolvedType if {@code declaredType} is a generic class and {@code resolvedType} is a * parameterization of that type, the returned binding will be for the resolved type */ // TODO(dpb): See if we can just pass one nongeneric/parameterized type. public MembersInjectionBinding membersInjectionBinding( DeclaredType declaredType, Optional resolvedType) { // If the class this is injecting has some type arguments, resolve everything. if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { DeclaredType resolved = asDeclared(resolvedType.get()); // Validate that we're resolving from the correct type. checkState( types.isSameType(types.erasure(resolved), types.erasure(declaredType)), "erased expected type: %s, erased actual type: %s", types.erasure(resolved), types.erasure(declaredType)); declaredType = resolved; } ImmutableSortedSet injectionSites = injectionSiteFactory.getInjectionSites(declaredType); ImmutableSet dependencies = injectionSites.stream() .flatMap(injectionSite -> injectionSite.dependencies().stream()) .collect(toImmutableSet()); Key key = keyFactory.forMembersInjectedType(declaredType); TypeElement typeElement = MoreElements.asType(declaredType.asElement()); return new AutoValue_MembersInjectionBinding( key, dependencies, typeElement, hasNonDefaultTypeParameters(typeElement, key.type(), types) ? Optional.of( membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty())) : Optional.empty(), injectionSites); } }