• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package dagger.internal.codegen.writing;
18 
19 import static androidx.room.compiler.processing.XTypeKt.isVoid;
20 import static com.google.common.base.Preconditions.checkArgument;
21 import static com.google.common.base.Preconditions.checkState;
22 import static com.google.common.collect.Iterables.getOnlyElement;
23 import static com.squareup.javapoet.MethodSpec.methodBuilder;
24 import static com.squareup.javapoet.TypeSpec.classBuilder;
25 import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
26 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
27 import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
28 import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
29 import static dagger.internal.codegen.xprocessing.MethodSpecs.overriding;
30 import static javax.lang.model.element.Modifier.FINAL;
31 import static javax.lang.model.element.Modifier.PRIVATE;
32 import static javax.lang.model.element.Modifier.PUBLIC;
33 import static javax.lang.model.element.Modifier.STATIC;
34 
35 import androidx.room.compiler.processing.XMethodElement;
36 import androidx.room.compiler.processing.XType;
37 import com.google.common.collect.ImmutableMap;
38 import com.google.common.collect.ImmutableSet;
39 import com.google.common.collect.Maps;
40 import com.google.common.collect.Sets;
41 import com.squareup.javapoet.CodeBlock;
42 import com.squareup.javapoet.FieldSpec;
43 import com.squareup.javapoet.MethodSpec;
44 import com.squareup.javapoet.ParameterSpec;
45 import com.squareup.javapoet.TypeName;
46 import com.squareup.javapoet.TypeSpec;
47 import dagger.internal.Preconditions;
48 import dagger.internal.codegen.base.UniqueNameSet;
49 import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
50 import dagger.internal.codegen.binding.ComponentDescriptor;
51 import dagger.internal.codegen.binding.ComponentRequirement;
52 import dagger.internal.codegen.binding.ComponentRequirement.NullPolicy;
53 import dagger.internal.codegen.compileroption.CompilerOptions;
54 import dagger.internal.codegen.javapoet.TypeNames;
55 import dagger.internal.codegen.xprocessing.XElements;
56 import java.util.Optional;
57 import java.util.Set;
58 import java.util.stream.Stream;
59 import javax.inject.Inject;
60 import javax.lang.model.element.Modifier;
61 
62 /** Factory for creating {@link ComponentCreatorImplementation} instances. */
63 final class ComponentCreatorImplementationFactory {
64 
65   private final CompilerOptions compilerOptions;
66   private final ComponentImplementation componentImplementation;
67 
68   @Inject
ComponentCreatorImplementationFactory( CompilerOptions compilerOptions, ComponentImplementation componentImplementation)69   ComponentCreatorImplementationFactory(
70       CompilerOptions compilerOptions, ComponentImplementation componentImplementation) {
71     this.compilerOptions = compilerOptions;
72     this.componentImplementation = componentImplementation;
73   }
74 
75   /** Returns a new creator implementation for the given component, if necessary. */
create()76   Optional<ComponentCreatorImplementation> create() {
77     if (!componentImplementation.componentDescriptor().hasCreator()) {
78       return Optional.empty();
79     }
80 
81     Optional<ComponentCreatorDescriptor> creatorDescriptor =
82         componentImplementation.componentDescriptor().creatorDescriptor();
83 
84     Builder builder =
85         creatorDescriptor.isPresent()
86             ? new BuilderForCreatorDescriptor(creatorDescriptor.get())
87             : new BuilderForGeneratedRootComponentBuilder();
88     return Optional.of(builder.build());
89   }
90 
91   /** Base class for building a creator implementation. */
92   private abstract class Builder {
93     private final TypeSpec.Builder classBuilder =
94         classBuilder(componentImplementation.getCreatorName());
95     private final UniqueNameSet fieldNames = new UniqueNameSet();
96     private ImmutableMap<ComponentRequirement, FieldSpec> fields;
97 
98     /** Builds the {@link ComponentCreatorImplementation}. */
build()99     ComponentCreatorImplementation build() {
100       setModifiers();
101       setSupertype();
102       addConstructor();
103       this.fields = addFields();
104       addSetterMethods();
105       addFactoryMethod();
106       return ComponentCreatorImplementation.create(
107           classBuilder.build(), componentImplementation.getCreatorName(), fields);
108     }
109 
110     /** Returns the descriptor for the component. */
componentDescriptor()111     final ComponentDescriptor componentDescriptor() {
112       return componentImplementation.componentDescriptor();
113     }
114 
115     /**
116      * The set of requirements that must be passed to the component's constructor in the order
117      * they must be passed.
118      */
componentConstructorRequirements()119     final ImmutableSet<ComponentRequirement> componentConstructorRequirements() {
120       return componentImplementation.graph().componentRequirements();
121     }
122 
123     /** Returns the requirements that have setter methods on the creator type. */
setterMethods()124     abstract ImmutableSet<ComponentRequirement> setterMethods();
125 
126     /**
127      * Returns the component requirements that have factory method parameters, mapped to the name
128      * for that parameter.
129      */
factoryMethodParameters()130     abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters();
131 
132     /**
133      * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status
134      * for each requirement indicating what's needed for that requirement in the implementation
135      * class currently being generated.
136      */
userSettableRequirements()137     abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements();
138 
139     /**
140      * Component requirements that are both settable by the creator and needed to construct the
141      * component.
142      */
neededUserSettableRequirements()143     private Set<ComponentRequirement> neededUserSettableRequirements() {
144       return Sets.intersection(
145           userSettableRequirements().keySet(), componentConstructorRequirements());
146     }
147 
setModifiers()148     private void setModifiers() {
149       visibility().ifPresent(classBuilder::addModifiers);
150       classBuilder.addModifiers(STATIC, FINAL);
151     }
152 
153     /** Returns the visibility modifier the generated class should have, if any. */
visibility()154     protected abstract Optional<Modifier> visibility();
155 
156     /** Sets the superclass being extended or interface being implemented for this creator. */
setSupertype()157     protected abstract void setSupertype();
158 
159     /** Adds a constructor for the creator type, if needed. */
addConstructor()160     protected void addConstructor() {
161       MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(PRIVATE);
162       componentImplementation
163           .creatorComponentFields()
164           .forEach(
165               field -> {
166                 fieldNames.claim(field.name);
167                 classBuilder.addField(field);
168                 constructor.addParameter(field.type, field.name);
169                 constructor.addStatement("this.$1N = $1N", field);
170               });
171       classBuilder.addMethod(constructor.build());
172     }
173 
addFields()174     private ImmutableMap<ComponentRequirement, FieldSpec> addFields() {
175       // Fields in an abstract creator class need to be visible from subclasses.
176       ImmutableMap<ComponentRequirement, FieldSpec> result =
177           Maps.toMap(
178               Sets.intersection(neededUserSettableRequirements(), setterMethods()),
179               requirement ->
180                   FieldSpec.builder(
181                           requirement.type().getTypeName(),
182                           fieldNames.getUniqueName(requirement.variableName()),
183                           PRIVATE)
184                       .build());
185       classBuilder.addFields(result.values());
186       return result;
187     }
188 
addSetterMethods()189     private void addSetterMethods() {
190       Maps.filterKeys(userSettableRequirements(), setterMethods()::contains)
191           .forEach(
192               (requirement, status) ->
193                   createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod));
194     }
195 
196     /** Creates a new setter method builder, with no method body, for the given requirement. */
setterMethodBuilder(ComponentRequirement requirement)197     protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement);
198 
createSetterMethod( ComponentRequirement requirement, RequirementStatus status)199     private Optional<MethodSpec> createSetterMethod(
200         ComponentRequirement requirement, RequirementStatus status) {
201       switch (status) {
202         case NEEDED:
203           return Optional.of(normalSetterMethod(requirement));
204         case UNNEEDED:
205           // If this is a generated Builder, then remove the setter methods for modules that don't
206           // require an instance.
207           if (!componentDescriptor().creatorDescriptor().isPresent()
208                   && !requirement.requiresModuleInstance()) {
209             return Optional.empty();
210           }
211           // TODO(bcorso): Don't generate noop setters for any unneeded requirements.
212           // However, since this is a breaking change we can at least avoid trying
213           // to generate noop setters for impossible cases like when the requirement type
214           // is in another package. This avoids unnecessary breakages in Dagger's generated
215           // due to the noop setters.
216           if (isElementAccessibleFrom(
217               requirement.typeElement(), componentImplementation.name().packageName())) {
218             return Optional.of(noopSetterMethod(requirement));
219           } else {
220             return Optional.empty();
221           }
222         case UNSETTABLE_REPEATED_MODULE:
223           return Optional.of(repeatedModuleSetterMethod(requirement));
224       }
225       throw new AssertionError();
226     }
227 
normalSetterMethod(ComponentRequirement requirement)228     private MethodSpec normalSetterMethod(ComponentRequirement requirement) {
229       MethodSpec.Builder method = setterMethodBuilder(requirement);
230       ParameterSpec parameter = parameter(method.build());
231       method.addStatement(
232           "this.$N = $L",
233           fields.get(requirement),
234           requirement.nullPolicy().equals(NullPolicy.ALLOW)
235               ? CodeBlock.of("$N", parameter)
236               : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
237       return maybeReturnThis(method);
238     }
239 
noopSetterMethod(ComponentRequirement requirement)240     private MethodSpec noopSetterMethod(ComponentRequirement requirement) {
241       MethodSpec.Builder method = setterMethodBuilder(requirement);
242       ParameterSpec parameter = parameter(method.build());
243       method
244           .addAnnotation(Deprecated.class)
245           .addJavadoc(
246               "@deprecated This module is declared, but an instance is not used in the component. "
247                   + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
248           .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
249       return maybeReturnThis(method);
250     }
251 
repeatedModuleSetterMethod(ComponentRequirement requirement)252     private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) {
253       return setterMethodBuilder(requirement)
254           .addStatement(
255               "throw new $T($T.format($S, $T.class.getCanonicalName()))",
256               UnsupportedOperationException.class,
257               String.class,
258               "%s cannot be set because it is inherited from the enclosing component",
259               TypeNames.rawTypeName(requirement.type().getTypeName()))
260           .build();
261     }
262 
parameter(MethodSpec method)263     private ParameterSpec parameter(MethodSpec method) {
264       return getOnlyElement(method.parameters);
265     }
266 
maybeReturnThis(MethodSpec.Builder method)267     private MethodSpec maybeReturnThis(MethodSpec.Builder method) {
268       MethodSpec built = method.build();
269       if (built.returnType.equals(TypeName.VOID)) {
270         return built;
271       }
272       return method.addStatement("return this").build();
273     }
274 
addFactoryMethod()275     private void addFactoryMethod() {
276       classBuilder.addMethod(factoryMethod());
277     }
278 
factoryMethod()279     MethodSpec factoryMethod() {
280       MethodSpec.Builder factoryMethod = factoryMethodBuilder();
281       factoryMethod
282           .returns(componentDescriptor().typeElement().getClassName())
283           .addModifiers(PUBLIC);
284 
285       ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
286           factoryMethodParameters();
287       userSettableRequirements()
288           .keySet()
289           .forEach(
290               requirement -> {
291                 if (fields.containsKey(requirement)) {
292                   FieldSpec field = fields.get(requirement);
293                   addNullHandlingForField(requirement, field, factoryMethod);
294                 } else if (factoryMethodParameters.containsKey(requirement)) {
295                   String parameterName = factoryMethodParameters.get(requirement);
296                   addNullHandlingForParameter(requirement, parameterName, factoryMethod);
297                 }
298               });
299       factoryMethod.addStatement(
300           "return new $T($L)",
301           componentImplementation.name(),
302           componentConstructorArgs(factoryMethodParameters));
303       return factoryMethod.build();
304     }
305 
addNullHandlingForField( ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod)306     private void addNullHandlingForField(
307         ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
308       switch (requirement.nullPolicy()) {
309         case NEW:
310           checkState(requirement.kind().isModule());
311           factoryMethod
312               .beginControlFlow("if ($N == null)", field)
313               .addStatement("this.$N = $L", field, newModuleInstance(requirement))
314               .endControlFlow();
315           break;
316         case THROW:
317           // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for
318           // @BindsInstance requirements, but that's not easily proguardable.
319           factoryMethod.addStatement(
320               "$T.checkBuilderRequirement($N, $T.class)",
321               Preconditions.class,
322               field,
323               TypeNames.rawTypeName(field.type));
324           break;
325         case ALLOW:
326           break;
327       }
328     }
329 
addNullHandlingForParameter( ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod)330     private void addNullHandlingForParameter(
331         ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
332       if (!requirement.nullPolicy().equals(NullPolicy.ALLOW)) {
333         // Factory method parameters are always required unless they are a nullable
334         // binds-instance (i.e. ALLOW)
335         factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter);
336       }
337     }
338 
339     /** Returns a builder for the creator's factory method. */
factoryMethodBuilder()340     protected abstract MethodSpec.Builder factoryMethodBuilder();
341 
componentConstructorArgs( ImmutableMap<ComponentRequirement, String> factoryMethodParameters)342     private CodeBlock componentConstructorArgs(
343         ImmutableMap<ComponentRequirement, String> factoryMethodParameters) {
344       return Stream.concat(
345               componentImplementation.creatorComponentFields().stream()
346                   .map(field -> CodeBlock.of("$N", field)),
347               componentConstructorRequirements().stream()
348                   .map(
349                       requirement -> {
350                         if (fields.containsKey(requirement)) {
351                           return CodeBlock.of("$N", fields.get(requirement));
352                         } else if (factoryMethodParameters.containsKey(requirement)) {
353                           return CodeBlock.of("$L", factoryMethodParameters.get(requirement));
354                         } else {
355                           return newModuleInstance(requirement);
356                         }
357                       }))
358           .collect(toParametersCodeBlock());
359     }
360 
newModuleInstance(ComponentRequirement requirement)361     private CodeBlock newModuleInstance(ComponentRequirement requirement) {
362       checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here
363       return ModuleProxies.newModuleInstance(
364           requirement.typeElement(), componentImplementation.getCreatorName());
365     }
366   }
367 
368   /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
369   private final class BuilderForCreatorDescriptor extends Builder {
370     final ComponentCreatorDescriptor creatorDescriptor;
371 
372     BuilderForCreatorDescriptor(ComponentCreatorDescriptor creatorDescriptor) {
373       this.creatorDescriptor = creatorDescriptor;
374     }
375 
376     @Override
377     protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
378       return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus);
379     }
380 
381     @Override
382     protected Optional<Modifier> visibility() {
383       return Optional.of(PRIVATE);
384     }
385 
386     @Override
387     protected void setSupertype() {
388       addSupertype(super.classBuilder, creatorDescriptor.typeElement());
389     }
390 
391     @Override
392     protected void addConstructor() {
393       if (!componentImplementation.creatorComponentFields().isEmpty()) {
394         super.addConstructor();
395       }
396     }
397 
398     @Override
399     protected ImmutableSet<ComponentRequirement> setterMethods() {
400       return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet());
401     }
402 
403     @Override
404     protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
405       return ImmutableMap.copyOf(
406           Maps.transformValues(creatorDescriptor.factoryParameters(), XElements::getSimpleName));
407     }
408 
409     private XType creatorType() {
410       return creatorDescriptor.typeElement().getType();
411     }
412 
413     @Override
414     protected MethodSpec.Builder factoryMethodBuilder() {
415       return overriding(creatorDescriptor.factoryMethod(), creatorType());
416     }
417 
418     private RequirementStatus requirementStatus(ComponentRequirement requirement) {
419       if (isRepeatedModule(requirement)) {
420         return RequirementStatus.UNSETTABLE_REPEATED_MODULE;
421       }
422 
423       return componentConstructorRequirements().contains(requirement)
424           ? RequirementStatus.NEEDED
425           : RequirementStatus.UNNEEDED;
426     }
427 
428     /**
429      * Returns whether the given requirement is for a repeat of a module inherited from an ancestor
430      * component. This creator is not allowed to set such a module.
431      */
432     final boolean isRepeatedModule(ComponentRequirement requirement) {
433       return !componentConstructorRequirements().contains(requirement)
434           && !isOwnedModule(requirement);
435     }
436 
437     /**
438      * Returns whether the given {@code requirement} is for a module type owned by the component.
439      */
440     private boolean isOwnedModule(ComponentRequirement requirement) {
441       return componentImplementation.graph().ownedModuleTypes().contains(requirement.typeElement());
442     }
443 
444     @Override
445     protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
446       XMethodElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
447       MethodSpec.Builder method = overriding(supertypeMethod, creatorType());
448       if (!isVoid(supertypeMethod.getReturnType())) {
449         // Take advantage of covariant returns so that we don't have to worry about type variables
450         method.returns(componentImplementation.getCreatorName());
451       }
452       return method;
453     }
454   }
455 
456   /**
457    * Builder for a component builder class that is automatically generated for a root component that
458    * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}).
459    */
460   private final class BuilderForGeneratedRootComponentBuilder extends Builder {
461 
462     @Override
463     protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
464       return Maps.toMap(
465           setterMethods(),
466           requirement ->
467               componentConstructorRequirements().contains(requirement)
468                   ? RequirementStatus.NEEDED
469                   : RequirementStatus.UNNEEDED);
470     }
471 
472     @Override
473     protected Optional<Modifier> visibility() {
474       return componentImplementation.componentDescriptor().typeElement().isPublic()
475           ? Optional.of(PUBLIC)
476           : Optional.empty();
477     }
478 
479     @Override
480     protected void setSupertype() {
481       // There's never a supertype for a root component auto-generated builder type.
482     }
483 
484     @Override
485     protected ImmutableSet<ComponentRequirement> setterMethods() {
486       return componentDescriptor().dependenciesAndConcreteModules();
487     }
488 
489     @Override
490     protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
491       return ImmutableMap.of();
492     }
493 
494     @Override
495     protected MethodSpec.Builder factoryMethodBuilder() {
496       return methodBuilder("build");
497     }
498 
499     @Override
500     protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
501       String name = simpleVariableName(requirement.typeElement().getClassName());
502       return methodBuilder(name)
503           .addModifiers(PUBLIC)
504           .addParameter(requirement.type().getTypeName(), name)
505           .returns(componentImplementation.getCreatorName());
506     }
507   }
508 
509   /** Enumeration of statuses a component requirement may have in a creator. */
510   enum RequirementStatus {
511     /** An instance is needed to create the component. */
512     NEEDED,
513 
514     /**
515      * An instance is not needed to create the component, but the requirement is for a module owned
516      * by the component. Setting the requirement is a no-op and any setter method should be marked
517      * deprecated on the generated type as a warning to the user.
518      */
519     UNNEEDED,
520 
521     /**
522      * The requirement may not be set in this creator because the module it is for is already
523      * inherited from an ancestor component. Any setter method for it should throw an exception.
524      */
525     UNSETTABLE_REPEATED_MODULE,
526     ;
527   }
528 }
529