• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.writing;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
21 import static com.squareup.javapoet.MethodSpec.methodBuilder;
22 import static com.squareup.javapoet.TypeSpec.classBuilder;
23 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameters;
24 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
25 import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
26 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
27 import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
28 import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
29 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
30 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
32 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
33 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
34 import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
35 import static dagger.internal.codegen.model.BindingKind.INJECTION;
36 import static dagger.internal.codegen.model.BindingKind.PROVISION;
37 import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
38 import static javax.lang.model.element.Modifier.FINAL;
39 import static javax.lang.model.element.Modifier.PRIVATE;
40 import static javax.lang.model.element.Modifier.PUBLIC;
41 import static javax.lang.model.element.Modifier.STATIC;
42 
43 import androidx.room.compiler.processing.XElement;
44 import androidx.room.compiler.processing.XExecutableParameterElement;
45 import androidx.room.compiler.processing.XFiler;
46 import androidx.room.compiler.processing.XProcessingEnv;
47 import com.google.common.collect.ImmutableList;
48 import com.google.common.collect.ImmutableMap;
49 import com.google.common.collect.Lists;
50 import com.squareup.javapoet.AnnotationSpec;
51 import com.squareup.javapoet.ClassName;
52 import com.squareup.javapoet.CodeBlock;
53 import com.squareup.javapoet.FieldSpec;
54 import com.squareup.javapoet.MethodSpec;
55 import com.squareup.javapoet.ParameterSpec;
56 import com.squareup.javapoet.TypeName;
57 import com.squareup.javapoet.TypeSpec;
58 import dagger.internal.Factory;
59 import dagger.internal.codegen.base.SourceFileGenerator;
60 import dagger.internal.codegen.base.UniqueNameSet;
61 import dagger.internal.codegen.binding.Binding;
62 import dagger.internal.codegen.binding.ProvisionBinding;
63 import dagger.internal.codegen.binding.SourceFiles;
64 import dagger.internal.codegen.compileroption.CompilerOptions;
65 import dagger.internal.codegen.javapoet.TypeNames;
66 import dagger.internal.codegen.model.BindingKind;
67 import dagger.internal.codegen.model.DaggerAnnotation;
68 import dagger.internal.codegen.model.DependencyRequest;
69 import dagger.internal.codegen.model.Key;
70 import dagger.internal.codegen.model.Scope;
71 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
72 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
73 import java.util.List;
74 import java.util.Optional;
75 import java.util.stream.Stream;
76 import javax.inject.Inject;
77 
78 /**
79  * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for {@link
80  * Inject} constructors.
81  */
82 public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
83   private final CompilerOptions compilerOptions;
84   private final SourceFiles sourceFiles;
85 
86   @Inject
FactoryGenerator( XFiler filer, CompilerOptions compilerOptions, SourceFiles sourceFiles, XProcessingEnv processingEnv)87   FactoryGenerator(
88       XFiler filer,
89       CompilerOptions compilerOptions,
90       SourceFiles sourceFiles,
91       XProcessingEnv processingEnv) {
92     super(filer, processingEnv);
93     this.compilerOptions = compilerOptions;
94     this.sourceFiles = sourceFiles;
95   }
96 
97   @Override
originatingElement(ProvisionBinding binding)98   public XElement originatingElement(ProvisionBinding binding) {
99     // we only create factories for bindings that have a binding element
100     return binding.bindingElement().get();
101   }
102 
103   @Override
topLevelTypes(ProvisionBinding binding)104   public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) {
105     // We don't want to write out resolved bindings -- we want to write out the generic version.
106     checkArgument(!binding.unresolved().isPresent());
107     checkArgument(binding.bindingElement().isPresent());
108 
109     if (binding.kind() == BindingKind.DELEGATE) {
110       return ImmutableList.of();
111     }
112 
113     return ImmutableList.of(factoryBuilder(binding));
114   }
115 
factoryBuilder(ProvisionBinding binding)116   private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
117     TypeSpec.Builder factoryBuilder =
118         classBuilder(generatedClassNameForBinding(binding))
119             .addModifiers(PUBLIC, FINAL)
120             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
121 
122     if (binding.kind() == BindingKind.INJECTION
123         || binding.kind() == BindingKind.ASSISTED_INJECTION
124         || binding.kind() == BindingKind.PROVISION) {
125       factoryBuilder.addAnnotation(scopeMetadataAnnotation(binding));
126       factoryBuilder.addAnnotation(qualifierMetadataAnnotation(binding));
127     }
128 
129     factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface);
130     addConstructorAndFields(binding, factoryBuilder);
131     factoryBuilder.addMethod(getMethod(binding));
132     addCreateMethod(binding, factoryBuilder);
133 
134     factoryBuilder.addMethod(ProvisionMethod.create(binding, compilerOptions));
135     gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
136 
137     return factoryBuilder;
138   }
139 
addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)140   private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
141     if (FactoryCreationStrategy.of(binding) == FactoryCreationStrategy.SINGLETON_INSTANCE) {
142       return;
143     }
144     // TODO(bcorso): Make the constructor private?
145     MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
146     constructorParams(binding).forEach(
147         param -> {
148           constructor.addParameter(param).addStatement("this.$1N = $1N", param);
149           factoryBuilder.addField(
150               FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
151         });
152     factoryBuilder.addMethod(constructor.build());
153   }
154 
constructorParams(ProvisionBinding binding)155   private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
156     ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
157     moduleParameter(binding).ifPresent(params::add);
158     frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
159     return params.build();
160   }
161 
moduleParameter(ProvisionBinding binding)162   private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
163     if (binding.requiresModuleInstance()) {
164       // TODO(bcorso, dpb): Should this use contributingModule()?
165       TypeName type = binding.bindingTypeElement().get().getType().getTypeName();
166       return Optional.of(ParameterSpec.builder(type, "module").build());
167     }
168     return Optional.empty();
169   }
170 
frameworkFields(ProvisionBinding binding)171   private ImmutableMap<DependencyRequest, FieldSpec> frameworkFields(ProvisionBinding binding) {
172     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
173     // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
174     moduleParameter(binding).ifPresent(module -> uniqueFieldNames.claim(module.name));
175     // We avoid Maps.transformValues here because it would implicitly depend on the order in which
176     // the transform function is evaluated on each entry in the map.
177     ImmutableMap.Builder<DependencyRequest, FieldSpec> builder = ImmutableMap.builder();
178     generateBindingFieldsForDependencies(binding).forEach(
179         (dependency, field) ->
180             builder.put(dependency,
181                 FieldSpec.builder(
182                         field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
183                     .build()));
184     return builder.build();
185   }
186 
addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)187   private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
188     // If constructing a factory for @Inject or @Provides bindings, we use a static create method
189     // so that generated components can avoid having to refer to the generic types
190     // of the factory.  (Otherwise they may have visibility problems referring to the types.)
191     MethodSpec.Builder createMethodBuilder =
192         methodBuilder("create")
193             .addModifiers(PUBLIC, STATIC)
194             .returns(parameterizedGeneratedTypeNameForBinding(binding))
195             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
196 
197     switch (FactoryCreationStrategy.of(binding)) {
198       case SINGLETON_INSTANCE:
199         FieldSpec.Builder instanceFieldBuilder =
200             FieldSpec.builder(
201                     generatedClassNameForBinding(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
202                 .initializer("new $T()", generatedClassNameForBinding(binding));
203 
204         if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
205           // If the factory has type parameters, ignore them in the field declaration & initializer
206           instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
207           createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
208         }
209 
210         ClassName instanceHolderName =
211             generatedClassNameForBinding(binding).nestedClass("InstanceHolder");
212         createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName);
213         factoryBuilder.addType(
214             TypeSpec.classBuilder(instanceHolderName)
215                 .addModifiers(PRIVATE, STATIC, FINAL)
216                 .addField(instanceFieldBuilder.build())
217                 .build());
218         break;
219       case CLASS_CONSTRUCTOR:
220         List<ParameterSpec> params = constructorParams(binding);
221         createMethodBuilder.addParameters(params);
222         createMethodBuilder.addStatement(
223             "return new $T($L)",
224             parameterizedGeneratedTypeNameForBinding(binding),
225             makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
226         break;
227       default:
228         throw new AssertionError();
229     }
230     factoryBuilder.addMethod(createMethodBuilder.build());
231   }
232 
getMethod(ProvisionBinding binding)233   private MethodSpec getMethod(ProvisionBinding binding) {
234     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
235     ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
236     frameworkFields.values().forEach(field -> uniqueFieldNames.claim(field.name));
237     ImmutableMap<XExecutableParameterElement, ParameterSpec> assistedParameters =
238         assistedParameters(binding).stream()
239             .collect(
240                 toImmutableMap(
241                     parameter -> parameter,
242                     parameter ->
243                         ParameterSpec.builder(
244                                 parameter.getType().getTypeName(),
245                                 uniqueFieldNames.getUniqueName(parameter.getJvmName()))
246                             .build()));
247     TypeName providedTypeName = providedTypeName(binding);
248     MethodSpec.Builder getMethod =
249         methodBuilder("get")
250             .addModifiers(PUBLIC)
251             .addParameters(assistedParameters.values());
252 
253     if (factoryTypeName(binding).isPresent()) {
254       getMethod.addAnnotation(Override.class);
255     }
256     CodeBlock invokeNewInstance =
257         ProvisionMethod.invoke(
258             binding,
259             request ->
260                 sourceFiles.frameworkTypeUsageStatement(
261                     CodeBlock.of("$N", frameworkFields.get(request)), request.kind()),
262             param -> assistedParameters.get(param).name,
263             generatedClassNameForBinding(binding),
264             moduleParameter(binding).map(module -> CodeBlock.of("$N", module)),
265             compilerOptions);
266 
267     if (binding.kind().equals(PROVISION)) {
268       binding
269           .nullability()
270           .nullableAnnotations()
271           .forEach(getMethod::addAnnotation);
272       getMethod.returns(providedTypeName);
273       getMethod.addStatement("return $L", invokeNewInstance);
274     } else if (!binding.injectionSites().isEmpty()) {
275       CodeBlock instance = CodeBlock.of("instance");
276       getMethod
277           .returns(providedTypeName)
278           .addStatement("$T $L = $L", providedTypeName, instance, invokeNewInstance)
279           .addCode(
280               InjectionSiteMethod.invokeAll(
281                   binding.injectionSites(),
282                   generatedClassNameForBinding(binding),
283                   instance,
284                   binding.key().type().xprocessing(),
285                   sourceFiles.frameworkFieldUsages(binding.dependencies(), frameworkFields)::get))
286           .addStatement("return $L", instance);
287 
288     } else {
289       getMethod
290           .returns(providedTypeName)
291           .addStatement("return $L", invokeNewInstance);
292     }
293     return getMethod.build();
294   }
295 
scopeMetadataAnnotation(ProvisionBinding binding)296   private AnnotationSpec scopeMetadataAnnotation(ProvisionBinding binding) {
297     AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.SCOPE_METADATA);
298     binding.scope()
299         .map(Scope::scopeAnnotation)
300         .map(DaggerAnnotation::className)
301         .map(ClassName::canonicalName)
302         .ifPresent(scopeCanonicalName -> builder.addMember("value", "$S", scopeCanonicalName));
303     return builder.build();
304   }
305 
qualifierMetadataAnnotation(ProvisionBinding binding)306   private AnnotationSpec qualifierMetadataAnnotation(ProvisionBinding binding) {
307     AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.QUALIFIER_METADATA);
308     // Collect all qualifiers on the binding itself or its dependencies
309     Stream.concat(
310             Stream.of(binding.key()),
311             binding.provisionDependencies().stream().map(DependencyRequest::key))
312         .map(Key::qualifier)
313         .flatMap(presentValues())
314         .map(DaggerAnnotation::className)
315         .map(ClassName::canonicalName)
316         .distinct()
317         .forEach(qualifier -> builder.addMember("value", "$S", qualifier));
318     return builder.build();
319   }
320 
providedTypeName(ProvisionBinding binding)321   private static TypeName providedTypeName(ProvisionBinding binding) {
322     return binding.contributedType().getTypeName();
323   }
324 
factoryTypeName(ProvisionBinding binding)325   private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
326     return binding.kind() == BindingKind.ASSISTED_INJECTION
327         ? Optional.empty()
328         : Optional.of(factoryOf(providedTypeName(binding)));
329   }
330 
toParameter(FieldSpec field)331   private static ParameterSpec toParameter(FieldSpec field) {
332     return ParameterSpec.builder(field.type, field.name).build();
333   }
334 
335   /** The strategy for getting an instance of a factory for a {@link Binding}. */
336   private enum FactoryCreationStrategy {
337     /** The factory class is a single instance. */
338     SINGLETON_INSTANCE,
339     /** The factory must be created by calling the constructor. */
340     CLASS_CONSTRUCTOR;
341 
of(Binding binding)342     static FactoryCreationStrategy of(Binding binding) {
343       switch (binding.kind()) {
344         case DELEGATE:
345           throw new AssertionError("Delegate bindings don't have a factory.");
346         case PROVISION:
347           return binding.dependencies().isEmpty() && !binding.requiresModuleInstance()
348               ? SINGLETON_INSTANCE
349               : CLASS_CONSTRUCTOR;
350         case INJECTION:
351         case MULTIBOUND_SET:
352         case MULTIBOUND_MAP:
353           return binding.dependencies().isEmpty()
354               ? SINGLETON_INSTANCE
355               : CLASS_CONSTRUCTOR;
356         default:
357           return CLASS_CONSTRUCTOR;
358       }
359     }
360   }
361 }
362