• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.auto.common.MoreElements.asExecutable;
20 import static com.google.common.base.Preconditions.checkArgument;
21 import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
22 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
23 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
24 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
25 
26 import com.google.auto.common.MoreTypes;
27 import com.google.common.collect.ImmutableMap;
28 import com.google.common.collect.Maps;
29 import com.squareup.javapoet.ClassName;
30 import com.squareup.javapoet.CodeBlock;
31 import com.squareup.javapoet.MethodSpec;
32 import com.squareup.javapoet.TypeName;
33 import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
34 import dagger.internal.codegen.javapoet.Expression;
35 import dagger.internal.codegen.langmodel.DaggerElements;
36 import dagger.internal.codegen.langmodel.DaggerTypes;
37 import dagger.model.DependencyRequest;
38 import java.util.Optional;
39 import java.util.function.Function;
40 import javax.lang.model.SourceVersion;
41 import javax.lang.model.element.Element;
42 import javax.lang.model.element.ExecutableElement;
43 import javax.lang.model.type.DeclaredType;
44 import javax.lang.model.type.TypeMirror;
45 
46 /**
47  * A binding expression that invokes methods or constructors directly (without attempting to scope)
48  * {@link dagger.model.RequestKind#INSTANCE} requests.
49  */
50 final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression {
51   private final CompilerOptions compilerOptions;
52   private final ProvisionBinding provisionBinding;
53   private final ComponentBindingExpressions componentBindingExpressions;
54   private final MembersInjectionMethods membersInjectionMethods;
55   private final ComponentRequirementExpressions componentRequirementExpressions;
56   private final DaggerTypes types;
57   private final DaggerElements elements;
58   private final SourceVersion sourceVersion;
59 
SimpleMethodBindingExpression( ResolvedBindings resolvedBindings, CompilerOptions compilerOptions, ComponentBindingExpressions componentBindingExpressions, MembersInjectionMethods membersInjectionMethods, ComponentRequirementExpressions componentRequirementExpressions, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion)60   SimpleMethodBindingExpression(
61       ResolvedBindings resolvedBindings,
62       CompilerOptions compilerOptions,
63       ComponentBindingExpressions componentBindingExpressions,
64       MembersInjectionMethods membersInjectionMethods,
65       ComponentRequirementExpressions componentRequirementExpressions,
66       DaggerTypes types,
67       DaggerElements elements,
68       SourceVersion sourceVersion) {
69     super(resolvedBindings);
70     this.compilerOptions = compilerOptions;
71     this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
72     checkArgument(
73         provisionBinding.implicitDependencies().isEmpty(),
74         "framework deps are not currently supported");
75     checkArgument(provisionBinding.bindingElement().isPresent());
76     this.componentBindingExpressions = componentBindingExpressions;
77     this.membersInjectionMethods = membersInjectionMethods;
78     this.componentRequirementExpressions = componentRequirementExpressions;
79     this.types = types;
80     this.elements = elements;
81     this.sourceVersion = sourceVersion;
82   }
83 
84   @Override
getDependencyExpression(ClassName requestingClass)85   Expression getDependencyExpression(ClassName requestingClass) {
86     ImmutableMap<DependencyRequest, Expression> arguments =
87         ImmutableMap.copyOf(
88             Maps.asMap(
89                 provisionBinding.dependencies(),
90                 request -> dependencyArgument(request, requestingClass)));
91     Function<DependencyRequest, CodeBlock> argumentsFunction =
92         request -> arguments.get(request).codeBlock();
93     return requiresInjectionMethod(
94             provisionBinding,
95             arguments.values().asList(),
96             compilerOptions,
97             requestingClass.packageName(),
98             types)
99         ? invokeInjectionMethod(argumentsFunction, requestingClass)
100         : invokeMethod(argumentsFunction, requestingClass);
101   }
102 
invokeMethod( Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass)103   private Expression invokeMethod(
104       Function<DependencyRequest, CodeBlock> argumentsFunction,
105       ClassName requestingClass) {
106     // TODO(dpb): align this with the contents of InlineMethods.create
107     CodeBlock arguments =
108         provisionBinding.dependencies().stream()
109             .map(argumentsFunction)
110             .collect(toParametersCodeBlock());
111     ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
112     CodeBlock invocation;
113     switch (method.getKind()) {
114       case CONSTRUCTOR:
115         invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
116         break;
117       case METHOD:
118         CodeBlock module =
119             moduleReference(requestingClass)
120                 .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get()));
121         invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments);
122         break;
123       default:
124         throw new IllegalStateException();
125     }
126 
127     return Expression.create(simpleMethodReturnType(), invocation);
128   }
129 
constructorTypeName(ClassName requestingClass)130   private TypeName constructorTypeName(ClassName requestingClass) {
131     DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
132     TypeName typeName = TypeName.get(type);
133     if (type.getTypeArguments()
134         .stream()
135         .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
136       return typeName;
137     }
138     return rawTypeName(typeName);
139   }
140 
invokeInjectionMethod( Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass)141   private Expression invokeInjectionMethod(
142       Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) {
143     return injectMembers(
144         ProvisionMethod.invoke(
145             provisionBinding,
146             argumentsFunction,
147             requestingClass,
148             moduleReference(requestingClass),
149             compilerOptions,
150             elements));
151   }
152 
dependencyArgument(DependencyRequest dependency, ClassName requestingClass)153   private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
154     return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass);
155   }
156 
injectMembers(CodeBlock instance)157   private Expression injectMembers(CodeBlock instance) {
158     if (provisionBinding.injectionSites().isEmpty()) {
159       return Expression.create(simpleMethodReturnType(), instance);
160     }
161     if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
162       // Java 7 type inference can't figure out that instance in
163       // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
164       // Parameterized<Object>
165       if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
166         TypeName keyType = TypeName.get(provisionBinding.key().type());
167         instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
168       }
169     }
170     MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key());
171     TypeMirror returnType =
172         membersInjectionMethod.returnType.equals(TypeName.OBJECT)
173             ? elements.getTypeElement(Object.class).asType()
174             : provisionBinding.key().type();
175     return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance));
176   }
177 
moduleReference(ClassName requestingClass)178   private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
179     return provisionBinding.requiresModuleInstance()
180         ? provisionBinding
181             .contributingModule()
182             .map(Element::asType)
183             .map(ComponentRequirement::forModule)
184             .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
185         : Optional.empty();
186   }
187 
simpleMethodReturnType()188   private TypeMirror simpleMethodReturnType() {
189     return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type());
190   }
191 }
192