| /* |
| * Copyright (C) 2016 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.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Verify.verify; |
| import static dagger.internal.codegen.Accessibility.isRawTypeAccessible; |
| import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom; |
| import static dagger.internal.codegen.BindingRequest.bindingRequest; |
| import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION; |
| import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock; |
| import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope; |
| import static dagger.internal.codegen.MemberSelect.staticFactoryCreation; |
| import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK; |
| import static dagger.internal.codegen.TypeNames.SINGLE_CHECK; |
| import static dagger.model.BindingKind.DELEGATE; |
| import static dagger.model.BindingKind.MULTIBOUND_MAP; |
| import static dagger.model.BindingKind.MULTIBOUND_SET; |
| |
| import com.google.auto.common.MoreTypes; |
| import com.google.common.collect.ImmutableList; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.CodeBlock; |
| import com.squareup.javapoet.MethodSpec; |
| import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; |
| import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; |
| import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod; |
| import dagger.model.DependencyRequest; |
| import dagger.model.RequestKind; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Optional; |
| import javax.inject.Provider; |
| import javax.lang.model.type.TypeMirror; |
| |
| /** A central repository of code expressions used to access any binding available to a component. */ |
| final class ComponentBindingExpressions { |
| |
| // TODO(dpb,ronshapiro): refactor this and ComponentRequirementFields into a |
| // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its |
| // parents? If so, maybe make BindingExpression.Factory create it. |
| |
| private final Optional<ComponentBindingExpressions> parent; |
| private final BindingGraph graph; |
| private final GeneratedComponentModel generatedComponentModel; |
| private final ComponentRequirementFields componentRequirementFields; |
| private final OptionalFactories optionalFactories; |
| private final DaggerTypes types; |
| private final DaggerElements elements; |
| private final CompilerOptions compilerOptions; |
| private final MembersInjectionMethods membersInjectionMethods; |
| private final InnerSwitchingProviders innerSwitchingProviders; |
| private final StaticSwitchingProviders staticSwitchingProviders; |
| private final ModifiableBindingExpressions modifiableBindingExpressions; |
| private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>(); |
| |
| ComponentBindingExpressions( |
| BindingGraph graph, |
| GeneratedComponentModel generatedComponentModel, |
| ComponentRequirementFields componentRequirementFields, |
| OptionalFactories optionalFactories, |
| DaggerTypes types, |
| DaggerElements elements, |
| CompilerOptions compilerOptions) { |
| this( |
| Optional.empty(), |
| graph, |
| generatedComponentModel, |
| componentRequirementFields, |
| new StaticSwitchingProviders(generatedComponentModel, types), |
| optionalFactories, |
| types, |
| elements, |
| compilerOptions); |
| } |
| |
| private ComponentBindingExpressions( |
| Optional<ComponentBindingExpressions> parent, |
| BindingGraph graph, |
| GeneratedComponentModel generatedComponentModel, |
| ComponentRequirementFields componentRequirementFields, |
| StaticSwitchingProviders staticSwitchingProviders, |
| OptionalFactories optionalFactories, |
| DaggerTypes types, |
| DaggerElements elements, |
| CompilerOptions compilerOptions) { |
| this.parent = parent; |
| this.graph = graph; |
| this.generatedComponentModel = generatedComponentModel; |
| this.componentRequirementFields = checkNotNull(componentRequirementFields); |
| this.optionalFactories = checkNotNull(optionalFactories); |
| this.types = checkNotNull(types); |
| this.elements = checkNotNull(elements); |
| this.compilerOptions = checkNotNull(compilerOptions); |
| this.membersInjectionMethods = |
| new MembersInjectionMethods(generatedComponentModel, this, graph, elements, types); |
| this.innerSwitchingProviders = |
| new InnerSwitchingProviders(generatedComponentModel, this, types); |
| this.staticSwitchingProviders = staticSwitchingProviders; |
| this.modifiableBindingExpressions = |
| new ModifiableBindingExpressions( |
| parent.map(cbe -> cbe.modifiableBindingExpressions), |
| this, |
| graph, |
| generatedComponentModel, |
| compilerOptions); |
| } |
| |
| /** |
| * Returns a new object representing the bindings available from a child component of this one. |
| */ |
| ComponentBindingExpressions forChildComponent( |
| BindingGraph childGraph, |
| GeneratedComponentModel childComponentModel, |
| ComponentRequirementFields childComponentRequirementFields) { |
| return new ComponentBindingExpressions( |
| Optional.of(this), |
| childGraph, |
| childComponentModel, |
| childComponentRequirementFields, |
| staticSwitchingProviders, |
| optionalFactories, |
| types, |
| elements, |
| compilerOptions); |
| } |
| |
| /* Returns the {@link ModifiableBindingExpressions} for this component. */ |
| ModifiableBindingExpressions modifiableBindingExpressions() { |
| return modifiableBindingExpressions; |
| } |
| |
| /** |
| * Returns an expression that evaluates to the value of a binding request for a binding owned by |
| * this component or an ancestor. |
| * |
| * @param requestingClass the class that will contain the expression |
| * @throws IllegalStateException if there is no binding expression that satisfies the request |
| */ |
| Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) { |
| return getBindingExpression(request).getDependencyExpression(requestingClass); |
| } |
| |
| /** |
| * Equivalent to {@link #getDependencyExpression(BindingRequest, ClassName)} that is used only |
| * when the request is for implementation of a component method. |
| * |
| * @throws IllegalStateException if there is no binding expression that satisfies the request |
| */ |
| Expression getDependencyExpressionForComponentMethod( |
| BindingRequest request, |
| ComponentMethodDescriptor componentMethod, |
| GeneratedComponentModel componentModel) { |
| return getBindingExpression(request) |
| .getDependencyExpressionForComponentMethod(componentMethod, componentModel); |
| } |
| |
| /** |
| * Returns the {@link CodeBlock} for the method arguments used with the factory {@code create()} |
| * method for the given {@link ContributionBinding binding}. |
| */ |
| CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) { |
| return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding)); |
| } |
| |
| private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) { |
| ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); |
| |
| if (binding.requiresModuleInstance()) { |
| arguments.add( |
| componentRequirementFields.getExpressionDuringInitialization( |
| ComponentRequirement.forModule(binding.contributingModule().get().asType()), |
| generatedComponentModel.name())); |
| } |
| |
| binding.frameworkDependencies().stream() |
| .map(BindingRequest::bindingRequest) |
| .map( |
| request -> |
| getDependencyExpression(request, generatedComponentModel.name())) |
| .map(Expression::codeBlock) |
| .forEach(arguments::add); |
| |
| return arguments.build(); |
| } |
| |
| /** |
| * Returns an expression that evaluates to the value of a dependency request, for passing to a |
| * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one. |
| * |
| * <p>If the method is a generated static {@link InjectionMethods injection method}, each |
| * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the |
| * case for this dependency, the returned expression will use a cast to evaluate to the raw type. |
| * |
| * @param requestingClass the class that will contain the expression |
| */ |
| Expression getDependencyArgumentExpression( |
| DependencyRequest dependencyRequest, ClassName requestingClass) { |
| |
| TypeMirror dependencyType = dependencyRequest.key().type(); |
| Expression dependencyExpression = |
| getDependencyExpression(bindingRequest(dependencyRequest), requestingClass); |
| |
| if (dependencyRequest.kind().equals(RequestKind.INSTANCE) |
| && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName()) |
| && isRawTypeAccessible(dependencyType, requestingClass.packageName())) { |
| return dependencyExpression.castTo(types.erasure(dependencyType)); |
| } |
| |
| return dependencyExpression; |
| } |
| |
| /** Returns the implementation of a component method. */ |
| MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) { |
| checkArgument(componentMethod.dependencyRequest().isPresent()); |
| BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get()); |
| return MethodSpec.overriding( |
| componentMethod.methodElement(), |
| MoreTypes.asDeclared(graph.componentType().asType()), |
| types) |
| .addCode( |
| getBindingExpression(request) |
| .getComponentMethodImplementation(componentMethod, generatedComponentModel)) |
| .build(); |
| } |
| |
| /** Returns the {@link BindingExpression} for the given {@link BindingRequest}. */ |
| BindingExpression getBindingExpression(BindingRequest request) { |
| if (expressions.containsKey(request)) { |
| return expressions.get(request); |
| } |
| Optional<BindingExpression> expression = |
| modifiableBindingExpressions.maybeCreateModifiableBindingExpression(request); |
| if (!expression.isPresent()) { |
| ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| if (resolvedBindings != null && !resolvedBindings.ownedBindings().isEmpty()) { |
| expression = Optional.of(createBindingExpression(resolvedBindings, request)); |
| } |
| } |
| if (expression.isPresent()) { |
| expressions.put(request, expression.get()); |
| return expression.get(); |
| } |
| checkArgument(parent.isPresent(), "no expression found for %s", request); |
| return parent.get().getBindingExpression(request); |
| } |
| |
| /** Creates a binding expression. */ |
| BindingExpression createBindingExpression( |
| ResolvedBindings resolvedBindings, BindingRequest request) { |
| switch (resolvedBindings.bindingType()) { |
| case MEMBERS_INJECTION: |
| checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION)); |
| return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods); |
| |
| case PROVISION: |
| return provisionBindingExpression(resolvedBindings, request); |
| |
| case PRODUCTION: |
| return productionBindingExpression(resolvedBindings, request); |
| |
| default: |
| throw new AssertionError(resolvedBindings); |
| } |
| } |
| |
| /** |
| * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings |
| * or a {@link dagger.producers.Producer} for production bindings. |
| */ |
| private FrameworkInstanceBindingExpression frameworkInstanceBindingExpression( |
| ResolvedBindings resolvedBindings) { |
| // TODO(user): Consider merging the static factory creation logic into CreationExpressions? |
| Optional<MemberSelect> staticMethod = |
| useStaticFactoryCreation(resolvedBindings.contributionBinding()) |
| ? staticFactoryCreation(resolvedBindings) |
| : Optional.empty(); |
| FrameworkInstanceCreationExpression frameworkInstanceCreationExpression = |
| resolvedBindings.scope().isPresent() |
| ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings)) |
| : frameworkInstanceCreationExpression(resolvedBindings); |
| FrameworkInstanceSupplier frameworkInstanceSupplier = |
| staticMethod.isPresent() |
| ? staticMethod::get |
| : new FrameworkFieldInitializer( |
| generatedComponentModel, resolvedBindings, frameworkInstanceCreationExpression); |
| |
| switch (resolvedBindings.bindingType()) { |
| case PROVISION: |
| return new ProviderInstanceBindingExpression( |
| resolvedBindings, frameworkInstanceSupplier, types, elements); |
| case PRODUCTION: |
| return new ProducerNodeInstanceBindingExpression( |
| resolvedBindings, frameworkInstanceSupplier, types, elements, generatedComponentModel); |
| default: |
| throw new AssertionError("invalid binding type: " + resolvedBindings.bindingType()); |
| } |
| } |
| |
| private FrameworkInstanceCreationExpression scope( |
| ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) { |
| return () -> |
| CodeBlock.of( |
| "$T.provider($L)", |
| resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK, |
| unscoped.creationExpression()); |
| } |
| |
| /** |
| * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a |
| * {@link dagger.producers.Producer} for production bindings. |
| */ |
| private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression( |
| ResolvedBindings resolvedBindings) { |
| checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION)); |
| ContributionBinding binding = resolvedBindings.contributionBinding(); |
| switch (binding.kind()) { |
| case COMPONENT: |
| // The cast can be removed when we drop java 7 source support |
| return new InstanceFactoryCreationExpression( |
| () -> CodeBlock.of("($T) this", binding.key().type())); |
| |
| case BOUND_INSTANCE: |
| return instanceFactoryCreationExpression( |
| binding, ComponentRequirement.forBoundInstance(binding)); |
| |
| case COMPONENT_DEPENDENCY: |
| return instanceFactoryCreationExpression( |
| binding, ComponentRequirement.forDependency(binding.key().type())); |
| |
| case COMPONENT_PROVISION: |
| return new DependencyMethodProviderCreationExpression( |
| binding, generatedComponentModel, componentRequirementFields, compilerOptions, graph); |
| |
| case SUBCOMPONENT_BUILDER: |
| return new SubcomponentBuilderProviderCreationExpression( |
| binding.key().type(), generatedComponentModel.getSubcomponentName(binding.key())); |
| |
| case INJECTION: |
| case PROVISION: |
| return compilerOptions.experimentalAndroidMode2() |
| ? staticSwitchingProviders.newCreationExpression(binding, this) |
| : new InjectionOrProvisionProviderCreationExpression(binding, this); |
| |
| case COMPONENT_PRODUCTION: |
| return new DependencyMethodProducerCreationExpression( |
| binding, generatedComponentModel, componentRequirementFields, graph); |
| |
| case PRODUCTION: |
| return new ProducerCreationExpression(binding, this); |
| |
| case MULTIBOUND_SET: |
| return new SetFactoryCreationExpression(binding, generatedComponentModel, this, graph); |
| |
| case MULTIBOUND_MAP: |
| return new MapFactoryCreationExpression( |
| binding, generatedComponentModel, this, graph, elements); |
| |
| case DELEGATE: |
| return new DelegatingFrameworkInstanceCreationExpression( |
| binding, generatedComponentModel, this); |
| |
| case OPTIONAL: |
| return new OptionalFactoryInstanceCreationExpression( |
| optionalFactories, binding, generatedComponentModel, this); |
| |
| case MEMBERS_INJECTOR: |
| return new MembersInjectorProviderCreationExpression((ProvisionBinding) binding, this); |
| |
| default: |
| throw new AssertionError(binding); |
| } |
| } |
| |
| private InstanceFactoryCreationExpression instanceFactoryCreationExpression( |
| ContributionBinding binding, ComponentRequirement componentRequirement) { |
| return new InstanceFactoryCreationExpression( |
| binding.nullableType().isPresent(), |
| () -> |
| componentRequirementFields.getExpressionDuringInitialization( |
| componentRequirement, generatedComponentModel.name())); |
| } |
| |
| /** Returns a binding expression for a provision binding. */ |
| private BindingExpression provisionBindingExpression( |
| ResolvedBindings resolvedBindings, BindingRequest request) { |
| if (!request.requestKind().isPresent()) { |
| verify( |
| request.frameworkType().get().equals(FrameworkType.PRODUCER_NODE), |
| "expected a PRODUCER_NODE: %s", |
| request); |
| return producerFromProviderBindingExpression(resolvedBindings); |
| } |
| RequestKind requestKind = request.requestKind().get(); |
| switch (requestKind) { |
| case INSTANCE: |
| return instanceBindingExpression(resolvedBindings); |
| |
| case PROVIDER: |
| return providerBindingExpression(resolvedBindings); |
| |
| case LAZY: |
| case PRODUCED: |
| case PROVIDER_OF_LAZY: |
| return new DerivedFromFrameworkInstanceBindingExpression( |
| resolvedBindings, FrameworkType.PROVIDER, requestKind, this, types); |
| |
| case PRODUCER: |
| return producerFromProviderBindingExpression(resolvedBindings); |
| |
| case FUTURE: |
| return new ImmediateFutureBindingExpression(resolvedBindings, this, types); |
| |
| case MEMBERS_INJECTION: |
| throw new IllegalArgumentException(); |
| } |
| |
| throw new AssertionError(); |
| } |
| |
| /** Returns a binding expression for a production binding. */ |
| private BindingExpression productionBindingExpression( |
| ResolvedBindings resolvedBindings, BindingRequest request) { |
| if (request.frameworkType().isPresent()) { |
| return frameworkInstanceBindingExpression(resolvedBindings); |
| } else { |
| // If no FrameworkType is present, a RequestKind is guaranteed to be present. |
| return new DerivedFromFrameworkInstanceBindingExpression( |
| resolvedBindings, FrameworkType.PRODUCER_NODE, request.requestKind().get(), this, types); |
| } |
| } |
| |
| /** |
| * Returns a binding expression for {@link RequestKind#PROVIDER} requests. |
| * |
| * <p>{@code @Binds} bindings that don't {@linkplain #needsCaching(ResolvedBindings) need to be |
| * cached} can use a {@link DelegateBindingExpression}. |
| * |
| * <p>In fastInit mode, use an {@link InnerSwitchingProviders inner switching provider} unless |
| * that provider's case statement will simply call {@code get()} on another {@link Provider} (in |
| * which case, just use that Provider directly). |
| * |
| * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}. |
| */ |
| private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) { |
| if (resolvedBindings.contributionBinding().kind().equals(DELEGATE) |
| && !needsCaching(resolvedBindings)) { |
| return new DelegateBindingExpression( |
| resolvedBindings, RequestKind.PROVIDER, this, types, elements); |
| } else if (compilerOptions.fastInit() |
| && frameworkInstanceCreationExpression(resolvedBindings).useInnerSwitchingProvider() |
| && !(instanceBindingExpression(resolvedBindings) |
| instanceof DerivedFromFrameworkInstanceBindingExpression)) { |
| return wrapInMethod( |
| resolvedBindings, |
| bindingRequest(resolvedBindings.key(), RequestKind.PROVIDER), |
| innerSwitchingProviders.newBindingExpression(resolvedBindings.contributionBinding())); |
| } |
| return frameworkInstanceBindingExpression(resolvedBindings); |
| } |
| |
| /** |
| * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a |
| * provision binding. |
| */ |
| private FrameworkInstanceBindingExpression producerFromProviderBindingExpression( |
| ResolvedBindings resolvedBindings) { |
| checkArgument(resolvedBindings.bindingType().equals(BindingType.PROVISION)); |
| return new ProducerNodeInstanceBindingExpression( |
| resolvedBindings, |
| new FrameworkFieldInitializer( |
| generatedComponentModel, |
| resolvedBindings, |
| new ProducerFromProviderCreationExpression( |
| resolvedBindings.contributionBinding(), generatedComponentModel, this)), |
| types, |
| elements, |
| generatedComponentModel); |
| } |
| |
| /** |
| * Returns a binding expression for {@link RequestKind#INSTANCE} requests. |
| * |
| * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an |
| * instance of this binding, return it, wrapped in a method if the binding {@linkplain |
| * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies. |
| * |
| * <p>In fastInit mode, we can use direct expressions unless the binding needs to be cached. |
| */ |
| private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) { |
| Optional<BindingExpression> maybeDirectInstanceExpression = |
| unscopedDirectInstanceExpression(resolvedBindings); |
| if (canUseDirectInstanceExpression(resolvedBindings) |
| && maybeDirectInstanceExpression.isPresent()) { |
| BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get(); |
| return directInstanceExpression.requiresMethodEncapsulation() |
| || needsCaching(resolvedBindings) |
| ? wrapInMethod( |
| resolvedBindings, |
| bindingRequest(resolvedBindings.key(), RequestKind.INSTANCE), |
| directInstanceExpression) |
| : directInstanceExpression; |
| } |
| return new DerivedFromFrameworkInstanceBindingExpression( |
| resolvedBindings, FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types); |
| } |
| |
| /** |
| * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call |
| * {@code get()} on its provider, if there is one. |
| */ |
| private Optional<BindingExpression> unscopedDirectInstanceExpression( |
| ResolvedBindings resolvedBindings) { |
| switch (resolvedBindings.contributionBinding().kind()) { |
| case DELEGATE: |
| return Optional.of( |
| new DelegateBindingExpression( |
| resolvedBindings, RequestKind.INSTANCE, this, types, elements)); |
| |
| case COMPONENT: |
| return Optional.of( |
| new ComponentInstanceBindingExpression( |
| resolvedBindings, generatedComponentModel.name())); |
| |
| case COMPONENT_DEPENDENCY: |
| return Optional.of( |
| new ComponentRequirementBindingExpression( |
| resolvedBindings, |
| ComponentRequirement.forDependency(resolvedBindings.key().type()), |
| componentRequirementFields)); |
| |
| case COMPONENT_PROVISION: |
| return Optional.of( |
| new ComponentProvisionBindingExpression( |
| resolvedBindings, graph, componentRequirementFields, compilerOptions)); |
| |
| case SUBCOMPONENT_BUILDER: |
| return Optional.of( |
| new SubcomponentBuilderBindingExpression( |
| resolvedBindings, |
| generatedComponentModel.getSubcomponentName(resolvedBindings.key()))); |
| |
| case MULTIBOUND_SET: |
| return Optional.of( |
| new SetBindingExpression( |
| resolvedBindings, generatedComponentModel, graph, this, types, elements)); |
| |
| case MULTIBOUND_MAP: |
| return Optional.of( |
| new MapBindingExpression( |
| resolvedBindings, generatedComponentModel, graph, this, types, elements)); |
| |
| case OPTIONAL: |
| return Optional.of(new OptionalBindingExpression(resolvedBindings, this, types)); |
| |
| case BOUND_INSTANCE: |
| return Optional.of( |
| new ComponentRequirementBindingExpression( |
| resolvedBindings, |
| ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()), |
| componentRequirementFields)); |
| |
| case INJECTION: |
| case PROVISION: |
| return Optional.of( |
| new SimpleMethodBindingExpression( |
| resolvedBindings, |
| compilerOptions, |
| this, |
| membersInjectionMethods, |
| componentRequirementFields, |
| elements)); |
| |
| case MEMBERS_INJECTOR: |
| return Optional.empty(); |
| |
| case MEMBERS_INJECTION: |
| case COMPONENT_PRODUCTION: |
| case PRODUCTION: |
| throw new IllegalArgumentException( |
| resolvedBindings.contributionBinding().kind().toString()); |
| } |
| throw new AssertionError(); |
| } |
| |
| /** |
| * Returns {@code true} if the binding should use the static factory creation strategy. |
| * |
| * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we |
| * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading; |
| * however, we allow static factories that can reused across multiple bindings, e.g. {@code |
| * MapFactory} or {@code SetFactory}. |
| */ |
| private boolean useStaticFactoryCreation(ContributionBinding binding) { |
| return !(compilerOptions.experimentalAndroidMode2() || compilerOptions.fastInit()) |
| || binding.kind().equals(MULTIBOUND_MAP) |
| || binding.kind().equals(MULTIBOUND_SET); |
| } |
| |
| /** |
| * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this |
| * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached}, |
| * we can. |
| * |
| * <p>In fastInit mode, we can use a direct expression even if the binding {@linkplain |
| * #needsCaching(ResolvedBindings) needs to be cached}. |
| */ |
| private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) { |
| return !needsCaching(resolvedBindings) || compilerOptions.fastInit(); |
| } |
| |
| /** |
| * Returns a binding expression that uses a given one as the body of a method that users call. If |
| * a component provision method matches it, it will be the method implemented. If it does not |
| * match a component provision method and the binding is modifiable, then a new public modifiable |
| * binding method will be written. If the binding doesn't match a component method and is not |
| * modifiable, then a new private method will be written. |
| */ |
| BindingExpression wrapInMethod( |
| ResolvedBindings resolvedBindings, |
| BindingRequest request, |
| BindingExpression bindingExpression) { |
| // If we've already wrapped the expression, then use the delegate. |
| if (bindingExpression instanceof MethodBindingExpression) { |
| return bindingExpression; |
| } |
| |
| BindingMethodImplementation methodImplementation = |
| methodImplementation(resolvedBindings, request, bindingExpression); |
| Optional<ComponentMethodDescriptor> matchingComponentMethod = |
| graph.componentDescriptor().findMatchingComponentMethod(request); |
| Optional<ModifiableBindingMethod> matchingModifiableBindingMethod = |
| generatedComponentModel.getModifiableBindingMethod(request); |
| |
| Optional<BindingExpression> modifiableBindingExpression = |
| modifiableBindingExpressions.maybeWrapInModifiableMethodBindingExpression( |
| resolvedBindings, |
| request, |
| methodImplementation, |
| matchingComponentMethod, |
| matchingModifiableBindingMethod); |
| if (modifiableBindingExpression.isPresent()) { |
| return modifiableBindingExpression.get(); |
| } |
| |
| return matchingComponentMethod |
| .<BindingExpression>map( |
| componentMethod -> |
| new ComponentMethodBindingExpression( |
| methodImplementation, |
| generatedComponentModel, |
| componentMethod, |
| matchingModifiableBindingMethod)) |
| .orElseGet( |
| () -> |
| new PrivateMethodBindingExpression( |
| resolvedBindings, |
| request, |
| methodImplementation, |
| generatedComponentModel, |
| matchingModifiableBindingMethod)); |
| } |
| |
| private BindingMethodImplementation methodImplementation( |
| ResolvedBindings resolvedBindings, |
| BindingRequest request, |
| BindingExpression bindingExpression) { |
| if (compilerOptions.fastInit()) { |
| if (request.isRequestKind(RequestKind.PROVIDER)) { |
| return new SingleCheckedMethodImplementation( |
| resolvedBindings, request, bindingExpression, types, generatedComponentModel); |
| } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) { |
| return resolvedBindings.scope().get().isReusable() |
| ? new SingleCheckedMethodImplementation( |
| resolvedBindings, request, bindingExpression, types, generatedComponentModel) |
| : new DoubleCheckedMethodImplementation( |
| resolvedBindings, request, bindingExpression, types, generatedComponentModel); |
| } |
| } |
| |
| return new BindingMethodImplementation( |
| resolvedBindings, request, bindingExpression, generatedComponentModel.name(), types); |
| } |
| |
| /** |
| * Returns {@code true} if the component needs to make sure the provided value is cached. |
| * |
| * <p>The component needs to cache the value for scoped bindings except for {@code @Binds} |
| * bindings whose scope is no stronger than their delegate's. |
| */ |
| private boolean needsCaching(ResolvedBindings resolvedBindings) { |
| if (!resolvedBindings.scope().isPresent()) { |
| return false; |
| } |
| if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) { |
| return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph); |
| } |
| return true; |
| } |
| } |