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