blob: 720da39e70624f2bb983ddd0dad4e4f466bb9075 [file] [log] [blame]
erichang9dd6aed2017-06-15 15:54:00 -07001/*
2 * Copyright (C) 2017 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
17package dagger.internal.codegen;
18
erichang9dd6aed2017-06-15 15:54:00 -070019import static com.google.common.base.Preconditions.checkNotNull;
dpba6ff6c12017-08-14 08:47:36 -070020import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
21import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
erichang43774172017-08-04 10:42:07 -070022import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
erichang9dd6aed2017-06-15 15:54:00 -070023import static dagger.internal.codegen.MemberSelect.staticMemberSelect;
erichang43774172017-08-04 10:42:07 -070024import static dagger.internal.codegen.TypeNames.PRODUCER;
dpba6ff6c12017-08-14 08:47:36 -070025import static dagger.internal.codegen.TypeNames.rawTypeName;
erichang43774172017-08-04 10:42:07 -070026import static javax.lang.model.element.Modifier.PRIVATE;
erichang9dd6aed2017-06-15 15:54:00 -070027
erichang9dd6aed2017-06-15 15:54:00 -070028import com.google.common.collect.ImmutableMap;
29import com.squareup.javapoet.ClassName;
30import com.squareup.javapoet.CodeBlock;
31import com.squareup.javapoet.FieldSpec;
dpba6ff6c12017-08-14 08:47:36 -070032import com.squareup.javapoet.TypeName;
erichang9dd6aed2017-06-15 15:54:00 -070033import java.util.Optional;
dpba6ff6c12017-08-14 08:47:36 -070034import javax.lang.model.type.TypeMirror;
ronshapirocb725da2017-07-04 15:14:48 -070035import javax.lang.model.util.Elements;
erichang9dd6aed2017-06-15 15:54:00 -070036
37/** The code expressions to declare, initialize, and/or access a binding in a component. */
ronshapiro186d17a2017-08-11 08:18:07 -070038abstract class BindingExpression {
39 private final BindingKey bindingKey;
erichang9dd6aed2017-06-15 15:54:00 -070040
ronshapiro186d17a2017-08-11 08:18:07 -070041 BindingExpression(BindingKey bindingKey) {
42 this.bindingKey = checkNotNull(bindingKey);
dpb02dd23f2017-07-27 11:13:11 -070043 }
44
ronshapiro186d17a2017-08-11 08:18:07 -070045 /** The key for which this instance can fulfill requests. */
46 final BindingKey bindingKey() {
47 return bindingKey;
dpb02dd23f2017-07-27 11:13:11 -070048 }
49
50 /**
dpba6ff6c12017-08-14 08:47:36 -070051 * Returns an expression that evaluates to the value of a dependency request.
52 *
53 * @param requestingClass the class that will contain the expression
dpb02dd23f2017-07-27 11:13:11 -070054 */
dpba6ff6c12017-08-14 08:47:36 -070055 abstract CodeBlock getDependencyExpression(DependencyRequest request, ClassName requestingClass);
dpb02dd23f2017-07-27 11:13:11 -070056
57 /**
dpba6ff6c12017-08-14 08:47:36 -070058 * Returns an expression that evaluates to the value of a framework dependency.
59 *
60 * @param requestingClass the class that will contain the expression
dpb02dd23f2017-07-27 11:13:11 -070061 */
dpba6ff6c12017-08-14 08:47:36 -070062 abstract CodeBlock getDependencyExpression(
ronshapiro186d17a2017-08-11 08:18:07 -070063 FrameworkDependency frameworkDependency, ClassName requestingClass);
erichang9dd6aed2017-06-15 15:54:00 -070064
dpba6ff6c12017-08-14 08:47:36 -070065 /**
66 * Returns an expression that evaluates to the value of a dependency request, for passing to a
67 * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one.
68 *
69 * <p>If the method is a generated static {@link InjectionMethods injection method}, each
70 * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the
71 * case for this dependency, the returned expression will use a cast to evaluate to the raw type.
72 *
73 * @param requestingClass the class that will contain the expression
74 */
75 // TODO(b/64024402) Merge with getDependencyExpression(DependencyRequest, ClassName) if possible.
76 CodeBlock getDependencyArgumentExpression(
77 DependencyRequest dependencyRequest, ClassName requestingClass) {
78 CodeBlock.Builder argument = CodeBlock.builder();
79
80 TypeMirror dependencyType = dependencyRequest.key().type();
81 if (!isTypeAccessibleFrom(dependencyType, requestingClass.packageName())
82 && isRawTypeAccessible(dependencyType, requestingClass.packageName())) {
83 argument.add("($T) ", rawTypeName(TypeName.get(dependencyType)));
84 }
85
86 argument.add(getDependencyExpression(dependencyRequest, requestingClass));
87 return argument.build();
88 }
89
erichang9dd6aed2017-06-15 15:54:00 -070090 /** Factory for building a {@link BindingExpression}. */
91 static final class Factory {
dpbc8c6c052017-07-28 10:48:06 -070092 private final CompilerOptions compilerOptions;
erichang9dd6aed2017-06-15 15:54:00 -070093 private final ClassName componentName;
erichang43774172017-08-04 10:42:07 -070094 private final UniqueNameSet componentFieldNames;
erichang9dd6aed2017-06-15 15:54:00 -070095 private final HasBindingExpressions hasBindingExpressions;
96 private final ImmutableMap<BindingKey, String> subcomponentNames;
97 private final BindingGraph graph;
ronshapirocb725da2017-07-04 15:14:48 -070098 private final Elements elements;
erichang9dd6aed2017-06-15 15:54:00 -070099
100 Factory(
dpbc8c6c052017-07-28 10:48:06 -0700101 CompilerOptions compilerOptions,
erichang9dd6aed2017-06-15 15:54:00 -0700102 ClassName componentName,
erichang43774172017-08-04 10:42:07 -0700103 UniqueNameSet componentFieldNames,
erichang9dd6aed2017-06-15 15:54:00 -0700104 HasBindingExpressions hasBindingExpressions,
105 ImmutableMap<BindingKey, String> subcomponentNames,
ronshapirocb725da2017-07-04 15:14:48 -0700106 BindingGraph graph,
107 Elements elements) {
dpbc8c6c052017-07-28 10:48:06 -0700108 this.compilerOptions = checkNotNull(compilerOptions);
erichang9dd6aed2017-06-15 15:54:00 -0700109 this.componentName = checkNotNull(componentName);
erichang43774172017-08-04 10:42:07 -0700110 this.componentFieldNames = checkNotNull(componentFieldNames);
erichang9dd6aed2017-06-15 15:54:00 -0700111 this.hasBindingExpressions = checkNotNull(hasBindingExpressions);
112 this.subcomponentNames = checkNotNull(subcomponentNames);
113 this.graph = checkNotNull(graph);
ronshapiroe05f9212017-08-08 11:22:11 -0700114 this.elements = checkNotNull(elements);
erichang9dd6aed2017-06-15 15:54:00 -0700115 }
116
117 /** Creates a binding expression for a field. */
erichang43774172017-08-04 10:42:07 -0700118 BindingExpression forField(ResolvedBindings resolvedBindings) {
119 FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.empty());
erichang9dd6aed2017-06-15 15:54:00 -0700120 MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name);
ronshapiro186d17a2017-08-11 08:18:07 -0700121 return create(resolvedBindings, Optional.of(fieldSpec), memberSelect);
erichang43774172017-08-04 10:42:07 -0700122 }
123
124 BindingExpression forProducerFromProviderField(ResolvedBindings resolvedBindings) {
125 FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.of(PRODUCER));
126 MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name);
ronshapiro186d17a2017-08-11 08:18:07 -0700127 return new ProducerBindingExpression(
128 resolvedBindings.bindingKey(),
erichang43774172017-08-04 10:42:07 -0700129 Optional.of(fieldSpec),
130 hasBindingExpressions,
ronshapiro186d17a2017-08-11 08:18:07 -0700131 memberSelect,
erichang43774172017-08-04 10:42:07 -0700132 true);
erichang9dd6aed2017-06-15 15:54:00 -0700133 }
134
ronshapiro186d17a2017-08-11 08:18:07 -0700135 /**
136 * Creates a binding expression for a static method call.
137 */
erichang9dd6aed2017-06-15 15:54:00 -0700138 Optional<BindingExpression> forStaticMethod(ResolvedBindings resolvedBindings) {
ronshapiro186d17a2017-08-11 08:18:07 -0700139 return staticMemberSelect(resolvedBindings)
140 .map(memberSelect -> create(resolvedBindings, Optional.empty(), memberSelect));
erichang43774172017-08-04 10:42:07 -0700141 }
142
143 /**
144 * Adds a field representing the resolved bindings, optionally forcing it to use a particular
145 * binding type (instead of the type the resolved bindings would typically use).
146 */
147 private FieldSpec generateFrameworkField(
148 ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) {
149 boolean useRawType = useRawType(resolvedBindings);
150
151 FrameworkField contributionBindingField =
152 FrameworkField.forResolvedBindings(resolvedBindings, frameworkClass);
153 FieldSpec.Builder contributionField =
154 FieldSpec.builder(
155 useRawType
156 ? contributionBindingField.type().rawType
157 : contributionBindingField.type(),
158 componentFieldNames.getUniqueName(contributionBindingField.name()));
159 contributionField.addModifiers(PRIVATE);
160 if (useRawType) {
161 contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
162 }
163
164 return contributionField.build();
165 }
166
167 private boolean useRawType(ResolvedBindings resolvedBindings) {
168 Optional<String> bindingPackage = resolvedBindings.bindingPackage();
169 return bindingPackage.isPresent()
170 && !bindingPackage.get().equals(componentName.packageName());
erichang9dd6aed2017-06-15 15:54:00 -0700171 }
172
ronshapiro186d17a2017-08-11 08:18:07 -0700173 private BindingExpression create(
174 ResolvedBindings resolvedBindings,
175 Optional<FieldSpec> fieldSpec,
176 MemberSelect memberSelect) {
erichang9dd6aed2017-06-15 15:54:00 -0700177 BindingKey bindingKey = resolvedBindings.bindingKey();
178 switch (resolvedBindings.bindingType()) {
179 case MEMBERS_INJECTION:
ronshapiro186d17a2017-08-11 08:18:07 -0700180 return new MembersInjectorBindingExpression(
181 bindingKey, fieldSpec, hasBindingExpressions, memberSelect);
erichang9dd6aed2017-06-15 15:54:00 -0700182 case PRODUCTION:
ronshapiro186d17a2017-08-11 08:18:07 -0700183 return new ProducerBindingExpression(
184 bindingKey, fieldSpec, hasBindingExpressions, memberSelect, false);
erichang9dd6aed2017-06-15 15:54:00 -0700185 case PROVISION:
186 ProvisionBinding provisionBinding =
187 (ProvisionBinding) resolvedBindings.contributionBinding();
188
ronshapiro186d17a2017-08-11 08:18:07 -0700189 ProviderBindingExpression providerBindingExpression =
190 new ProviderBindingExpression(
191 bindingKey, fieldSpec, hasBindingExpressions, memberSelect);
erichang9dd6aed2017-06-15 15:54:00 -0700192
193 switch (provisionBinding.bindingKind()) {
194 case SUBCOMPONENT_BUILDER:
ronshapiro186d17a2017-08-11 08:18:07 -0700195 return new SubcomponentBuilderBindingExpression(
196 providerBindingExpression, subcomponentNames.get(bindingKey));
erichang9dd6aed2017-06-15 15:54:00 -0700197 case SYNTHETIC_MULTIBOUND_SET:
ronshapiro186d17a2017-08-11 08:18:07 -0700198 return new SetBindingExpression(
erichang9dd6aed2017-06-15 15:54:00 -0700199 provisionBinding,
200 graph,
201 hasBindingExpressions,
ronshapiro186d17a2017-08-11 08:18:07 -0700202 providerBindingExpression,
ronshapirocb725da2017-07-04 15:14:48 -0700203 elements);
ronshapiro3a891dd2017-07-04 20:04:14 -0700204 case SYNTHETIC_OPTIONAL_BINDING:
ronshapiro186d17a2017-08-11 08:18:07 -0700205 return new OptionalBindingExpression(
206 provisionBinding, providerBindingExpression, hasBindingExpressions);
erichang9dd6aed2017-06-15 15:54:00 -0700207 case INJECTION:
208 case PROVISION:
ronshapiroe05f9212017-08-08 11:22:11 -0700209 if (!provisionBinding.scope().isPresent()
erichang9dd6aed2017-06-15 15:54:00 -0700210 && !provisionBinding.requiresModuleInstance()
211 && provisionBinding.bindingElement().isPresent()) {
ronshapiro186d17a2017-08-11 08:18:07 -0700212 return new SimpleMethodBindingExpression(
dpbc8c6c052017-07-28 10:48:06 -0700213 compilerOptions,
erichang9dd6aed2017-06-15 15:54:00 -0700214 provisionBinding,
ronshapiro186d17a2017-08-11 08:18:07 -0700215 providerBindingExpression,
erichang9dd6aed2017-06-15 15:54:00 -0700216 hasBindingExpressions);
217 }
218 // fall through
219 default:
ronshapiro186d17a2017-08-11 08:18:07 -0700220 return providerBindingExpression;
erichang9dd6aed2017-06-15 15:54:00 -0700221 }
222 default:
223 throw new AssertionError();
224 }
225 }
226 }
erichang9dd6aed2017-06-15 15:54:00 -0700227}