blob: e86a85679e9e543b8943daf27c74a29e73c2ff62 [file] [log] [blame]
dpb68c77d82017-08-15 15:23:35 -07001/*
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
17package dagger.internal.codegen;
18
dpb64861842017-12-12 08:17:18 -080019import static com.google.common.base.Preconditions.checkArgument;
bcorsof0a99fe2017-12-04 10:52:05 -080020import static com.google.common.base.Preconditions.checkNotNull;
dpb3db68f02018-01-04 09:15:52 -080021import static com.google.common.collect.Iterables.getOnlyElement;
dpb68c77d82017-08-15 15:23:35 -070022import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
23import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
dpb3db68f02018-01-04 09:15:52 -080024import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION;
25import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
dpb09fb2cb2018-01-29 08:39:33 -080026import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope;
dpb356a6842018-02-07 07:45:50 -080027import static dagger.internal.codegen.MemberSelect.staticFactoryCreation;
dpb3db68f02018-01-04 09:15:52 -080028import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
29import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
30import static dagger.internal.codegen.TypeNames.REFERENCE_RELEASING_PROVIDER;
31import static dagger.internal.codegen.TypeNames.SINGLE_CHECK;
dpb09fb2cb2018-01-29 08:39:33 -080032import static dagger.model.BindingKind.DELEGATE;
bcorsoaf9e0042018-04-16 14:12:23 -070033import static dagger.model.BindingKind.MULTIBOUND_MAP;
34import static dagger.model.BindingKind.MULTIBOUND_SET;
dpb68c77d82017-08-15 15:23:35 -070035
dpb3db68f02018-01-04 09:15:52 -080036import com.google.auto.common.MoreTypes;
bcorso8c3605d2017-12-13 13:36:49 -080037import com.google.common.collect.HashBasedTable;
dpb3db68f02018-01-04 09:15:52 -080038import com.google.common.collect.ImmutableList;
bcorso8c3605d2017-12-13 13:36:49 -080039import com.google.common.collect.Table;
dpb68c77d82017-08-15 15:23:35 -070040import com.squareup.javapoet.ClassName;
bcorsoe643cc62017-10-24 08:57:09 -070041import com.squareup.javapoet.CodeBlock;
dpb02db2132018-01-08 07:20:23 -080042import com.squareup.javapoet.MethodSpec;
dpb3db68f02018-01-04 09:15:52 -080043import dagger.internal.MembersInjectors;
bcorso2c279662017-12-01 09:51:18 -080044import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
dpb3db68f02018-01-04 09:15:52 -080045import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
ronshapiroa5023ca2018-01-02 12:55:01 -080046import dagger.model.DependencyRequest;
ronshapiro0a277fd2017-12-22 08:30:49 -080047import dagger.model.Key;
ronshapiro120abc62017-12-15 09:57:09 -080048import dagger.model.RequestKind;
bcorsof0a99fe2017-12-04 10:52:05 -080049import java.util.Optional;
dpb356a6842018-02-07 07:45:50 -080050import javax.inject.Provider;
dpb68c77d82017-08-15 15:23:35 -070051import javax.lang.model.type.TypeMirror;
52
ronshapirodc07ed52017-08-23 08:52:10 -070053/** A central repository of code expressions used to access any binding available to a component. */
dpb68c77d82017-08-15 15:23:35 -070054final class ComponentBindingExpressions {
55
ronshapirodc07ed52017-08-23 08:52:10 -070056 // TODO(dpb,ronshapiro): refactor this and ComponentRequirementFields into a
57 // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
58 // parents? If so, maybe make BindingExpression.Factory create it.
dpb68c77d82017-08-15 15:23:35 -070059
bcorsof0a99fe2017-12-04 10:52:05 -080060 private final Optional<ComponentBindingExpressions> parent;
61 private final BindingGraph graph;
dpb02db2132018-01-08 07:20:23 -080062 private final GeneratedComponentModel generatedComponentModel;
dpb73afb302018-02-12 10:59:53 -080063 private final SubcomponentNames subcomponentNames;
64 private final ComponentRequirementFields componentRequirementFields;
65 private final ReferenceReleasingManagerFields referenceReleasingManagerFields;
66 private final OptionalFactories optionalFactories;
bcorsof0a99fe2017-12-04 10:52:05 -080067 private final DaggerTypes types;
dpbe7b89582018-02-19 08:42:19 -080068 private final DaggerElements elements;
dpb73afb302018-02-12 10:59:53 -080069 private final CompilerOptions compilerOptions;
70 private final MembersInjectionMethods membersInjectionMethods;
bcorso8a8039a2018-03-19 09:36:26 -070071 private final SwitchingProviders switchingProviders;
ronshapiro0a277fd2017-12-22 08:30:49 -080072 private final Table<Key, RequestKind, BindingExpression> expressions = HashBasedTable.create();
dpb68c77d82017-08-15 15:23:35 -070073
bcorsof0a99fe2017-12-04 10:52:05 -080074 ComponentBindingExpressions(
75 BindingGraph graph,
76 GeneratedComponentModel generatedComponentModel,
77 SubcomponentNames subcomponentNames,
78 ComponentRequirementFields componentRequirementFields,
79 OptionalFactories optionalFactories,
80 DaggerTypes types,
dpbe7b89582018-02-19 08:42:19 -080081 DaggerElements elements,
bcorsof0a99fe2017-12-04 10:52:05 -080082 CompilerOptions compilerOptions) {
83 this(
84 Optional.empty(),
85 graph,
86 generatedComponentModel,
87 subcomponentNames,
88 componentRequirementFields,
89 new ReferenceReleasingManagerFields(graph, generatedComponentModel),
90 optionalFactories,
91 types,
92 elements,
93 compilerOptions);
dpb68c77d82017-08-15 15:23:35 -070094 }
95
bcorsof0a99fe2017-12-04 10:52:05 -080096 private ComponentBindingExpressions(
97 Optional<ComponentBindingExpressions> parent,
98 BindingGraph graph,
99 GeneratedComponentModel generatedComponentModel,
100 SubcomponentNames subcomponentNames,
101 ComponentRequirementFields componentRequirementFields,
102 ReferenceReleasingManagerFields referenceReleasingManagerFields,
103 OptionalFactories optionalFactories,
104 DaggerTypes types,
dpbe7b89582018-02-19 08:42:19 -0800105 DaggerElements elements,
bcorsof0a99fe2017-12-04 10:52:05 -0800106 CompilerOptions compilerOptions) {
107 this.parent = parent;
108 this.graph = graph;
dpb02db2132018-01-08 07:20:23 -0800109 this.generatedComponentModel = generatedComponentModel;
dpb73afb302018-02-12 10:59:53 -0800110 this.subcomponentNames = checkNotNull(subcomponentNames);
111 this.componentRequirementFields = checkNotNull(componentRequirementFields);
112 this.referenceReleasingManagerFields = checkNotNull(referenceReleasingManagerFields);
113 this.optionalFactories = checkNotNull(optionalFactories);
114 this.types = checkNotNull(types);
115 this.elements = checkNotNull(elements);
116 this.compilerOptions = checkNotNull(compilerOptions);
117 this.membersInjectionMethods =
118 new MembersInjectionMethods(generatedComponentModel, this, graph, elements, types);
bcorsoaf9e0042018-04-16 14:12:23 -0700119 this.switchingProviders = new SwitchingProviders(generatedComponentModel, this, types);
bcorsof0a99fe2017-12-04 10:52:05 -0800120 }
121
122 /**
123 * Returns a new object representing the bindings available from a child component of this one.
124 */
125 ComponentBindingExpressions forChildComponent(
126 BindingGraph childGraph,
127 GeneratedComponentModel childComponentModel,
128 ComponentRequirementFields childComponentRequirementFields) {
129 return new ComponentBindingExpressions(
130 Optional.of(this),
131 childGraph,
132 childComponentModel,
dpb73afb302018-02-12 10:59:53 -0800133 subcomponentNames,
bcorsof0a99fe2017-12-04 10:52:05 -0800134 childComponentRequirementFields,
dpb73afb302018-02-12 10:59:53 -0800135 referenceReleasingManagerFields,
136 optionalFactories,
137 types,
138 elements,
139 compilerOptions);
dpb68c77d82017-08-15 15:23:35 -0700140 }
141
142 /**
143 * Returns an expression that evaluates to the value of a dependency request for a binding owned
144 * by this component or an ancestor.
145 *
146 * @param requestingClass the class that will contain the expression
147 * @throws IllegalStateException if there is no binding expression that satisfies the dependency
148 * request
149 */
ronshapiro0a277fd2017-12-22 08:30:49 -0800150 Expression getDependencyExpression(Key key, RequestKind requestKind, ClassName requestingClass) {
151 return getBindingExpression(key, requestKind).getDependencyExpression(requestingClass);
ronshapiroba794862017-09-28 11:39:34 -0700152 }
153
154 /**
155 * Returns an expression that evaluates to the value of a dependency request for a binding owned
156 * by this component or an ancestor.
157 *
158 * @param requestingClass the class that will contain the expression
159 * @throws IllegalStateException if there is no binding expression that satisfies the dependency
160 * request
161 */
162 Expression getDependencyExpression(DependencyRequest request, ClassName requestingClass) {
ronshapiro0a277fd2017-12-22 08:30:49 -0800163 return getDependencyExpression(request.key(), request.kind(), requestingClass);
dpb68c77d82017-08-15 15:23:35 -0700164 }
165
166 /**
167 * Returns an expression that evaluates to the value of a framework dependency for a binding owned
168 * in this component or an ancestor.
169 *
170 * @param requestingClass the class that will contain the expression
171 * @throws IllegalStateException if there is no binding expression that satisfies the dependency
172 * request
173 */
ronshapiroba794862017-09-28 11:39:34 -0700174 Expression getDependencyExpression(
dpb68c77d82017-08-15 15:23:35 -0700175 FrameworkDependency frameworkDependency, ClassName requestingClass) {
ronshapiroba794862017-09-28 11:39:34 -0700176 return getDependencyExpression(
ronshapiro0a277fd2017-12-22 08:30:49 -0800177 frameworkDependency.key(), frameworkDependency.dependencyRequestKind(), requestingClass);
dpb68c77d82017-08-15 15:23:35 -0700178 }
179
180 /**
bcorso5db065d2018-04-03 13:49:31 -0700181 * Returns the {@link CodeBlock} for the method argmuments used with the factory {@code create()}
182 * method for the given {@link ContributionBinding binding}.
dpb3db68f02018-01-04 09:15:52 -0800183 */
bcorso5db065d2018-04-03 13:49:31 -0700184 CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) {
185 return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding));
186 }
187
188 private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) {
189 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
190
191 if (binding.requiresModuleInstance()) {
192 arguments.add(
193 componentRequirementFields.getExpressionDuringInitialization(
194 ComponentRequirement.forModule(binding.contributingModule().get().asType()),
195 generatedComponentModel.name()));
196 }
197
198 binding
199 .frameworkDependencies()
dpb3db68f02018-01-04 09:15:52 -0800200 .stream()
bcorso5db065d2018-04-03 13:49:31 -0700201 .map(dependency -> getDependencyExpression(dependency, generatedComponentModel.name()))
202 .map(Expression::codeBlock)
203 .forEach(arguments::add);
204
205 return arguments.build();
dpb3db68f02018-01-04 09:15:52 -0800206 }
207
208 /**
dpb68c77d82017-08-15 15:23:35 -0700209 * Returns an expression that evaluates to the value of a dependency request, for passing to a
210 * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one.
211 *
212 * <p>If the method is a generated static {@link InjectionMethods injection method}, each
213 * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the
214 * case for this dependency, the returned expression will use a cast to evaluate to the raw type.
215 *
216 * @param requestingClass the class that will contain the expression
217 */
218 // TODO(b/64024402) Merge with getDependencyExpression(DependencyRequest, ClassName) if possible.
ronshapiroba794862017-09-28 11:39:34 -0700219 Expression getDependencyArgumentExpression(
dpb68c77d82017-08-15 15:23:35 -0700220 DependencyRequest dependencyRequest, ClassName requestingClass) {
dpb68c77d82017-08-15 15:23:35 -0700221
222 TypeMirror dependencyType = dependencyRequest.key().type();
ronshapiroba794862017-09-28 11:39:34 -0700223 Expression dependencyExpression = getDependencyExpression(dependencyRequest, requestingClass);
224
bcorsoaaffd2d2018-04-16 14:44:42 -0700225 if (dependencyRequest.kind().equals(RequestKind.INSTANCE)
226 && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName())
dpb68c77d82017-08-15 15:23:35 -0700227 && isRawTypeAccessible(dependencyType, requestingClass.packageName())) {
ronshapiroba794862017-09-28 11:39:34 -0700228 return dependencyExpression.castTo(types.erasure(dependencyType));
dpb68c77d82017-08-15 15:23:35 -0700229 }
230
ronshapiroba794862017-09-28 11:39:34 -0700231 return dependencyExpression;
dpb68c77d82017-08-15 15:23:35 -0700232 }
233
dpb02db2132018-01-08 07:20:23 -0800234 /** Returns the implementation of a component method. */
235 MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) {
236 checkArgument(componentMethod.dependencyRequest().isPresent());
237 DependencyRequest dependencyRequest = componentMethod.dependencyRequest().get();
238 return MethodSpec.overriding(
239 componentMethod.methodElement(),
240 MoreTypes.asDeclared(graph.componentType().asType()),
241 types)
242 .addCode(
243 getBindingExpression(dependencyRequest.key(), dependencyRequest.kind())
244 .getComponentMethodImplementation(componentMethod, generatedComponentModel.name()))
245 .build();
bcorso11f9b872017-10-09 16:18:55 -0700246 }
247
ronshapiro0a277fd2017-12-22 08:30:49 -0800248 private BindingExpression getBindingExpression(Key key, RequestKind requestKind) {
249 ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
250 if (resolvedBindings != null && !resolvedBindings.ownedBindings().isEmpty()) {
251 if (!expressions.contains(key, requestKind)) {
dpb73afb302018-02-12 10:59:53 -0800252 expressions.put(key, requestKind, createBindingExpression(resolvedBindings, requestKind));
bcorso8c3605d2017-12-13 13:36:49 -0800253 }
ronshapiro0a277fd2017-12-22 08:30:49 -0800254 return expressions.get(key, requestKind);
bcorsof0a99fe2017-12-04 10:52:05 -0800255 }
dpb80eda692018-03-05 11:31:42 -0800256 checkArgument(parent.isPresent(), "no expression found for %s-%s", key, requestKind);
257 return parent.get().getBindingExpression(key, requestKind);
bcorsof0a99fe2017-12-04 10:52:05 -0800258 }
259
dpb73afb302018-02-12 10:59:53 -0800260 /** Creates a binding expression. */
261 private BindingExpression createBindingExpression(
262 ResolvedBindings resolvedBindings, RequestKind requestKind) {
263 switch (resolvedBindings.bindingType()) {
264 case MEMBERS_INJECTION:
265 checkArgument(requestKind.equals(RequestKind.MEMBERS_INJECTION));
ronshapiro9288a972018-02-14 06:02:12 -0800266 return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods);
bcorsof0a99fe2017-12-04 10:52:05 -0800267
dpb73afb302018-02-12 10:59:53 -0800268 case PROVISION:
269 return provisionBindingExpression(resolvedBindings, requestKind);
270
271 case PRODUCTION:
272 return frameworkInstanceBindingExpression(resolvedBindings, requestKind);
273
274 default:
275 throw new AssertionError(resolvedBindings);
bcorsof0a99fe2017-12-04 10:52:05 -0800276 }
dpb73afb302018-02-12 10:59:53 -0800277 }
bcorsof0a99fe2017-12-04 10:52:05 -0800278
dpb73afb302018-02-12 10:59:53 -0800279 /**
280 * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings
281 * or a {@link dagger.producers.Producer} for production bindings.
282 */
283 private FrameworkInstanceBindingExpression frameworkInstanceBindingExpression(
284 ResolvedBindings resolvedBindings, RequestKind requestKind) {
bcorsoaf9e0042018-04-16 14:12:23 -0700285 // TODO(user): Consider merging the static factory creation logic into CreationExpressions?
286 Optional<MemberSelect> staticMethod =
287 useStaticFactoryCreation(resolvedBindings.contributionBinding())
288 ? staticFactoryCreation(resolvedBindings)
289 : Optional.empty();
dpb73afb302018-02-12 10:59:53 -0800290 FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
291 resolvedBindings.scope().isPresent()
292 ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings))
293 : frameworkInstanceCreationExpression(resolvedBindings);
294 return new FrameworkInstanceBindingExpression(
295 resolvedBindings,
296 requestKind,
297 this,
298 resolvedBindings.bindingType().frameworkType(),
299 staticMethod.isPresent()
300 ? staticMethod::get
301 : new FrameworkFieldInitializer(
302 generatedComponentModel, resolvedBindings, frameworkInstanceCreationExpression),
303 types,
304 elements);
305 }
dpb64861842017-12-12 08:17:18 -0800306
dpb73afb302018-02-12 10:59:53 -0800307 private FrameworkInstanceCreationExpression scope(
308 ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) {
309 if (requiresReleasableReferences(resolvedBindings)) {
310 return () ->
311 CodeBlock.of(
312 "$T.create($L, $L)",
313 REFERENCE_RELEASING_PROVIDER,
314 unscoped.creationExpression(),
315 referenceReleasingManagerFields.getExpression(
316 resolvedBindings.scope().get(), generatedComponentModel.name()));
317 } else {
318 return () ->
319 CodeBlock.of(
320 "$T.provider($L)",
321 resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK,
322 unscoped.creationExpression());
dpb64861842017-12-12 08:17:18 -0800323 }
dpb73afb302018-02-12 10:59:53 -0800324 }
dpb64861842017-12-12 08:17:18 -0800325
dpb73afb302018-02-12 10:59:53 -0800326 /**
327 * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a
328 * {@link dagger.producers.Producer} for production bindings.
329 */
330 private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression(
331 ResolvedBindings resolvedBindings) {
332 checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION));
333 ContributionBinding binding = resolvedBindings.contributionBinding();
334 switch (binding.kind()) {
335 case COMPONENT:
336 // The cast can be removed when we drop java 7 source support
337 return new InstanceFactoryCreationExpression(
338 () -> CodeBlock.of("($T) this", binding.key().type()));
dpb64861842017-12-12 08:17:18 -0800339
dpb73afb302018-02-12 10:59:53 -0800340 case BOUND_INSTANCE:
341 return instanceFactoryCreationExpression(
342 binding, ComponentRequirement.forBoundInstance(binding));
bcorsof0a99fe2017-12-04 10:52:05 -0800343
dpb73afb302018-02-12 10:59:53 -0800344 case COMPONENT_DEPENDENCY:
345 return instanceFactoryCreationExpression(
346 binding, ComponentRequirement.forDependency(binding.key().type()));
dpb3db68f02018-01-04 09:15:52 -0800347
dpb73afb302018-02-12 10:59:53 -0800348 case COMPONENT_PROVISION:
349 return new DependencyMethodProviderCreationExpression(
350 binding, generatedComponentModel, componentRequirementFields, compilerOptions, graph);
dpb3db68f02018-01-04 09:15:52 -0800351
dpb73afb302018-02-12 10:59:53 -0800352 case SUBCOMPONENT_BUILDER:
353 return new SubcomponentBuilderProviderCreationExpression(
354 binding.key().type(), subcomponentNames.get(binding.key()));
dpb356a6842018-02-07 07:45:50 -0800355
dpb73afb302018-02-12 10:59:53 -0800356 case INJECTION:
357 case PROVISION:
bcorso5db065d2018-04-03 13:49:31 -0700358 return new InjectionOrProvisionProviderCreationExpression(binding, this);
dpb3db68f02018-01-04 09:15:52 -0800359
dpb73afb302018-02-12 10:59:53 -0800360 case COMPONENT_PRODUCTION:
361 return new DependencyMethodProducerCreationExpression(
362 binding, generatedComponentModel, componentRequirementFields, graph);
dpb3db68f02018-01-04 09:15:52 -0800363
dpb73afb302018-02-12 10:59:53 -0800364 case PRODUCTION:
bcorso5db065d2018-04-03 13:49:31 -0700365 return new ProducerCreationExpression(binding, this);
dpb3db68f02018-01-04 09:15:52 -0800366
dpb73afb302018-02-12 10:59:53 -0800367 case MULTIBOUND_SET:
368 return new SetFactoryCreationExpression(binding, generatedComponentModel, this, graph);
dpb3db68f02018-01-04 09:15:52 -0800369
dpb73afb302018-02-12 10:59:53 -0800370 case MULTIBOUND_MAP:
371 return new MapFactoryCreationExpression(binding, generatedComponentModel, this, graph);
dpb3db68f02018-01-04 09:15:52 -0800372
dpb73afb302018-02-12 10:59:53 -0800373 case RELEASABLE_REFERENCE_MANAGER:
374 return new ReleasableReferenceManagerProviderCreationExpression(
375 binding, generatedComponentModel, referenceReleasingManagerFields);
dpb3db68f02018-01-04 09:15:52 -0800376
dpb73afb302018-02-12 10:59:53 -0800377 case RELEASABLE_REFERENCE_MANAGERS:
378 return new ReleasableReferenceManagerSetProviderCreationExpression(
379 binding, generatedComponentModel, referenceReleasingManagerFields, graph);
dpb3db68f02018-01-04 09:15:52 -0800380
dpb73afb302018-02-12 10:59:53 -0800381 case DELEGATE:
382 return new DelegatingFrameworkInstanceCreationExpression(
383 binding, generatedComponentModel, this);
dpb3db68f02018-01-04 09:15:52 -0800384
dpb73afb302018-02-12 10:59:53 -0800385 case OPTIONAL:
386 if (binding.explicitDependencies().isEmpty()) {
387 return () -> optionalFactories.absentOptionalProvider(binding);
388 } else {
389 return () ->
390 optionalFactories.presentOptionalFactory(
391 binding,
392 getDependencyExpression(
393 getOnlyElement(binding.frameworkDependencies()),
394 generatedComponentModel.name())
395 .codeBlock());
dpb356a6842018-02-07 07:45:50 -0800396 }
dpb356a6842018-02-07 07:45:50 -0800397
dpb73afb302018-02-12 10:59:53 -0800398 case MEMBERS_INJECTOR:
399 TypeMirror membersInjectedType =
400 getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments());
dpb356a6842018-02-07 07:45:50 -0800401
dpb73afb302018-02-12 10:59:53 -0800402 if (((ProvisionBinding) binding).injectionSites().isEmpty()) {
403 return new InstanceFactoryCreationExpression(
404 // The type parameter can be removed when we drop Java 7 source support.
405 () -> CodeBlock.of("$T.<$T>noOp()", MembersInjectors.class, membersInjectedType));
406 } else {
407 return new InstanceFactoryCreationExpression(
dpb356a6842018-02-07 07:45:50 -0800408 () ->
dpb73afb302018-02-12 10:59:53 -0800409 CodeBlock.of(
410 "$T.create($L)",
411 membersInjectorNameForType(MoreTypes.asTypeElement(membersInjectedType)),
bcorso5db065d2018-04-03 13:49:31 -0700412 getCreateMethodArgumentsCodeBlock(binding)));
dpb73afb302018-02-12 10:59:53 -0800413 }
414
415 default:
416 throw new AssertionError(binding);
417 }
418 }
419
420 private InstanceFactoryCreationExpression instanceFactoryCreationExpression(
421 ContributionBinding binding, ComponentRequirement componentRequirement) {
422 return new InstanceFactoryCreationExpression(
423 binding.nullableType().isPresent(),
424 () ->
425 componentRequirementFields.getExpressionDuringInitialization(
426 componentRequirement, generatedComponentModel.name()));
427 }
428
429 /** Returns a binding expression for a provision binding. */
430 private BindingExpression provisionBindingExpression(
431 ResolvedBindings resolvedBindings, RequestKind requestKind) {
432 switch (requestKind) {
433 case PRODUCER:
434 return producerFromProviderBindingExpression(resolvedBindings, requestKind);
435
436 case INSTANCE:
437 return instanceBindingExpression(resolvedBindings);
438
439 case FUTURE:
440 return new ImmediateFutureBindingExpression(resolvedBindings, this, types);
441
442 case LAZY:
443 case PRODUCED:
444 case PROVIDER_OF_LAZY:
445 return new DerivedFromProviderBindingExpression(resolvedBindings, requestKind, this, types);
446
447 case PROVIDER:
448 return providerBindingExpression(resolvedBindings);
449
450 case MEMBERS_INJECTION:
451 throw new IllegalArgumentException();
dpb356a6842018-02-07 07:45:50 -0800452 }
453
dpb73afb302018-02-12 10:59:53 -0800454 throw new AssertionError();
455 }
dpb356a6842018-02-07 07:45:50 -0800456
dpb73afb302018-02-12 10:59:53 -0800457 /**
458 * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a
459 * provision binding.
460 */
461 private FrameworkInstanceBindingExpression producerFromProviderBindingExpression(
462 ResolvedBindings resolvedBindings, RequestKind requestKind) {
463 checkArgument(resolvedBindings.bindingType().frameworkType().equals(FrameworkType.PROVIDER));
464 return new FrameworkInstanceBindingExpression(
465 resolvedBindings,
466 requestKind,
467 this,
468 FrameworkType.PRODUCER,
469 new FrameworkFieldInitializer(
470 generatedComponentModel,
471 resolvedBindings,
472 new ProducerFromProviderCreationExpression(
473 resolvedBindings.contributionBinding(), generatedComponentModel, this)),
474 types,
475 elements);
476 }
dpb356a6842018-02-07 07:45:50 -0800477
dpb73afb302018-02-12 10:59:53 -0800478 /**
479 * Returns a binding expression for {@link RequestKind#INSTANCE} requests.
480 *
481 * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an
482 * instance of this binding, return it, wrapped in a method if the binding {@linkplain
483 * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies.
484 *
485 * <p>In default mode, we can use direct expressions for bindings that don't need to be cached in
486 * a reference-releasing scope.
487 *
488 * <p>In Android mode, we can use direct expressions unless the binding needs to be cached.
489 */
490 private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) {
491 Optional<BindingExpression> maybeDirectInstanceExpression =
492 unscopedDirectInstanceExpression(resolvedBindings);
493 if (canUseDirectInstanceExpression(resolvedBindings)
494 && maybeDirectInstanceExpression.isPresent()) {
495 BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get();
496 return directInstanceExpression.requiresMethodEncapsulation()
497 || needsCaching(resolvedBindings)
498 ? wrapInMethod(resolvedBindings, RequestKind.INSTANCE, directInstanceExpression)
499 : directInstanceExpression;
dpb356a6842018-02-07 07:45:50 -0800500 }
dpb73afb302018-02-12 10:59:53 -0800501 return new DerivedFromProviderBindingExpression(
502 resolvedBindings, RequestKind.INSTANCE, this, types);
503 }
dpb356a6842018-02-07 07:45:50 -0800504
dpb73afb302018-02-12 10:59:53 -0800505 /**
506 * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call
507 * {@code get()} on its provider, if there is one.
508 */
509 private Optional<BindingExpression> unscopedDirectInstanceExpression(
510 ResolvedBindings resolvedBindings) {
511 switch (resolvedBindings.contributionBinding().kind()) {
512 case DELEGATE:
513 return Optional.of(
514 new DelegateBindingExpression(
515 resolvedBindings, RequestKind.INSTANCE, this, types, elements));
516
517 case COMPONENT:
518 return Optional.of(
519 new ComponentInstanceBindingExpression(
520 resolvedBindings, generatedComponentModel.name()));
521
522 case COMPONENT_DEPENDENCY:
523 return Optional.of(
524 new ComponentRequirementBindingExpression(
525 resolvedBindings,
526 ComponentRequirement.forDependency(resolvedBindings.key().type()),
527 componentRequirementFields));
528
529 case COMPONENT_PROVISION:
530 return Optional.of(
531 new ComponentProvisionBindingExpression(
532 resolvedBindings, graph, componentRequirementFields, compilerOptions));
533
534 case SUBCOMPONENT_BUILDER:
535 return Optional.of(
536 new SubcomponentBuilderBindingExpression(
537 resolvedBindings, subcomponentNames.get(resolvedBindings.key())));
538
539 case MULTIBOUND_SET:
540 return Optional.of(
541 new SetBindingExpression(resolvedBindings, graph, this, types, elements));
542
543 case MULTIBOUND_MAP:
544 return Optional.of(
545 new MapBindingExpression(resolvedBindings, graph, this, types, elements));
546
547 case OPTIONAL:
548 return Optional.of(new OptionalBindingExpression(resolvedBindings, this, types));
549
550 case BOUND_INSTANCE:
551 return Optional.of(
552 new ComponentRequirementBindingExpression(
553 resolvedBindings,
554 ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()),
555 componentRequirementFields));
556
557 case INJECTION:
558 case PROVISION:
559 return Optional.of(
560 new SimpleMethodBindingExpression(
561 resolvedBindings,
562 compilerOptions,
563 this,
564 membersInjectionMethods,
565 componentRequirementFields,
566 elements));
567
568 case MEMBERS_INJECTOR:
569 case RELEASABLE_REFERENCE_MANAGER:
570 case RELEASABLE_REFERENCE_MANAGERS:
571 // TODO(dpb): Implement direct expressions for these.
572 return Optional.empty();
573
574 case MEMBERS_INJECTION:
575 case COMPONENT_PRODUCTION:
576 case PRODUCTION:
577 throw new IllegalArgumentException(
578 resolvedBindings.contributionBinding().kind().toString());
579 }
580 throw new AssertionError();
581 }
582
583 /**
bcorsoaf9e0042018-04-16 14:12:23 -0700584 * Returns {@code true} if the binding should use the static factory creation strategy.
585 *
586 * In default mode, we always use the static factory creation strategy. In Android mode, we
587 * prefer to use the SwitchingProvider than the static factories to reduce class loading; however,
588 * we allow static factories that can reused across multiple bindings, e.g. {@code MapFactory} or
589 * {@code SetFactory}.
590 */
591 private boolean useStaticFactoryCreation(ContributionBinding binding) {
592 return !compilerOptions.experimentalAndroidMode()
593 || binding.kind().equals(MULTIBOUND_MAP)
594 || binding.kind().equals(MULTIBOUND_SET);
595 }
596
597 /**
dpb73afb302018-02-12 10:59:53 -0800598 * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this
599 * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached},
600 * we can.
601 *
602 * <p>In Android mode, we can use a direct expression even if the binding {@linkplain
603 * #needsCaching(ResolvedBindings) needs to be cached} as long as it's not in a
604 * reference-releasing scope.
605 */
606 private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) {
607 return !needsCaching(resolvedBindings)
608 || (compilerOptions.experimentalAndroidMode()
609 && !requiresReleasableReferences(resolvedBindings));
610 }
611
612 /**
613 * Returns a binding expression for {@link RequestKind#PROVIDER} requests.
614 *
615 * <p>In default mode, {@code @Binds} bindings that don't {@linkplain
616 * #needsCaching(ResolvedBindings) need to be cached} can use a {@link DelegateBindingExpression}.
617 *
618 * <p>In Android mode, if {@linkplain #instanceBindingExpression(ResolvedBindings) instance
619 * binding expressions} don't call {@code Provider.get()} on the provider binding expression, and
bcorso8a8039a2018-03-19 09:36:26 -0700620 * there's no simple factory, then return a {@link SwitchingProviders} binding expression wrapped
621 * in a method.
dpb73afb302018-02-12 10:59:53 -0800622 *
623 * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}.
624 */
625 private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) {
626 if (compilerOptions.experimentalAndroidMode()) {
bcorso8a8039a2018-03-19 09:36:26 -0700627 if (!frameworkInstanceCreationExpression(resolvedBindings).isSimpleFactory()
dpb73afb302018-02-12 10:59:53 -0800628 && !(instanceBindingExpression(resolvedBindings)
629 instanceof DerivedFromProviderBindingExpression)) {
630 return wrapInMethod(
631 resolvedBindings,
632 RequestKind.PROVIDER,
bcorsoaf9e0042018-04-16 14:12:23 -0700633 switchingProviders.newBindingExpression(resolvedBindings.contributionBinding()));
bcorso3828ca22018-01-29 10:14:12 -0800634 }
dpb73afb302018-02-12 10:59:53 -0800635 } else if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)
636 && !needsCaching(resolvedBindings)) {
637 return new DelegateBindingExpression(
638 resolvedBindings, RequestKind.PROVIDER, this, types, elements);
bcorsof0a99fe2017-12-04 10:52:05 -0800639 }
dpb73afb302018-02-12 10:59:53 -0800640 return frameworkInstanceBindingExpression(resolvedBindings, RequestKind.PROVIDER);
641 }
bcorso3828ca22018-01-29 10:14:12 -0800642
dpb73afb302018-02-12 10:59:53 -0800643 /**
dpb73afb302018-02-12 10:59:53 -0800644 * Returns a binding expression that uses a given one as the body of a method that users call. If
645 * a component provision method matches it, it will be the method implemented. If not, a new
646 * private method will be written.
647 */
648 private BindingExpression wrapInMethod(
649 ResolvedBindings resolvedBindings,
650 RequestKind requestKind,
651 BindingExpression bindingExpression) {
652 BindingMethodImplementation methodImplementation =
653 methodImplementation(resolvedBindings, requestKind, bindingExpression);
654
655 return findMatchingComponentMethod(resolvedBindings.key(), requestKind)
656 .<BindingExpression>map(
657 componentMethod ->
658 new ComponentMethodBindingExpression(
dpbd7685762018-02-16 12:20:09 -0800659 methodImplementation, generatedComponentModel, componentMethod))
dpb73afb302018-02-12 10:59:53 -0800660 .orElseGet(
661 () ->
662 new PrivateMethodBindingExpression(
663 resolvedBindings, requestKind, methodImplementation, generatedComponentModel));
664 }
665
666 /** Returns the first component method associated with this request kind, if one exists. */
667 private Optional<ComponentMethodDescriptor> findMatchingComponentMethod(
668 Key key, RequestKind requestKind) {
669 return graph
670 .componentDescriptor()
671 .componentMethods()
672 .stream()
673 .filter(method -> doesComponentMethodMatch(method, key, requestKind))
674 .findFirst();
675 }
676
677 /** Returns true if the component method matches the dependency request binding key and kind. */
678 private boolean doesComponentMethodMatch(
679 ComponentMethodDescriptor componentMethod, Key key, RequestKind requestKind) {
680 return componentMethod
681 .dependencyRequest()
682 .filter(request -> request.key().equals(key))
683 .filter(request -> request.kind().equals(requestKind))
684 .isPresent();
685 }
686
687 private BindingMethodImplementation methodImplementation(
688 ResolvedBindings resolvedBindings,
689 RequestKind requestKind,
690 BindingExpression bindingExpression) {
bcorso567ff0e2018-03-21 13:49:09 -0700691 if (compilerOptions.experimentalAndroidMode()) {
692 if (requestKind.equals(RequestKind.PROVIDER)) {
693 return new SingleCheckedMethodImplementation(
694 resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel);
695 } else if (requestKind.equals(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) {
696 return resolvedBindings.scope().get().isReusable()
697 ? new SingleCheckedMethodImplementation(
698 resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel)
699 : new DoubleCheckedMethodImplementation(
700 resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel);
701 }
702 }
703
704 return new BindingMethodImplementation(
705 resolvedBindings, requestKind, bindingExpression, generatedComponentModel.name(), types);
dpb73afb302018-02-12 10:59:53 -0800706 }
707
708 /**
709 * Returns {@code true} if the component needs to make sure the provided value is cached.
710 *
711 * <p>The component needs to cache the value for scoped bindings except for {@code @Binds}
712 * bindings whose scope is no stronger than their delegate's.
713 */
714 private boolean needsCaching(ResolvedBindings resolvedBindings) {
715 if (!resolvedBindings.scope().isPresent()) {
716 return false;
bcorso3828ca22018-01-29 10:14:12 -0800717 }
dpb73afb302018-02-12 10:59:53 -0800718 if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) {
719 return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph);
720 }
721 return true;
722 }
723
724 // TODO(user): Enable releasable references in experimentalAndroidMode
725 private boolean requiresReleasableReferences(ResolvedBindings resolvedBindings) {
726 return resolvedBindings.scope().isPresent()
727 && referenceReleasingManagerFields.requiresReleasableReferences(
728 resolvedBindings.scope().get());
dpb68c77d82017-08-15 15:23:35 -0700729 }
730}