260 lines
12 KiB
Java
260 lines
12 KiB
Java
/*
|
|
* 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.writing;
|
|
|
|
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 dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
|
|
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
|
|
import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
|
|
import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
|
|
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
|
|
|
|
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.binding.Binding;
|
|
import dagger.internal.codegen.binding.BindingGraph;
|
|
import dagger.internal.codegen.binding.BindingRequest;
|
|
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
|
|
import dagger.internal.codegen.binding.ComponentRequirement;
|
|
import dagger.internal.codegen.binding.ContributionBinding;
|
|
import dagger.internal.codegen.binding.FrameworkType;
|
|
import dagger.internal.codegen.binding.FrameworkTypeMapper;
|
|
import dagger.internal.codegen.binding.MembersInjectionBinding;
|
|
import dagger.internal.codegen.binding.ProductionBinding;
|
|
import dagger.internal.codegen.binding.ProvisionBinding;
|
|
import dagger.internal.codegen.javapoet.Expression;
|
|
import dagger.internal.codegen.langmodel.DaggerTypes;
|
|
import dagger.internal.codegen.xprocessing.MethodSpecs;
|
|
import dagger.spi.model.DependencyRequest;
|
|
import dagger.spi.model.RequestKind;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import javax.inject.Inject;
|
|
import javax.lang.model.type.TypeMirror;
|
|
|
|
/** A central repository of code expressions used to access any binding available to a component. */
|
|
@PerComponentImplementation
|
|
public final class ComponentRequestRepresentations {
|
|
// 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 RequestRepresentation.Factory create it.
|
|
|
|
private final Optional<ComponentRequestRepresentations> parent;
|
|
private final BindingGraph graph;
|
|
private final ComponentImplementation componentImplementation;
|
|
private final ComponentRequirementExpressions componentRequirementExpressions;
|
|
private final MembersInjectionBindingRepresentation.Factory
|
|
membersInjectionBindingRepresentationFactory;
|
|
private final ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory;
|
|
private final ProductionBindingRepresentation.Factory productionBindingRepresentationFactory;
|
|
private final ExperimentalSwitchingProviderDependencyRepresentation.Factory
|
|
experimentalSwitchingProviderDependencyRepresentationFactory;
|
|
private final DaggerTypes types;
|
|
private final Map<Binding, BindingRepresentation> representations = new HashMap<>();
|
|
private final Map<Binding, ExperimentalSwitchingProviderDependencyRepresentation>
|
|
experimentalSwitchingProviderDependencyRepresentations = new HashMap<>();
|
|
|
|
@Inject
|
|
ComponentRequestRepresentations(
|
|
@ParentComponent Optional<ComponentRequestRepresentations> parent,
|
|
BindingGraph graph,
|
|
ComponentImplementation componentImplementation,
|
|
ComponentRequirementExpressions componentRequirementExpressions,
|
|
MembersInjectionBindingRepresentation.Factory membersInjectionBindingRepresentationFactory,
|
|
ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory,
|
|
ProductionBindingRepresentation.Factory productionBindingRepresentationFactory,
|
|
ExperimentalSwitchingProviderDependencyRepresentation.Factory
|
|
experimentalSwitchingProviderDependencyRepresentationFactory,
|
|
DaggerTypes types) {
|
|
this.parent = parent;
|
|
this.graph = graph;
|
|
this.componentImplementation = componentImplementation;
|
|
this.membersInjectionBindingRepresentationFactory =
|
|
membersInjectionBindingRepresentationFactory;
|
|
this.provisionBindingRepresentationFactory = provisionBindingRepresentationFactory;
|
|
this.productionBindingRepresentationFactory = productionBindingRepresentationFactory;
|
|
this.experimentalSwitchingProviderDependencyRepresentationFactory =
|
|
experimentalSwitchingProviderDependencyRepresentationFactory;
|
|
this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
|
|
this.types = types;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) {
|
|
return getRequestRepresentation(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 getRequestRepresentation(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, ClassName requestingClass) {
|
|
return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding, requestingClass));
|
|
}
|
|
|
|
private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(
|
|
ContributionBinding binding, ClassName requestingClass) {
|
|
ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
|
|
|
|
if (binding.requiresModuleInstance()) {
|
|
arguments.add(
|
|
componentRequirementExpressions.getExpressionDuringInitialization(
|
|
ComponentRequirement.forModule(binding.contributingModule().get().getType()),
|
|
requestingClass));
|
|
}
|
|
|
|
binding.dependencies().stream()
|
|
.map(dependency -> frameworkRequest(binding, dependency))
|
|
.map(request -> getDependencyExpression(request, requestingClass))
|
|
.map(Expression::codeBlock)
|
|
.forEach(arguments::add);
|
|
|
|
return arguments.build();
|
|
}
|
|
|
|
private static BindingRequest frameworkRequest(
|
|
ContributionBinding binding, DependencyRequest dependency) {
|
|
// TODO(bcorso): See if we can get rid of FrameworkTypeMatcher
|
|
FrameworkType frameworkType =
|
|
FrameworkTypeMapper.forBindingType(binding.bindingType())
|
|
.getFrameworkType(dependency.kind());
|
|
return BindingRequest.bindingRequest(dependency.key(), frameworkType);
|
|
}
|
|
|
|
/**
|
|
* 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().java();
|
|
BindingRequest bindingRequest = bindingRequest(dependencyRequest);
|
|
Expression dependencyExpression = getDependencyExpression(bindingRequest, 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. */
|
|
public MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) {
|
|
checkArgument(componentMethod.dependencyRequest().isPresent());
|
|
BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
|
|
return MethodSpecs.overriding(
|
|
componentMethod.methodElement(), graph.componentTypeElement().getType())
|
|
.addCode(
|
|
getRequestRepresentation(request)
|
|
.getComponentMethodImplementation(componentMethod, componentImplementation))
|
|
.build();
|
|
}
|
|
|
|
/** Returns the {@link RequestRepresentation} for the given {@link BindingRequest}. */
|
|
RequestRepresentation getRequestRepresentation(BindingRequest request) {
|
|
Optional<Binding> localBinding =
|
|
request.isRequestKind(RequestKind.MEMBERS_INJECTION)
|
|
? graph.localMembersInjectionBinding(request.key())
|
|
: graph.localContributionBinding(request.key());
|
|
|
|
if (localBinding.isPresent()) {
|
|
return getBindingRepresentation(localBinding.get()).getRequestRepresentation(request);
|
|
}
|
|
|
|
checkArgument(parent.isPresent(), "no expression found for %s", request);
|
|
return parent.get().getRequestRepresentation(request);
|
|
}
|
|
|
|
private BindingRepresentation getBindingRepresentation(Binding binding) {
|
|
return reentrantComputeIfAbsent(
|
|
representations, binding, this::getBindingRepresentationUncached);
|
|
}
|
|
|
|
private BindingRepresentation getBindingRepresentationUncached(Binding binding) {
|
|
switch (binding.bindingType()) {
|
|
case MEMBERS_INJECTION:
|
|
return membersInjectionBindingRepresentationFactory.create(
|
|
(MembersInjectionBinding) binding);
|
|
case PROVISION:
|
|
return provisionBindingRepresentationFactory.create((ProvisionBinding) binding);
|
|
case PRODUCTION:
|
|
return productionBindingRepresentationFactory.create((ProductionBinding) binding);
|
|
}
|
|
throw new AssertionError();
|
|
}
|
|
|
|
/**
|
|
* Returns an {@link ExperimentalSwitchingProviderDependencyRepresentation} for the requested
|
|
* binding to satisfy dependency requests on it from experimental switching providers. Cannot be
|
|
* used for Members Injection requests.
|
|
*/
|
|
ExperimentalSwitchingProviderDependencyRepresentation
|
|
getExperimentalSwitchingProviderDependencyRepresentation(BindingRequest request) {
|
|
checkState(
|
|
componentImplementation.compilerMode().isExperimentalMergedMode(),
|
|
"Compiler mode should be experimentalMergedMode!");
|
|
Optional<Binding> localBinding = graph.localContributionBinding(request.key());
|
|
|
|
if (localBinding.isPresent()) {
|
|
return reentrantComputeIfAbsent(
|
|
experimentalSwitchingProviderDependencyRepresentations,
|
|
localBinding.get(),
|
|
binding ->
|
|
experimentalSwitchingProviderDependencyRepresentationFactory.create(
|
|
(ProvisionBinding) binding));
|
|
}
|
|
|
|
checkArgument(parent.isPresent(), "no expression found for %s", request);
|
|
return parent.get().getExperimentalSwitchingProviderDependencyRepresentation(request);
|
|
}
|
|
}
|