blob: 8ba51433581f3eea8190c5e92e16180242a55417 [file] [log] [blame]
ronshapiroe05f9212017-08-08 11:22:11 -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
19import static com.google.common.base.CaseFormat.LOWER_CAMEL;
20import static com.google.common.base.CaseFormat.UPPER_CAMEL;
ronshapiro1ae4c002018-11-16 08:23:34 -080021import static com.google.common.base.Preconditions.checkArgument;
ronshapiroe05f9212017-08-08 11:22:11 -070022import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
23import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
24import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
25import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
ronshapiroe05f9212017-08-08 11:22:11 -070026import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
ronshapiroe05f9212017-08-08 11:22:11 -070027import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
dpb639bd4d2017-10-24 15:15:17 -070028import static dagger.internal.codegen.DaggerStreams.toImmutableList;
ronshapiro5f059592017-12-07 07:18:33 -080029import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
ronshapiro120abc62017-12-15 09:57:09 -080030import static dagger.internal.codegen.RequestKinds.requestTypeName;
ronshapiroe05f9212017-08-08 11:22:11 -070031import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
32import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
33import static dagger.internal.codegen.TypeNames.rawTypeName;
34import static java.util.stream.Collectors.toList;
ronshapiroe05f9212017-08-08 11:22:11 -070035import static javax.lang.model.element.Modifier.STATIC;
36import static javax.lang.model.type.TypeKind.VOID;
37
38import com.google.auto.common.MoreElements;
39import com.google.common.collect.ImmutableList;
40import com.google.common.collect.ImmutableSet;
41import com.squareup.javapoet.ClassName;
42import com.squareup.javapoet.CodeBlock;
ronshapiroe05f9212017-08-08 11:22:11 -070043import com.squareup.javapoet.ParameterSpec;
44import com.squareup.javapoet.TypeName;
ronshapiroe05f9212017-08-08 11:22:11 -070045import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
ronshapiroa5023ca2018-01-02 12:55:01 -080046import dagger.model.DependencyRequest;
ronshapiro120abc62017-12-15 09:57:09 -080047import dagger.model.RequestKind;
ronshapiroe05f9212017-08-08 11:22:11 -070048import java.util.ArrayList;
49import java.util.List;
50import java.util.Optional;
51import java.util.function.Function;
52import javax.lang.model.element.ExecutableElement;
ronshapiroe05f9212017-08-08 11:22:11 -070053import javax.lang.model.element.TypeElement;
ronshapiroe05f9212017-08-08 11:22:11 -070054import javax.lang.model.element.VariableElement;
55import javax.lang.model.type.TypeKind;
56import javax.lang.model.type.TypeMirror;
ronshapiro55cd3922017-08-10 09:48:08 -070057import javax.lang.model.util.Types;
ronshapiroe05f9212017-08-08 11:22:11 -070058
ronshapiro61bdbb32018-12-21 12:01:20 -080059/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
ronshapiroe05f9212017-08-08 11:22:11 -070060final class InjectionMethods {
ronshapiro61bdbb32018-12-21 12:01:20 -080061
ronshapiroe05f9212017-08-08 11:22:11 -070062 /**
ronshapiro61bdbb32018-12-21 12:01:20 -080063 * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
ronshapiroe05f9212017-08-08 11:22:11 -070064 * constructor. Its parameters match the dependency requests for constructor and members
65 * injection.
66 *
67 * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
68 * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
69 * necessary and this method returns {@link Optional#empty()}.
70 *
ronshapiroe05f9212017-08-08 11:22:11 -070071 * <p>Example:
72 *
73 * <pre><code>
74 * abstract class FooModule {
75 * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
76 * }
77 *
78 * public static proxyProvideFoo(Bar bar, Baz baz) { … }
79 * </code></pre>
80 *
81 * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
82 * raw parameter types are publicly accessible, no method is necessary and this method returns
83 * {@code Optional#empty()}.
84 *
85 * <p>Example:
86 *
87 * <pre><code>
88 * class Foo {
89 * {@literal @Inject} Foo(Bar bar) {}
90 * }
91 *
92 * public static Foo newFoo(Bar bar) { … }
93 * </code></pre>
94 */
95 static final class ProvisionMethod {
96
97 /**
98 * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
bcorso472e2682018-04-09 14:51:34 -070099 * constructor} and injects the instance's members.
ronshapiroe05f9212017-08-08 11:22:11 -0700100 */
ronshapiro61bdbb32018-12-21 12:01:20 -0800101 static InjectionMethod create(
102 ProvisionBinding binding, CompilerOptions compilerOptions, DaggerElements elements) {
103 ClassName proxyEnclosingClass = generatedClassNameForBinding(binding);
ronshapiroe05f9212017-08-08 11:22:11 -0700104 ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
105 switch (element.getKind()) {
106 case CONSTRUCTOR:
ronshapiro61bdbb32018-12-21 12:01:20 -0800107 return constructorProxy(proxyEnclosingClass, element, elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700108 case METHOD:
bcorso472e2682018-04-09 14:51:34 -0700109 return methodProxy(
ronshapiro61bdbb32018-12-21 12:01:20 -0800110 proxyEnclosingClass,
bcorso472e2682018-04-09 14:51:34 -0700111 element,
112 methodName(element),
113 ReceiverAccessibility.IGNORE,
ronshapiro61bdbb32018-12-21 12:01:20 -0800114 CheckNotNullPolicy.get(binding, compilerOptions),
115 elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700116 default:
117 throw new AssertionError(element);
118 }
119 }
120
121 /**
122 * Invokes the injection method for {@code binding}, with the dependencies transformed with the
123 * {@code dependencyUsage} function.
124 */
ronshapiro61bdbb32018-12-21 12:01:20 -0800125 // TODO(ronshapiro): Further extract a ProvisionMethod type that composes an InjectionMethod, so
126 // users can write ProvisionMethod.create().invoke()
ronshapiroe05f9212017-08-08 11:22:11 -0700127 static CodeBlock invoke(
128 ProvisionBinding binding,
129 Function<DependencyRequest, CodeBlock> dependencyUsage,
ronshapirodc07ed52017-08-23 08:52:10 -0700130 ClassName requestingClass,
ronshapiro5f059592017-12-07 07:18:33 -0800131 Optional<CodeBlock> moduleReference,
ronshapiro61bdbb32018-12-21 12:01:20 -0800132 CompilerOptions compilerOptions,
133 DaggerElements elements) {
ronshapirodc07ed52017-08-23 08:52:10 -0700134 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
135 moduleReference.ifPresent(arguments::add);
136 arguments.addAll(
137 injectionMethodArguments(
138 binding.provisionDependencies(), dependencyUsage, requestingClass));
ronshapiro61bdbb32018-12-21 12:01:20 -0800139 // TODO(ronshapiro): make InjectionMethods @Injectable
140 return create(binding, compilerOptions, elements).invoke(arguments.build(), requestingClass);
ronshapiroe05f9212017-08-08 11:22:11 -0700141 }
142
ronshapiro61bdbb32018-12-21 12:01:20 -0800143 private static InjectionMethod constructorProxy(
144 ClassName proxyEnclosingClass, ExecutableElement constructor, DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700145 TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
ronshapiro61bdbb32018-12-21 12:01:20 -0800146 InjectionMethod.Builder injectionMethod =
147 InjectionMethod.builder(elements)
148 .name(methodName(constructor))
149 .returnType(enclosingType.asType())
150 .enclosingClass(proxyEnclosingClass);
ronshapiroe05f9212017-08-08 11:22:11 -0700151
ronshapiro61bdbb32018-12-21 12:01:20 -0800152 injectionMethod
153 .copyTypeParameters(enclosingType)
154 .copyThrows(constructor);
ronshapiroe05f9212017-08-08 11:22:11 -0700155
ronshapiro61bdbb32018-12-21 12:01:20 -0800156 CodeBlock arguments = injectionMethod.copyParameters(constructor);
157 injectionMethod
158 .methodBodyBuilder()
159 .addStatement("return new $T($L)", enclosingType, arguments);
160 return injectionMethod.build();
ronshapiroe05f9212017-08-08 11:22:11 -0700161 }
162
163 /**
164 * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
165 * requires the use of an injection method.
166 */
ronshapiro5f059592017-12-07 07:18:33 -0800167 static boolean requiresInjectionMethod(
ronshapiro1ae4c002018-11-16 08:23:34 -0800168 ProvisionBinding binding,
169 ImmutableList<Expression> arguments,
170 CompilerOptions compilerOptions,
171 String callingPackage,
172 DaggerTypes types) {
ronshapiroe05f9212017-08-08 11:22:11 -0700173 ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
174 return !binding.injectionSites().isEmpty()
ronshapiro5f059592017-12-07 07:18:33 -0800175 || binding.shouldCheckForNull(compilerOptions)
ronshapiroe05f9212017-08-08 11:22:11 -0700176 || !isElementAccessibleFrom(method, callingPackage)
ronshapiro1ae4c002018-11-16 08:23:34 -0800177 || !areParametersAssignable(method, arguments, types)
178 // This check should be removable once we drop support for -source 7
179 || method.getParameters().stream()
180 .map(VariableElement::asType)
181 .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
182 }
183
184 private static boolean areParametersAssignable(
185 ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types) {
186 List<? extends VariableElement> parameters = element.getParameters();
187 checkArgument(parameters.size() == arguments.size());
188 for (int i = 0; i < parameters.size(); i++) {
189 if (!types.isAssignable(arguments.get(i).type(), parameters.get(i).asType())) {
190 return false;
191 }
192 }
193 return true;
ronshapiroe05f9212017-08-08 11:22:11 -0700194 }
195
ronshapiroe05f9212017-08-08 11:22:11 -0700196 /**
197 * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
198 * associated with {@code @Inject} constructors, the method will also inject all {@link
199 * InjectionSite}s.
200 */
201 private static String methodName(ExecutableElement method) {
202 switch (method.getKind()) {
203 case CONSTRUCTOR:
204 return "new" + method.getEnclosingElement().getSimpleName();
205 case METHOD:
206 return "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
207 default:
208 throw new AssertionError(method);
209 }
210 }
211 }
212
213 /**
214 * A static method that injects one member of an instance of a type. Its first parameter is an
215 * instance of the type to be injected. The remaining parameters match the dependency requests for
216 * the injection site.
217 *
218 * <p>Example:
219 *
220 * <pre><code>
221 * class Foo {
222 * {@literal @Inject} Bar bar;
223 * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
224 * }
225 *
226 * public static injectBar(Foo instance, Bar bar) { … }
227 * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
228 * </code></pre>
229 */
230 static final class InjectionSiteMethod {
231 /**
232 * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
233 * that's in a different package), a method in the supertype's package must be generated to give
234 * the subclass's members injector a way to inject it. Each potentially inaccessible member
235 * receives its own method, as the subclass may need to inject them in a different order from
236 * the parent class.
237 */
ronshapiro61bdbb32018-12-21 12:01:20 -0800238 static InjectionMethod create(InjectionSite injectionSite, DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700239 String methodName = methodName(injectionSite);
ronshapiro61bdbb32018-12-21 12:01:20 -0800240 ClassName proxyEnclosingClass = membersInjectorNameForType(
241 MoreElements.asType(injectionSite.element().getEnclosingElement()));
ronshapiroe05f9212017-08-08 11:22:11 -0700242 switch (injectionSite.kind()) {
243 case METHOD:
244 return methodProxy(
ronshapiro61bdbb32018-12-21 12:01:20 -0800245 proxyEnclosingClass,
ronshapiroe05f9212017-08-08 11:22:11 -0700246 MoreElements.asExecutable(injectionSite.element()),
247 methodName,
ronshapiro5f059592017-12-07 07:18:33 -0800248 ReceiverAccessibility.CAST_IF_NOT_PUBLIC,
ronshapiro61bdbb32018-12-21 12:01:20 -0800249 CheckNotNullPolicy.IGNORE,
250 elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700251 case FIELD:
ronshapiro61bdbb32018-12-21 12:01:20 -0800252 return fieldProxy(
253 proxyEnclosingClass,
254 MoreElements.asVariable(injectionSite.element()),
255 methodName,
256 elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700257 default:
258 throw new AssertionError(injectionSite);
259 }
260 }
261
262 /**
263 * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
264 * transformed using the {@code dependencyUsage} function.
ronshapiro55cd3922017-08-10 09:48:08 -0700265 *
266 * @param instanceType the type of the {@code instance} parameter
ronshapiroe05f9212017-08-08 11:22:11 -0700267 */
268 static CodeBlock invokeAll(
269 ImmutableSet<InjectionSite> injectionSites,
270 ClassName generatedTypeName,
271 CodeBlock instanceCodeBlock,
ronshapiro55cd3922017-08-10 09:48:08 -0700272 TypeMirror instanceType,
273 Types types,
ronshapiro61bdbb32018-12-21 12:01:20 -0800274 Function<DependencyRequest, CodeBlock> dependencyUsage,
275 DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700276 return injectionSites
277 .stream()
278 .map(
ronshapiro55cd3922017-08-10 09:48:08 -0700279 injectionSite -> {
280 TypeMirror injectSiteType =
281 types.erasure(injectionSite.element().getEnclosingElement().asType());
282
283 // If instance has been declared as Object because it is not accessible from the
284 // component, but the injectionSite is in a supertype of instanceType that is
285 // publicly accessible, the InjectionSiteMethod will request the actual type and not
286 // Object as the first parameter. If so, cast to the supertype which is accessible
287 // from within generatedTypeName
288 CodeBlock maybeCastedInstance =
289 !types.isSubtype(instanceType, injectSiteType)
290 && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
291 ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
292 : instanceCodeBlock;
293 return CodeBlock.of(
294 "$L;",
ronshapiro61bdbb32018-12-21 12:01:20 -0800295 invoke(
296 injectionSite,
297 generatedTypeName,
298 maybeCastedInstance,
299 dependencyUsage,
300 elements));
ronshapiro55cd3922017-08-10 09:48:08 -0700301 })
ronshapiroe05f9212017-08-08 11:22:11 -0700302 .collect(toConcatenatedCodeBlock());
303 }
304
305 /**
306 * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
307 * using the {@code dependencyUsage} function.
308 */
309 private static CodeBlock invoke(
310 InjectionSite injectionSite,
311 ClassName generatedTypeName,
312 CodeBlock instanceCodeBlock,
ronshapiro61bdbb32018-12-21 12:01:20 -0800313 Function<DependencyRequest, CodeBlock> dependencyUsage,
314 DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700315 List<CodeBlock> arguments = new ArrayList<>();
316 arguments.add(instanceCodeBlock);
317 if (!injectionSite.dependencies().isEmpty()) {
318 arguments.addAll(
319 injectionSite
320 .dependencies()
321 .stream()
322 .map(dependencyUsage)
323 .collect(toList()));
324 }
ronshapiro61bdbb32018-12-21 12:01:20 -0800325 return create(injectionSite, elements).invoke(arguments, generatedTypeName);
ronshapiroe05f9212017-08-08 11:22:11 -0700326 }
327
328 /*
329 * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
330 *
331 * - @Inject void members() {} will generate a method that conflicts with the instance
332 * method `injectMembers(T)`
333 * - Adding the index could conflict with another member:
334 * @Inject void a(Object o) {}
335 * @Inject void a(String s) {}
336 * @Inject void a1(String s) {}
337 *
338 * Here, Method a(String) will add the suffix "1", which will conflict with the method
339 * generated for a1(String)
340 * - Members named "members" or "methods" could also conflict with the {@code static} injection
341 * method.
342 */
343 private static String methodName(InjectionSite injectionSite) {
344 int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
345 String indexString = index == 0 ? "" : String.valueOf(index + 1);
346 return "inject"
347 + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
348 + indexString;
349 }
350 }
351
352 /**
353 * Returns an argument list suitable for calling an injection method. Down-casts any arguments
354 * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
355 *
356 * @param dependencies the dependencies used by the method
357 * @param dependencyUsage function to apply on each of {@code dependencies} before casting
358 * @param requestingClass the class calling the injection method
359 */
ronshapirodc07ed52017-08-23 08:52:10 -0700360 private static ImmutableList<CodeBlock> injectionMethodArguments(
ronshapiroe05f9212017-08-08 11:22:11 -0700361 ImmutableSet<DependencyRequest> dependencies,
362 Function<DependencyRequest, CodeBlock> dependencyUsage,
363 ClassName requestingClass) {
364 return dependencies.stream()
365 .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
ronshapirodc07ed52017-08-23 08:52:10 -0700366 .collect(toImmutableList());
ronshapiroe05f9212017-08-08 11:22:11 -0700367 }
368
369 private static CodeBlock injectionMethodArgument(
370 DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
371 TypeMirror keyType = dependency.key().type();
372 CodeBlock.Builder codeBlock = CodeBlock.builder();
373 if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
374 && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
ronshapiro120abc62017-12-15 09:57:09 -0800375 if (!dependency.kind().equals(RequestKind.INSTANCE)) {
ronshapiroe05f9212017-08-08 11:22:11 -0700376 TypeName usageTypeName = accessibleType(dependency);
377 codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
378 } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
379 codeBlock.add("($T)", keyType);
380 }
381 }
382 return codeBlock.add(argument).build();
383 }
384
385 /**
386 * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
387 * {@link Object}.
388 */
389 private static TypeName accessibleType(DependencyRequest dependency) {
ronshapiro120abc62017-12-15 09:57:09 -0800390 TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
ronshapiroa5023ca2018-01-02 12:55:01 -0800391 return dependency
392 .requestElement()
393 .map(element -> element.asType().getKind().isPrimitive())
394 .orElse(false)
395 ? typeName.unbox()
396 : typeName;
ronshapiroe05f9212017-08-08 11:22:11 -0700397 }
398
399 /**
400 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
401 * Object}.
402 */
403 private static TypeName accessibleType(TypeMirror type) {
404 return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
405 }
406
ronshapiro61bdbb32018-12-21 12:01:20 -0800407 /**
408 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
409 * Object}.
410 */
411 // TODO(ronshapiro): Can we use DaggerTypes.publiclyAccessibleType in place of this method?
412 private static TypeMirror accessibleType(TypeMirror type, DaggerElements elements) {
413 return isRawTypePubliclyAccessible(type)
414 ? type
415 : elements.getTypeElement(Object.class).asType();
ronshapiroe05f9212017-08-08 11:22:11 -0700416 }
417
418 private enum ReceiverAccessibility {
419 CAST_IF_NOT_PUBLIC {
420 @Override
ronshapiro61bdbb32018-12-21 12:01:20 -0800421 TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
422 return accessibleType(type, elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700423 }
424
425 @Override
426 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
427 return instanceWithPotentialCast(instance, instanceType);
428 }
429 },
430 IGNORE {
431 @Override
ronshapiro61bdbb32018-12-21 12:01:20 -0800432 TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
433 return type;
ronshapiroe05f9212017-08-08 11:22:11 -0700434 }
435
436 @Override
437 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
438 return instance;
439 }
440 },
441 ;
442
ronshapiro61bdbb32018-12-21 12:01:20 -0800443 abstract TypeMirror parameterType(TypeMirror type, DaggerElements elements);
ronshapiroe05f9212017-08-08 11:22:11 -0700444 abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
445 }
446
447 private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
448 return isRawTypePubliclyAccessible(instanceType)
449 ? instance
450 : CodeBlock.of("(($T) $L)", instanceType, instance);
451 }
452
ronshapiro5f059592017-12-07 07:18:33 -0800453 private enum CheckNotNullPolicy {
454 IGNORE, CHECK_FOR_NULL;
455 CodeBlock checkForNull(CodeBlock maybeNull) {
456 if (this.equals(IGNORE)) {
457 return maybeNull;
458 }
459 return checkNotNullProvidesMethod(maybeNull);
460 }
461
462 static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
463 return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
464 }
465 }
466
ronshapiro61bdbb32018-12-21 12:01:20 -0800467 private static InjectionMethod methodProxy(
468 ClassName proxyEnclosingClass,
ronshapiro5f059592017-12-07 07:18:33 -0800469 ExecutableElement method,
470 String methodName,
471 ReceiverAccessibility receiverAccessibility,
ronshapiro61bdbb32018-12-21 12:01:20 -0800472 CheckNotNullPolicy checkNotNullPolicy,
473 DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700474 TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
ronshapiro61bdbb32018-12-21 12:01:20 -0800475 InjectionMethod.Builder injectionMethod =
476 InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
477 ParameterSpec instance = null;
ronshapiroe05f9212017-08-08 11:22:11 -0700478 if (!method.getModifiers().contains(STATIC)) {
ronshapiro61bdbb32018-12-21 12:01:20 -0800479 instance =
480 injectionMethod.addParameter(
481 "instance", receiverAccessibility.parameterType(enclosingType.asType(), elements));
ronshapiroe05f9212017-08-08 11:22:11 -0700482 }
ronshapiro61bdbb32018-12-21 12:01:20 -0800483
484 CodeBlock arguments = injectionMethod.copyParameters(method);
ronshapiroe05f9212017-08-08 11:22:11 -0700485 if (!method.getReturnType().getKind().equals(VOID)) {
ronshapiro61bdbb32018-12-21 12:01:20 -0800486 injectionMethod
487 .returnType(method.getReturnType())
488 .nullableAnnotation(getNullableType(method));
489 injectionMethod.methodBodyBuilder().add("return ");
ronshapiroe05f9212017-08-08 11:22:11 -0700490 }
ronshapiro5f059592017-12-07 07:18:33 -0800491 CodeBlock.Builder proxyInvocation = CodeBlock.builder();
ronshapiroe05f9212017-08-08 11:22:11 -0700492 if (method.getModifiers().contains(STATIC)) {
ronshapiro5f059592017-12-07 07:18:33 -0800493 proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType())));
ronshapiroe05f9212017-08-08 11:22:11 -0700494 } else {
ronshapiro61bdbb32018-12-21 12:01:20 -0800495 injectionMethod.copyTypeParameters(enclosingType);
ronshapiro5f059592017-12-07 07:18:33 -0800496 proxyInvocation.add(
ronshapiro61bdbb32018-12-21 12:01:20 -0800497 receiverAccessibility.potentiallyCast(
498 CodeBlock.of("$N", instance), enclosingType.asType()));
ronshapiroe05f9212017-08-08 11:22:11 -0700499 }
ronshapiro61bdbb32018-12-21 12:01:20 -0800500
501 injectionMethod
502 .copyTypeParameters(method)
503 .copyThrows(method);
ronshapiroe05f9212017-08-08 11:22:11 -0700504
ronshapiro5f059592017-12-07 07:18:33 -0800505 proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments);
ronshapiro61bdbb32018-12-21 12:01:20 -0800506 injectionMethod
507 .methodBodyBuilder()
508 .add(checkNotNullPolicy.checkForNull(proxyInvocation.build()))
509 .add(";");
510 return injectionMethod.build();
ronshapiroe05f9212017-08-08 11:22:11 -0700511 }
512
ronshapiro61bdbb32018-12-21 12:01:20 -0800513 private static InjectionMethod fieldProxy(
514 ClassName proxyEnclosingClass,
515 VariableElement field,
516 String methodName,
517 DaggerElements elements) {
ronshapiroe05f9212017-08-08 11:22:11 -0700518 TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
ronshapiro61bdbb32018-12-21 12:01:20 -0800519 InjectionMethod.Builder injectionMethod =
520 InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
521 injectionMethod.copyTypeParameters(enclosingType);
ronshapiroe05f9212017-08-08 11:22:11 -0700522
ronshapiro61bdbb32018-12-21 12:01:20 -0800523 ParameterSpec instance =
524 injectionMethod.addParameter("instance", accessibleType(enclosingType.asType(), elements));
525 CodeBlock parameter = injectionMethod.copyParameter(field);
526 injectionMethod
527 .methodBodyBuilder()
528 .addStatement(
529 "$L.$L = $L",
530 instanceWithPotentialCast(CodeBlock.of("$N", instance), enclosingType.asType()),
ronshapiroe05f9212017-08-08 11:22:11 -0700531 field.getSimpleName(),
ronshapiro61bdbb32018-12-21 12:01:20 -0800532 parameter);
533 return injectionMethod.build();
ronshapiroe05f9212017-08-08 11:22:11 -0700534 }
535}