| /* |
| * 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.BindingRequest.bindingRequest; |
| import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION; |
| import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope; |
| import static dagger.internal.codegen.MemberSelect.staticFactoryCreation; |
| import static dagger.internal.codegen.RequestKinds.isDerivedFromProvider; |
| import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock; |
| import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK; |
| import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK; |
| import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible; |
| import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible; |
| import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; |
| import static dagger.model.BindingKind.DELEGATE; |
| import static dagger.model.BindingKind.MULTIBOUND_MAP; |
| import static dagger.model.BindingKind.MULTIBOUND_SET; |
| import static javax.lang.model.element.Modifier.ABSTRACT; |
| |
| 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.MethodBindingExpression.MethodImplementationStrategy; |
| import dagger.internal.codegen.javapoet.Expression; |
| import dagger.internal.codegen.langmodel.DaggerElements; |
| import dagger.internal.codegen.langmodel.DaggerTypes; |
| import dagger.model.DependencyRequest; |
| import dagger.model.RequestKind; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Optional; |
| import javax.inject.Inject; |
| import javax.inject.Provider; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.type.TypeMirror; |
| |
| /** A central repository of code expressions used to access any binding available to a component. */ |
| @PerComponentImplementation |
| final class ComponentBindingExpressions { |
| // TODO(dpb,ronshapiro): refactor this and ComponentRequirementExpressions 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 ComponentImplementation componentImplementation; |
| private final ComponentRequirementExpressions componentRequirementExpressions; |
| private final OptionalFactories optionalFactories; |
| private final DaggerTypes types; |
| private final DaggerElements elements; |
| private final SourceVersion sourceVersion; |
| private final CompilerOptions compilerOptions; |
| private final MembersInjectionMethods membersInjectionMethods; |
| private final InnerSwitchingProviders innerSwitchingProviders; |
| private final ModifiableBindingExpressions modifiableBindingExpressions; |
| private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>(); |
| |
| @Inject |
| ComponentBindingExpressions( |
| @ParentComponent Optional<ComponentBindingExpressions> parent, |
| BindingGraph graph, |
| ComponentImplementation componentImplementation, |
| ComponentRequirementExpressions componentRequirementExpressions, |
| OptionalFactories optionalFactories, |
| DaggerTypes types, |
| DaggerElements elements, |
| SourceVersion sourceVersion, |
| @GenerationCompilerOptions CompilerOptions compilerOptions) { |
| this.parent = parent; |
| this.graph = graph; |
| this.componentImplementation = componentImplementation; |
| this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions); |
| this.optionalFactories = checkNotNull(optionalFactories); |
| this.types = checkNotNull(types); |
| this.elements = checkNotNull(elements); |
| this.sourceVersion = checkNotNull(sourceVersion); |
| this.compilerOptions = checkNotNull(compilerOptions); |
| this.membersInjectionMethods = |
| new MembersInjectionMethods(componentImplementation, this, graph, elements, types); |
| this.innerSwitchingProviders = |
| new InnerSwitchingProviders(componentImplementation, this, types); |
| this.modifiableBindingExpressions = |
| new ModifiableBindingExpressions( |
| parent.map(cbe -> cbe.modifiableBindingExpressions), |
| this, |
| graph, |
| componentImplementation, |
| compilerOptions, |
| types); |
| } |
| |
| /* 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, |
| ComponentImplementation componentImplementation) { |
| return getBindingExpression(request) |
| .getDependencyExpressionForComponentMethod(componentMethod, componentImplementation); |
| } |
| |
| /** |
| * 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( |
| componentRequirementExpressions.getExpressionDuringInitialization( |
| ComponentRequirement.forModule(binding.contributingModule().get().asType()), |
| componentImplementation.name())); |
| } |
| |
| binding.frameworkDependencies().stream() |
| .map(BindingRequest::bindingRequest) |
| .map(request -> getDependencyExpression(request, componentImplementation.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(); |
| BindingRequest bindingRequest = bindingRequest(dependencyRequest); |
| Expression dependencyExpression = getDependencyExpression(bindingRequest, requestingClass); |
| |
| if (compilerOptions.aheadOfTimeSubcomponents()) { |
| TypeMirror requestedType = |
| bindingRequest.requestedType(dependencyRequest.key().type(), types); |
| // If dependencyExpression.type() has been erased to it's publicly accessible type in AOT, |
| // we must sometimes cast the expression so that it is usable in the current component. To do |
| // so, we check that without the cast the assignment would fail, that argument to this proxy |
| // method erased the type, and that the raw type of the requested type is actually accessible |
| // in the current class so that the cast is valid. |
| if (!types.isAssignable(dependencyExpression.type(), requestedType) |
| && !isRawTypePubliclyAccessible(requestedType) |
| && isRawTypeAccessible(requestedType, requestingClass.packageName())) { |
| return dependencyExpression.castTo(types.erasure(requestedType)); |
| } |
| } |
| |
| 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()); |
| MethodSpec.Builder method = |
| MethodSpec.overriding( |
| componentMethod.methodElement(), |
| MoreTypes.asDeclared(graph.componentTypeElement().asType()), |
| types); |
| // Even though this is not used if the method is abstract, we need to invoke the binding |
| // expression in order for the side of effect of the method being added to the |
| // ComponentImplementation |
| CodeBlock methodBody = |
| getBindingExpression(request) |
| .getComponentMethodImplementation(componentMethod, componentImplementation); |
| if (!componentImplementation.superclassImplementation().isPresent() |
| && !modifiableBindingExpressions |
| .getModifiableBindingType(request) |
| .hasBaseClassImplementation() |
| && !componentImplementation.getModifiableBindingMethod(request).isPresent()) { |
| return method.addModifiers(ABSTRACT).build(); |
| } |
| return method.addCode(methodBody).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.bindingsOwnedBy(graph.componentDescriptor()).isEmpty()) { |
| expression = Optional.of(createBindingExpression(resolvedBindings, request)); |
| } |
| } |
| if (!expression.isPresent() |
| && compilerOptions.aheadOfTimeSubcomponents() |
| && request.requestKind().isPresent() |
| && isDerivedFromProvider(request.requestKind().get())) { |
| RequestKind requestKind = request.requestKind().get(); |
| expression = |
| Optional.of( |
| new DerivedFromFrameworkInstanceBindingExpression( |
| request.key(), FrameworkType.PROVIDER, requestKind, this, types)); |
| } |
| |
| 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); |
| } |
| 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 BindingExpression 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( |
| componentImplementation, resolvedBindings, frameworkInstanceCreationExpression); |
| |
| switch (resolvedBindings.bindingType()) { |
| case PROVISION: |
| return new ProviderInstanceBindingExpression( |
| resolvedBindings, frameworkInstanceSupplier, types, elements); |
| case PRODUCTION: |
| return new ProducerNodeInstanceBindingExpression( |
| resolvedBindings, frameworkInstanceSupplier, types, elements, componentImplementation); |
| 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, |
| componentImplementation, |
| componentRequirementExpressions, |
| compilerOptions, |
| graph); |
| |
| case SUBCOMPONENT_CREATOR: |
| return new AnonymousProviderCreationExpression( |
| binding, this, componentImplementation.name()); |
| |
| case INJECTION: |
| case PROVISION: |
| return new InjectionOrProvisionProviderCreationExpression(binding, this); |
| |
| case COMPONENT_PRODUCTION: |
| return new DependencyMethodProducerCreationExpression( |
| binding, componentImplementation, componentRequirementExpressions, graph); |
| |
| case PRODUCTION: |
| return new ProducerCreationExpression(binding, this); |
| |
| case MULTIBOUND_SET: |
| return new SetFactoryCreationExpression(binding, componentImplementation, this, graph); |
| |
| case MULTIBOUND_MAP: |
| return new MapFactoryCreationExpression( |
| binding, componentImplementation, this, graph, elements); |
| |
| case DELEGATE: |
| return new DelegatingFrameworkInstanceCreationExpression( |
| binding, componentImplementation, this); |
| |
| case OPTIONAL: |
| return new OptionalFactoryInstanceCreationExpression( |
| optionalFactories, binding, componentImplementation, 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(), |
| () -> |
| componentRequirementExpressions.getExpressionDuringInitialization( |
| componentRequirement, componentImplementation.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.key(), FrameworkType.PROVIDER, requestKind, this, types); |
| |
| case PRODUCER: |
| return producerFromProviderBindingExpression(resolvedBindings); |
| |
| case FUTURE: |
| return new ImmediateFutureBindingExpression(resolvedBindings, this, types, sourceVersion); |
| |
| 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. |
| RequestKind requestKind = request.requestKind().get(); |
| return new DerivedFromFrameworkInstanceBindingExpression( |
| resolvedBindings.key(), FrameworkType.PRODUCER_NODE, requestKind, 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( |
| componentImplementation, |
| resolvedBindings, |
| new ProducerFromProviderCreationExpression( |
| resolvedBindings.contributionBinding(), componentImplementation, this)), |
| types, |
| elements, |
| componentImplementation); |
| } |
| |
| /** |
| * 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.key(), 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, componentImplementation.name())); |
| |
| case COMPONENT_DEPENDENCY: |
| return Optional.of( |
| new ComponentRequirementBindingExpression( |
| resolvedBindings, |
| ComponentRequirement.forDependency(resolvedBindings.key().type()), |
| componentRequirementExpressions)); |
| |
| case COMPONENT_PROVISION: |
| return Optional.of( |
| new ComponentProvisionBindingExpression( |
| resolvedBindings, graph, componentRequirementExpressions, compilerOptions)); |
| |
| case SUBCOMPONENT_CREATOR: |
| return Optional.of( |
| new SubcomponentCreatorBindingExpression( |
| resolvedBindings, |
| componentImplementation.getSubcomponentCreatorSimpleName(resolvedBindings.key()))); |
| |
| case MULTIBOUND_SET: |
| return Optional.of( |
| new SetBindingExpression( |
| resolvedBindings, componentImplementation, graph, this, types, elements)); |
| |
| case MULTIBOUND_MAP: |
| return Optional.of( |
| new MapBindingExpression( |
| resolvedBindings, componentImplementation, graph, this, types, elements)); |
| |
| case OPTIONAL: |
| return Optional.of( |
| new OptionalBindingExpression(resolvedBindings, this, types, sourceVersion)); |
| |
| case BOUND_INSTANCE: |
| return Optional.of( |
| new ComponentRequirementBindingExpression( |
| resolvedBindings, |
| ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()), |
| componentRequirementExpressions)); |
| |
| case INJECTION: |
| case PROVISION: |
| return Optional.of( |
| new SimpleMethodBindingExpression( |
| resolvedBindings, |
| compilerOptions, |
| this, |
| membersInjectionMethods, |
| componentRequirementExpressions, |
| types, |
| elements, |
| sourceVersion)); |
| |
| 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.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; |
| } |
| |
| MethodImplementationStrategy methodImplementationStrategy = |
| methodImplementationStrategy(resolvedBindings, request); |
| Optional<ComponentMethodDescriptor> matchingComponentMethod = |
| graph.componentDescriptor().firstMatchingComponentMethod(request); |
| |
| if (modifiableBindingExpressions.getModifiableBindingType(request).isModifiable() |
| && (componentImplementation.superclassImplementation().isPresent() |
| || !matchingComponentMethod.isPresent())) { |
| return modifiableBindingExpressions.wrapInModifiableMethodBindingExpression( |
| request, resolvedBindings, methodImplementationStrategy, bindingExpression); |
| } else if (matchingComponentMethod.isPresent()) { |
| ComponentMethodDescriptor componentMethod = matchingComponentMethod.get(); |
| return new ComponentMethodBindingExpression( |
| request, |
| resolvedBindings, |
| methodImplementationStrategy, |
| bindingExpression, |
| componentImplementation, |
| componentMethod, |
| types); |
| } else { |
| return new PrivateMethodBindingExpression( |
| request, |
| resolvedBindings, |
| methodImplementationStrategy, |
| bindingExpression, |
| componentImplementation, |
| types); |
| } |
| } |
| |
| private MethodImplementationStrategy methodImplementationStrategy( |
| ResolvedBindings resolvedBindings, BindingRequest request) { |
| if (compilerOptions.fastInit()) { |
| if (request.isRequestKind(RequestKind.PROVIDER)) { |
| return MethodImplementationStrategy.SINGLE_CHECK; |
| } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) { |
| return resolvedBindings.scope().get().isReusable() |
| ? MethodImplementationStrategy.SINGLE_CHECK |
| : MethodImplementationStrategy.DOUBLE_CHECK; |
| } |
| } |
| return MethodImplementationStrategy.SIMPLE; |
| } |
| |
| /** |
| * 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; |
| } |
| } |