• 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;
18 
19 import static com.google.auto.common.MoreTypes.asDeclared;
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.constructorBuilder;
24 import static com.squareup.javapoet.MethodSpec.methodBuilder;
25 import static com.squareup.javapoet.TypeSpec.classBuilder;
26 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
27 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
28 import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
29 import static javax.lang.model.element.Modifier.ABSTRACT;
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.PROTECTED;
33 import static javax.lang.model.element.Modifier.PUBLIC;
34 import static javax.lang.model.element.Modifier.STATIC;
35 
36 import com.google.common.collect.ImmutableMap;
37 import com.google.common.collect.ImmutableSet;
38 import com.google.common.collect.Maps;
39 import com.google.common.collect.Sets;
40 import com.squareup.javapoet.ClassName;
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.ComponentRequirement.NullPolicy;
49 import dagger.internal.codegen.javapoet.TypeNames;
50 import dagger.internal.codegen.langmodel.DaggerElements;
51 import dagger.internal.codegen.langmodel.DaggerTypes;
52 import java.util.Optional;
53 import java.util.Set;
54 import javax.inject.Inject;
55 import javax.lang.model.element.ExecutableElement;
56 import javax.lang.model.element.Modifier;
57 import javax.lang.model.type.DeclaredType;
58 import javax.lang.model.type.TypeKind;
59 
60 /** Factory for creating {@link ComponentCreatorImplementation} instances. */
61 final class ComponentCreatorImplementationFactory {
62 
63   private final DaggerElements elements;
64   private final DaggerTypes types;
65 
66   @Inject
ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types)67   ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types) {
68     this.elements = elements;
69     this.types = types;
70   }
71 
72   /** Returns a new creator implementation for the given component, if necessary. */
create( ComponentImplementation componentImplementation, Optional<BindingGraph> graph)73   Optional<ComponentCreatorImplementation> create(
74       ComponentImplementation componentImplementation, Optional<BindingGraph> graph) {
75     if (!componentImplementation.componentDescriptor().hasCreator()) {
76       return Optional.empty();
77     }
78 
79     Optional<ComponentCreatorDescriptor> creatorDescriptor =
80         componentImplementation.componentDescriptor().creatorDescriptor();
81 
82     if (componentImplementation.isAbstract()
83         && (hasNoSetterMethods(creatorDescriptor)
84             || componentImplementation.superclassImplementation().isPresent())) {
85       // 1. Factory-like creators (those with no setter methods) are only generated in concrete
86       //    components, because they only have a factory method and the factory method must call
87       //    a concrete component's constructor.
88       // 2. The component builder in ahead-of-time mode is generated with the base subcomponent
89       //    implementation, with the exception of the build method since that requires invoking the
90       //    constructor of a concrete component implementation. Intermediate component
91       //    implementations, because they still can't invoke the eventual constructor and have no
92       //    additional extensions to the builder, can ignore generating a builder implementation.
93       return Optional.empty();
94     }
95 
96     Builder builder =
97         creatorDescriptor.isPresent()
98             ? new BuilderForCreatorDescriptor(
99                 componentImplementation, creatorDescriptor.get(), graph)
100             : new BuilderForGeneratedRootComponentBuilder(componentImplementation);
101     return Optional.of(builder.build());
102   }
103 
hasNoSetterMethods( Optional<ComponentCreatorDescriptor> creatorDescriptor)104   private static boolean hasNoSetterMethods(
105       Optional<ComponentCreatorDescriptor> creatorDescriptor) {
106     return creatorDescriptor.filter(descriptor -> descriptor.setterMethods().isEmpty()).isPresent();
107   }
108 
109   /** Base class for building a creator implementation. */
110   private abstract class Builder {
111     final ComponentImplementation componentImplementation;
112     final ClassName className;
113     final TypeSpec.Builder classBuilder;
114 
115     private ImmutableMap<ComponentRequirement, FieldSpec> fields;
116 
Builder(ComponentImplementation componentImplementation)117     Builder(ComponentImplementation componentImplementation) {
118       this.componentImplementation = componentImplementation;
119       this.className = componentImplementation.getCreatorName();
120       this.classBuilder = classBuilder(className);
121     }
122 
123     /** Builds the {@link ComponentCreatorImplementation}. */
build()124     ComponentCreatorImplementation build() {
125       setModifiers();
126       setSupertype();
127       this.fields = getOrAddFields();
128       addConstructor();
129       addSetterMethods();
130       addFactoryMethod();
131       return ComponentCreatorImplementation.create(classBuilder.build(), className, fields);
132     }
133 
134     /** Returns the descriptor for the component. */
componentDescriptor()135     final ComponentDescriptor componentDescriptor() {
136       return componentImplementation.componentDescriptor();
137     }
138 
139     /**
140      * The set of requirements that must be passed to the component's constructor in the order
141      * they must be passed.
142      */
componentConstructorRequirements()143     final ImmutableSet<ComponentRequirement> componentConstructorRequirements() {
144       return componentImplementation.requirements();
145     }
146 
147     /** Returns the requirements that have setter methods on the creator type. */
setterMethods()148     abstract ImmutableSet<ComponentRequirement> setterMethods();
149 
150     /**
151      * Returns the component requirements that have factory method parameters, mapped to the name
152      * for that parameter.
153      */
factoryMethodParameters()154     abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters();
155 
156     /**
157      * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status
158      * for each requirement indicating what's needed for that requirement in the implementation
159      * class currently being generated.
160      */
userSettableRequirements()161     abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements();
162 
163     /**
164      * Component requirements that are both settable by the creator and needed to construct the
165      * component.
166      */
neededUserSettableRequirements()167     private Set<ComponentRequirement> neededUserSettableRequirements() {
168       return Sets.intersection(
169           userSettableRequirements().keySet(), componentConstructorRequirements());
170     }
171 
setModifiers()172     private void setModifiers() {
173       visibility().ifPresent(classBuilder::addModifiers);
174       if (!componentImplementation.isNested()) {
175         classBuilder.addModifiers(STATIC);
176       }
177       classBuilder.addModifiers(componentImplementation.isAbstract() ? ABSTRACT : FINAL);
178     }
179 
180     /** Returns the visibility modifier the generated class should have, if any. */
visibility()181     protected abstract Optional<Modifier> visibility();
182 
183     /** Sets the superclass being extended or interface being implemented for this creator. */
setSupertype()184     protected abstract void setSupertype();
185 
186     /** Adds a constructor for the creator type, if needed. */
addConstructor()187     protected abstract void addConstructor();
188 
getOrAddFields()189     private ImmutableMap<ComponentRequirement, FieldSpec> getOrAddFields() {
190       // If a base implementation is present, any fields are already defined there and don't need to
191       // be created in this implementation.
192       return componentImplementation
193           .baseCreatorImplementation()
194           .map(ComponentCreatorImplementation::fields)
195           .orElseGet(this::addFields);
196     }
197 
addFields()198     private ImmutableMap<ComponentRequirement, FieldSpec> addFields() {
199       // Fields in an abstract creator class need to be visible from subclasses.
200       Modifier modifier = componentImplementation.isAbstract() ? PROTECTED : PRIVATE;
201       UniqueNameSet fieldNames = new UniqueNameSet();
202       ImmutableMap<ComponentRequirement, FieldSpec> result =
203           Maps.toMap(
204               Sets.intersection(neededUserSettableRequirements(), setterMethods()),
205               requirement ->
206                   FieldSpec.builder(
207                           TypeName.get(requirement.type()),
208                           fieldNames.getUniqueName(requirement.variableName()),
209                           modifier)
210                       .build());
211       classBuilder.addFields(result.values());
212       return result;
213     }
214 
addSetterMethods()215     private void addSetterMethods() {
216       Maps.filterKeys(userSettableRequirements(), setterMethods()::contains)
217           .forEach(
218               (requirement, status) ->
219                   createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod));
220     }
221 
222     /** Creates a new setter method builder, with no method body, for the given requirement. */
setterMethodBuilder(ComponentRequirement requirement)223     protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement);
224 
createSetterMethod( ComponentRequirement requirement, RequirementStatus status)225     private Optional<MethodSpec> createSetterMethod(
226         ComponentRequirement requirement, RequirementStatus status) {
227       switch (status) {
228         case NEEDED:
229           return Optional.of(normalSetterMethod(requirement));
230         case UNNEEDED:
231           return Optional.of(noopSetterMethod(requirement));
232         case UNSETTABLE_REPEATED_MODULE:
233           return Optional.of(repeatedModuleSetterMethod(requirement));
234         case IMPLEMENTED_IN_SUPERTYPE:
235           return Optional.empty();
236       }
237       throw new AssertionError();
238     }
239 
normalSetterMethod(ComponentRequirement requirement)240     private MethodSpec normalSetterMethod(ComponentRequirement requirement) {
241       MethodSpec.Builder method = setterMethodBuilder(requirement);
242       ParameterSpec parameter = parameter(method.build());
243       method.addStatement(
244           "this.$N = $L",
245           fields.get(requirement),
246           requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)
247               ? CodeBlock.of("$N", parameter)
248               : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
249       return maybeReturnThis(method);
250     }
251 
noopSetterMethod(ComponentRequirement requirement)252     private MethodSpec noopSetterMethod(ComponentRequirement requirement) {
253       MethodSpec.Builder method = setterMethodBuilder(requirement);
254       ParameterSpec parameter = parameter(method.build());
255       method
256           .addAnnotation(Deprecated.class)
257           .addJavadoc(
258               "@deprecated This module is declared, but an instance is not used in the component. "
259                   + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
260           .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
261       return maybeReturnThis(method);
262     }
263 
repeatedModuleSetterMethod(ComponentRequirement requirement)264     private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) {
265       return setterMethodBuilder(requirement)
266           .addStatement(
267               "throw new $T($T.format($S, $T.class.getCanonicalName()))",
268               UnsupportedOperationException.class,
269               String.class,
270               "%s cannot be set because it is inherited from the enclosing component",
271               TypeNames.rawTypeName(TypeName.get(requirement.type())))
272           .build();
273     }
274 
parameter(MethodSpec method)275     private ParameterSpec parameter(MethodSpec method) {
276       return getOnlyElement(method.parameters);
277     }
278 
maybeReturnThis(MethodSpec.Builder method)279     private MethodSpec maybeReturnThis(MethodSpec.Builder method) {
280       MethodSpec built = method.build();
281       return built.returnType.equals(TypeName.VOID)
282           ? built
283           : method.addStatement("return this").build();
284     }
285 
addFactoryMethod()286     private void addFactoryMethod() {
287       if (!componentImplementation.isAbstract()) {
288         classBuilder.addMethod(factoryMethod());
289       }
290     }
291 
factoryMethod()292     MethodSpec factoryMethod() {
293       MethodSpec.Builder factoryMethod = factoryMethodBuilder();
294       factoryMethod
295           .returns(ClassName.get(componentDescriptor().typeElement()))
296           .addModifiers(PUBLIC);
297 
298       ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
299           factoryMethodParameters();
300       userSettableRequirements()
301           .keySet()
302           .forEach(
303               requirement -> {
304                 if (fields.containsKey(requirement)
305                     && componentConstructorRequirements().contains(requirement)) {
306                   // In AOT mode, there can be a field for a requirement even if the component's
307                   // constructor doesn't need it, because the base class for the creator was created
308                   // before the final graph for the component was known.
309                   FieldSpec field = fields.get(requirement);
310                   addNullHandlingForField(requirement, field, factoryMethod);
311                 } else if (factoryMethodParameters.containsKey(requirement)) {
312                   String parameterName = factoryMethodParameters.get(requirement);
313                   addNullHandlingForParameter(requirement, parameterName, factoryMethod);
314                 }
315               });
316       factoryMethod.addStatement(
317           "return new $T($L)",
318           componentImplementation.name(),
319           componentConstructorArgs(factoryMethodParameters));
320       return factoryMethod.build();
321     }
322 
addNullHandlingForField( ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod)323     private void addNullHandlingForField(
324         ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
325       switch (requirement.nullPolicy(elements, types)) {
326         case NEW:
327           checkState(requirement.kind().isModule());
328           factoryMethod
329               .beginControlFlow("if ($N == null)", field)
330               .addStatement("this.$N = $L", field, newModuleInstance(requirement))
331               .endControlFlow();
332           break;
333         case THROW:
334           // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for
335           // @BindsInstance requirements, but that's not easily proguardable.
336           factoryMethod.addStatement(
337               "$T.checkBuilderRequirement($N, $T.class)",
338               Preconditions.class,
339               field,
340               TypeNames.rawTypeName(field.type));
341           break;
342         case ALLOW:
343           break;
344       }
345     }
346 
addNullHandlingForParameter( ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod)347     private void addNullHandlingForParameter(
348         ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
349       if (!requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)) {
350         // Factory method parameters are always required unless they are a nullable
351         // binds-instance (i.e. ALLOW)
352         factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter);
353       }
354     }
355 
356     /** Returns a builder for the creator's factory method. */
factoryMethodBuilder()357     protected abstract MethodSpec.Builder factoryMethodBuilder();
358 
componentConstructorArgs( ImmutableMap<ComponentRequirement, String> factoryMethodParameters)359     private CodeBlock componentConstructorArgs(
360         ImmutableMap<ComponentRequirement, String> factoryMethodParameters) {
361       return componentConstructorRequirements().stream()
362           .map(
363               requirement -> {
364                 if (fields.containsKey(requirement)) {
365                   return CodeBlock.of("$N", fields.get(requirement));
366                 } else if (factoryMethodParameters.containsKey(requirement)) {
367                   return CodeBlock.of("$L", factoryMethodParameters.get(requirement));
368                 } else {
369                   return newModuleInstance(requirement);
370                 }
371               })
372           .collect(toParametersCodeBlock());
373     }
374 
newModuleInstance(ComponentRequirement requirement)375     private CodeBlock newModuleInstance(ComponentRequirement requirement) {
376       checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here
377       return ModuleProxies.newModuleInstance(requirement.typeElement(), className, elements);
378     }
379   }
380 
381   /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
382   private final class BuilderForCreatorDescriptor extends Builder {
383     final ComponentCreatorDescriptor creatorDescriptor;
384     private final Optional<BindingGraph> graph;
385 
386     BuilderForCreatorDescriptor(
387         ComponentImplementation componentImplementation,
388         ComponentCreatorDescriptor creatorDescriptor,
389         Optional<BindingGraph> graph) {
390       super(componentImplementation);
391       this.creatorDescriptor = creatorDescriptor;
392       this.graph = graph;
393     }
394 
395     @Override
396     protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
397       return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus);
398     }
399 
400     @Override
401     protected Optional<Modifier> visibility() {
402       if (componentImplementation.isAbstract()) {
403         // The component creator class of a top-level component implementation in ahead-of-time
404         // subcomponents mode must be public, not protected, because the creator's subclass will
405         // be a sibling of the component subclass implementation, not nested.
406         return Optional.of(componentImplementation.isNested() ? PROTECTED : PUBLIC);
407       }
408       return Optional.of(PRIVATE);
409     }
410 
411     @Override
412     protected void setSupertype() {
413       if (componentImplementation.baseCreatorImplementation().isPresent()) {
414         // If an abstract base implementation for this creator exists, extend that class.
415         classBuilder.superclass(componentImplementation.baseCreatorImplementation().get().name());
416       } else {
417         addSupertype(classBuilder, creatorDescriptor.typeElement());
418       }
419     }
420 
421     @Override
422     protected void addConstructor() {
423       // Just use the implicit no-arg public constructor.
424     }
425 
426     @Override
427     protected ImmutableSet<ComponentRequirement> setterMethods() {
428       return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet());
429     }
430 
431     @Override
432     protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
433       return ImmutableMap.copyOf(
434           Maps.transformValues(
435               creatorDescriptor.factoryParameters(),
436               element -> element.getSimpleName().toString()));
437     }
438 
439     private DeclaredType creatorType() {
440       return asDeclared(creatorDescriptor.typeElement().asType());
441     }
442 
443     @Override
444     protected MethodSpec.Builder factoryMethodBuilder() {
445       return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types);
446     }
447 
448     private RequirementStatus requirementStatus(ComponentRequirement requirement) {
449       // In ahead-of-time subcomponents mode, all builder methods are defined at the base
450       // implementation. The only case where a method needs to be overridden is for a repeated
451       // module, which is unknown at the point when a base implementation is generated. We do this
452       // at the root for simplicity (and as an aside, repeated modules are never used in google
453       // as of 11/28/18, and thus the additional cost of including these methods at the root is
454       // negligible).
455       if (isRepeatedModule(requirement)) {
456         return RequirementStatus.UNSETTABLE_REPEATED_MODULE;
457       }
458 
459       if (hasBaseCreatorImplementation()) {
460         return RequirementStatus.IMPLEMENTED_IN_SUPERTYPE;
461       }
462 
463       return componentConstructorRequirements().contains(requirement)
464           ? RequirementStatus.NEEDED
465           : RequirementStatus.UNNEEDED;
466     }
467 
468     /**
469      * Returns whether the given requirement is for a repeat of a module inherited from an ancestor
470      * component. This creator is not allowed to set such a module.
471      */
472     final boolean isRepeatedModule(ComponentRequirement requirement) {
473       return !componentConstructorRequirements().contains(requirement)
474           && !isOwnedModule(requirement);
475     }
476 
477     /**
478      * Returns whether the given {@code requirement} is for a module type owned by the component.
479      */
480     private boolean isOwnedModule(ComponentRequirement requirement) {
481       return graph.map(g -> g.ownedModuleTypes().contains(requirement.typeElement())).orElse(true);
482     }
483 
484     private boolean hasBaseCreatorImplementation() {
485       return !componentImplementation.isAbstract()
486           && componentImplementation.baseImplementation().isPresent();
487     }
488 
489     @Override
490     protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
491       ExecutableElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
492       MethodSpec.Builder method = MethodSpec.overriding(supertypeMethod, creatorType(), types);
493       if (!supertypeMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
494         // Take advantage of covariant returns so that we don't have to worry about type variables
495         method.returns(className);
496       }
497       return method;
498     }
499   }
500 
501   /**
502    * Builder for a component builder class that is automatically generated for a root component that
503    * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}).
504    */
505   private final class BuilderForGeneratedRootComponentBuilder extends Builder {
506     BuilderForGeneratedRootComponentBuilder(ComponentImplementation componentImplementation) {
507       super(componentImplementation);
508     }
509 
510     @Override
511     protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
512       return Maps.toMap(
513           setterMethods(),
514           requirement ->
515               componentConstructorRequirements().contains(requirement)
516                   ? RequirementStatus.NEEDED
517                   : RequirementStatus.UNNEEDED);
518     }
519 
520     @Override
521     protected Optional<Modifier> visibility() {
522       return componentImplementation
523           .componentDescriptor()
524           .typeElement()
525           .getModifiers()
526           .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
527     }
528 
529     @Override
530     protected void setSupertype() {
531       // There's never a supertype for a root component auto-generated builder type.
532     }
533 
534     @Override
535     protected void addConstructor() {
536       classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
537     }
538 
539     @Override
540     protected ImmutableSet<ComponentRequirement> setterMethods() {
541       return componentDescriptor().dependenciesAndConcreteModules();
542     }
543 
544     @Override
545     protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
546       return ImmutableMap.of();
547     }
548 
549     @Override
550     protected MethodSpec.Builder factoryMethodBuilder() {
551       return methodBuilder("build");
552     }
553 
554     @Override
555     protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
556       String name = simpleVariableName(requirement.typeElement());
557       return methodBuilder(name)
558           .addModifiers(PUBLIC)
559           .addParameter(TypeName.get(requirement.type()), name)
560           .returns(className);
561     }
562   }
563 
564   /** Enumeration of statuses a component requirement may have in a creator. */
565   enum RequirementStatus {
566     /** An instance is needed to create the component. */
567     NEEDED,
568 
569     /**
570      * An instance is not needed to create the component, but the requirement is for a module owned
571      * by the component. Setting the requirement is a no-op and any setter method should be marked
572      * deprecated on the generated type as a warning to the user.
573      */
574     UNNEEDED,
575 
576     /**
577      * The requirement may not be set in this creator because the module it is for is already
578      * inherited from an ancestor component. Any setter method for it should throw an exception.
579      */
580     UNSETTABLE_REPEATED_MODULE,
581 
582     /**
583      * The requirement is settable by the creator, but the setter method implementation already
584      * exists in a supertype.
585      */
586     IMPLEMENTED_IN_SUPERTYPE,
587     ;
588   }
589 }
590