• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.MoreElements.getLocalAndInheritedMethods;
20 import static com.google.auto.common.MoreTypes.asDeclared;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.base.Preconditions.checkState;
23 import static com.google.common.base.Predicates.in;
24 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
25 import static com.squareup.javapoet.MethodSpec.methodBuilder;
26 import static dagger.internal.codegen.BindingRequest.bindingRequest;
27 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
28 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.BUILDER_METHOD;
29 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD;
30 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
31 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CONSTRUCTOR;
32 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.INITIALIZE_METHOD;
33 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MODIFIABLE_BINDING_METHOD;
34 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR;
35 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.SUBCOMPONENT;
36 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
37 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
38 import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
39 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
40 import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
41 import static javax.lang.model.element.Modifier.ABSTRACT;
42 import static javax.lang.model.element.Modifier.FINAL;
43 import static javax.lang.model.element.Modifier.PRIVATE;
44 import static javax.lang.model.element.Modifier.PROTECTED;
45 import static javax.lang.model.element.Modifier.PUBLIC;
46 import static javax.lang.model.element.Modifier.STATIC;
47 
48 import com.google.auto.common.MoreTypes;
49 import com.google.common.collect.ImmutableList;
50 import com.google.common.collect.ImmutableListMultimap;
51 import com.google.common.collect.ImmutableMap;
52 import com.google.common.collect.ImmutableSet;
53 import com.google.common.collect.Iterables;
54 import com.google.common.collect.Lists;
55 import com.google.common.collect.Maps;
56 import com.google.common.collect.Multimaps;
57 import com.google.common.collect.Sets;
58 import com.squareup.javapoet.AnnotationSpec;
59 import com.squareup.javapoet.ClassName;
60 import com.squareup.javapoet.CodeBlock;
61 import com.squareup.javapoet.MethodSpec;
62 import com.squareup.javapoet.ParameterSpec;
63 import com.squareup.javapoet.TypeName;
64 import com.squareup.javapoet.TypeSpec;
65 import dagger.internal.ComponentDefinitionType;
66 import dagger.internal.Preconditions;
67 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
68 import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
69 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
70 import dagger.internal.codegen.javapoet.AnnotationSpecs;
71 import dagger.internal.codegen.javapoet.CodeBlocks;
72 import dagger.internal.codegen.langmodel.DaggerElements;
73 import dagger.internal.codegen.langmodel.DaggerTypes;
74 import dagger.model.Key;
75 import dagger.producers.internal.CancellationListener;
76 import dagger.producers.internal.Producers;
77 import java.util.Collection;
78 import java.util.List;
79 import java.util.Map;
80 import java.util.Optional;
81 import java.util.function.Function;
82 import javax.inject.Inject;
83 import javax.lang.model.element.ExecutableElement;
84 import javax.lang.model.type.DeclaredType;
85 
86 /** A builder of {@link ComponentImplementation}s. */
87 abstract class ComponentImplementationBuilder {
88   private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning";
89 
90   /**
91    * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
92    * before they get partitioned.
93    */
94   private static final int STATEMENTS_PER_METHOD = 100;
95 
96   private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
97 
98   // TODO(ronshapiro): replace this with composition instead of inheritance so we don't have
99   // non-final fields
100   @Inject BindingGraph graph;
101   @Inject ComponentBindingExpressions bindingExpressions;
102   @Inject ComponentRequirementExpressions componentRequirementExpressions;
103   @Inject ComponentImplementation componentImplementation;
104   @Inject ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
105   @Inject DaggerTypes types;
106   @Inject DaggerElements elements;
107   @Inject CompilerOptions compilerOptions;
108   @Inject ComponentImplementationFactory componentImplementationFactory;
109   @Inject TopLevelImplementationComponent topLevelImplementationComponent;
110   private boolean done;
111 
112   /**
113    * Returns a {@link ComponentImplementation} for this component. This is only intended to be
114    * called once (and will throw on successive invocations). If the component must be regenerated,
115    * use a new instance.
116    */
build()117   final ComponentImplementation build() {
118     checkState(
119         !done,
120         "ComponentImplementationBuilder has already built the ComponentImplementation for [%s].",
121         componentImplementation.name());
122     setSupertype();
123     componentImplementation.setCreatorImplementation(
124         componentCreatorImplementationFactory.create(
125             componentImplementation, Optional.of(componentImplementation.graph())));
126     componentImplementation
127         .creatorImplementation()
128         .map(ComponentCreatorImplementation::spec)
129         .ifPresent(this::addCreatorClass);
130 
131     getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements)
132         .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName()));
133     componentImplementation
134         .superclassImplementation()
135         .ifPresent(
136             superclassImplementation -> {
137               superclassImplementation
138                   .getAllModifiableMethodNames()
139                   .forEach(componentImplementation::claimMethodName);
140             });
141 
142     addFactoryMethods();
143     addInterfaceMethods();
144     addChildComponents();
145     implementModifiableModuleMethods();
146 
147     addConstructorAndInitializationMethods();
148 
149     if (graph.componentDescriptor().isProduction()) {
150       addCancellationListenerImplementation();
151     }
152 
153     if (componentImplementation.isAbstract()
154         && !componentImplementation.baseImplementation().isPresent()) {
155       componentImplementation.addAnnotation(compilerOptions.toGenerationOptionsAnnotation());
156     }
157 
158     if (componentImplementation.shouldEmitModifiableMetadataAnnotations()) {
159       componentImplementation.addAnnotation(
160           AnnotationSpec.builder(ComponentDefinitionType.class)
161               .addMember("value", "$T.class", graph.componentTypeElement())
162               .build());
163     }
164 
165     done = true;
166     return componentImplementation;
167   }
168 
169   /** Set the supertype for this generated class. */
setSupertype()170   private void setSupertype() {
171     if (componentImplementation.superclassImplementation().isPresent()) {
172       componentImplementation.addSuperclass(
173           componentImplementation.superclassImplementation().get().name());
174     } else {
175       componentImplementation.addSupertype(graph.componentTypeElement());
176     }
177   }
178 
179   /**
180    * Adds {@code creator} as a nested creator class. Root components and subcomponents will nest
181    * this in different classes.
182    */
addCreatorClass(TypeSpec creator)183   protected abstract void addCreatorClass(TypeSpec creator);
184 
185   /** Adds component factory methods. */
addFactoryMethods()186   protected abstract void addFactoryMethods();
187 
addInterfaceMethods()188   protected void addInterfaceMethods() {
189     // Each component method may have been declared by several supertypes. We want to implement
190     // only one method for each distinct signature.
191     ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature =
192         Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature);
193     for (List<ComponentMethodDescriptor> methodsWithSameSignature :
194         Multimaps.asMap(componentMethodsBySignature).values()) {
195       ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get();
196       MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod);
197 
198       if (compilerOptions.aheadOfTimeSubcomponents()) {
199         addPossiblyModifiableInterfaceMethod(anyOneMethod, methodSpec);
200       } else {
201         componentImplementation.addMethod(COMPONENT_METHOD, methodSpec);
202       }
203     }
204   }
205 
206   /**
207    * Adds a component interface method in ahead-of-time subcomponents mode. If the binding that
208    * implements the method is modifiable, registers the method.
209    */
addPossiblyModifiableInterfaceMethod( ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod)210   private void addPossiblyModifiableInterfaceMethod(
211       ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod) {
212     if (methodDescriptor.dependencyRequest().isPresent()
213         && componentImplementation
214             .getModifiableBindingMethod(bindingRequest(methodDescriptor.dependencyRequest().get()))
215             .isPresent()) {
216       // If there are multiple component methods that are modifiable and for the same binding
217       // request, implement all but one in the base implementation to delegate to the one that
218       // will remain (and be registered) modifiable
219       checkState(componentImplementation.isAbstract() && !componentImplementation.isNested());
220       componentImplementation.addMethod(
221           COMPONENT_METHOD, implementedComponentMethod.toBuilder().addModifiers(FINAL).build());
222     } else {
223       // TODO(b/117833324): Can this class be the one to interface with ComponentImplementation
224       // instead of having it go through ModifiableBindingExpressions?
225       bindingExpressions
226           .modifiableBindingExpressions()
227           .addPossiblyModifiableComponentMethod(methodDescriptor, implementedComponentMethod);
228     }
229   }
230 
addCancellationListenerImplementation()231   private void addCancellationListenerImplementation() {
232     componentImplementation.addSupertype(elements.getTypeElement(CancellationListener.class));
233     componentImplementation.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
234 
235     ImmutableList<ParameterSpec> parameters =
236         ImmutableList.of(ParameterSpec.builder(boolean.class, MAY_INTERRUPT_IF_RUNNING).build());
237 
238     MethodSpec.Builder methodBuilder =
239         methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
240             .addModifiers(PUBLIC)
241             .addAnnotation(Override.class)
242             .addParameters(parameters);
243     if (componentImplementation.superclassImplementation().isPresent()) {
244       methodBuilder.addStatement(
245           "super.$L($L)", CANCELLATION_LISTENER_METHOD_NAME, MAY_INTERRUPT_IF_RUNNING);
246     }
247 
248     ImmutableList<CodeBlock> cancellationStatements = cancellationStatements();
249 
250     if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
251       methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
252     } else {
253       ImmutableList<MethodSpec> cancelProducersMethods =
254           createPartitionedMethods(
255               "cancelProducers",
256               parameters,
257               cancellationStatements,
258               methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
259       for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
260         methodBuilder.addStatement("$N($L)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING);
261         componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
262       }
263     }
264 
265     Optional<CodeBlock> cancelParentStatement = cancelParentStatement();
266     cancelParentStatement.ifPresent(methodBuilder::addCode);
267 
268     if (cancellationStatements.isEmpty()
269         && !cancelParentStatement.isPresent()
270         && componentImplementation.superclassImplementation().isPresent()) {
271       // Partial child implementations that have no new cancellations don't need to override
272       // the method just to call super().
273       return;
274     }
275 
276     componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build());
277   }
278 
cancellationStatements()279   private ImmutableList<CodeBlock> cancellationStatements() {
280     // Reversing should order cancellations starting from entry points and going down to leaves
281     // rather than the other way around. This shouldn't really matter but seems *slightly*
282     // preferable because:
283     // When a future that another future depends on is cancelled, that cancellation will propagate
284     // up the future graph toward the entry point. Cancelling in reverse order should ensure that
285     // everything that depends on a particular node has already been cancelled when that node is
286     // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might
287     // propagate through most of the graph, making most of the cancel calls that follow in the
288     // onProducerFutureCancelled method do nothing.
289     ImmutableList<Key> cancellationKeys =
290         componentImplementation.getCancellableProducerKeys().reverse();
291 
292     ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder();
293     for (Key cancellationKey : cancellationKeys) {
294       cancellationStatements.add(
295           CodeBlock.of(
296               "$T.cancel($L, $N);",
297               Producers.class,
298               bindingExpressions
299                   .getDependencyExpression(
300                       bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE),
301                       componentImplementation.name())
302                   .codeBlock(),
303               MAY_INTERRUPT_IF_RUNNING));
304     }
305     return cancellationStatements.build();
306   }
307 
cancelParentStatement()308   protected Optional<CodeBlock> cancelParentStatement() {
309     // Returns empty by default. Overridden in subclass(es) to add a statement if and only if the
310     // component being generated is a concrete subcomponent implementation with a parent that
311     // allows cancellation to propagate to it from subcomponents.
312     return Optional.empty();
313   }
314 
315   /**
316    * For final components, reimplements all modifiable module methods that may have been modified.
317    */
implementModifiableModuleMethods()318   private void implementModifiableModuleMethods() {
319     if (componentImplementation.isAbstract()) {
320       return;
321     }
322     componentImplementation
323         .getAllModifiableModuleMethods()
324         .forEach(this::implementModifiableModuleMethod);
325   }
326 
implementModifiableModuleMethod(ComponentRequirement module, String methodName)327   private void implementModifiableModuleMethod(ComponentRequirement module, String methodName) {
328     // TODO(b/117833324): only reimplement methods for modules that were abstract or were repeated
329     // by an ancestor component.
330     componentImplementation.addMethod(
331         MODIFIABLE_BINDING_METHOD,
332         methodBuilder(methodName)
333             .addAnnotation(Override.class)
334             .addModifiers(PROTECTED)
335             .returns(TypeName.get(module.type()))
336             .addStatement(
337                 componentRequirementExpressions
338                     .getExpression(module)
339                     .getModifiableModuleMethodExpression(componentImplementation.name()))
340             .build());
341   }
342 
getMethodSignature(ComponentMethodDescriptor method)343   private MethodSignature getMethodSignature(ComponentMethodDescriptor method) {
344     return MethodSignature.forComponentMethod(
345         method, MoreTypes.asDeclared(graph.componentTypeElement().asType()), types);
346   }
347 
addChildComponents()348   private void addChildComponents() {
349     for (BindingGraph subgraph : graph.subgraphs()) {
350       // TODO(b/117833324): Can an abstract inner subcomponent implementation be elided if it's
351       // totally empty?
352       componentImplementation.addChild(
353           subgraph.componentDescriptor(), buildChildImplementation(subgraph));
354     }
355   }
356 
buildChildImplementation(BindingGraph childGraph)357   private ComponentImplementation buildChildImplementation(BindingGraph childGraph) {
358     ComponentImplementation childImplementation =
359         compilerOptions.aheadOfTimeSubcomponents()
360             ? abstractInnerSubcomponent(childGraph)
361             : concreteSubcomponent(childGraph);
362     return topLevelImplementationComponent
363         .currentImplementationSubcomponentBuilder()
364         .componentImplementation(childImplementation)
365         .bindingGraph(childGraph)
366         .parentBuilder(Optional.of(this))
367         .parentBindingExpressions(Optional.of(bindingExpressions))
368         .parentRequirementExpressions(Optional.of(componentRequirementExpressions))
369         .build()
370         .subcomponentBuilder()
371         .build();
372   }
373 
374   /** Creates an inner abstract subcomponent implementation. */
abstractInnerSubcomponent(BindingGraph childGraph)375   private ComponentImplementation abstractInnerSubcomponent(BindingGraph childGraph) {
376     return componentImplementation.childComponentImplementation(
377         childGraph,
378         Optional.of(
379             componentImplementationFactory.findChildSuperclassImplementation(
380                 childGraph.componentDescriptor(), componentImplementation)),
381         PROTECTED,
382         componentImplementation.isAbstract() ? ABSTRACT : FINAL);
383   }
384 
385   /** Creates a concrete inner subcomponent implementation. */
concreteSubcomponent(BindingGraph childGraph)386   private ComponentImplementation concreteSubcomponent(BindingGraph childGraph) {
387     return componentImplementation.childComponentImplementation(
388         childGraph,
389         Optional.empty(), // superclassImplementation
390         PRIVATE,
391         FINAL);
392   }
393 
394   /** Creates and adds the constructor and methods needed for initializing the component. */
addConstructorAndInitializationMethods()395   private void addConstructorAndInitializationMethods() {
396     MethodSpec.Builder constructor = componentConstructorBuilder();
397     if (!componentImplementation.isAbstract()) {
398       implementInitializationMethod(constructor, initializationParameters());
399     } else if (componentImplementation.hasInitializations()) {
400       addConfigureInitializationMethod();
401     }
402     componentImplementation.addMethod(CONSTRUCTOR, constructor.build());
403   }
404 
405   /** Returns a builder for the component's constructor. */
componentConstructorBuilder()406   private MethodSpec.Builder componentConstructorBuilder() {
407     return constructorBuilder()
408             .addModifiers(componentImplementation.isAbstract() ? PROTECTED : PRIVATE);
409   }
410 
411   /** Adds parameters and code to the given {@code initializationMethod}. */
implementInitializationMethod( MethodSpec.Builder initializationMethod, ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters)412   private void implementInitializationMethod(
413       MethodSpec.Builder initializationMethod,
414       ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters) {
415     initializationMethod.addParameters(initializationParameters.values());
416     initializationMethod.addCode(
417         CodeBlocks.concat(componentImplementation.getComponentRequirementInitializations()));
418     componentImplementation
419         .superConfigureInitializationMethod()
420         .ifPresent(
421             superConfigureInitializationMethod ->
422                 addSuperConfigureInitializationCall(
423                     initializationMethod,
424                     initializationParameters,
425                     superConfigureInitializationMethod));
426     addInitializeMethods(initializationMethod, initializationParameters.values().asList());
427   }
428 
429   /** Creates and adds a {@code configureInitializatoin} method to the component. */
addConfigureInitializationMethod()430   private void addConfigureInitializationMethod() {
431     MethodSpec.Builder method = configureInitializationMethodBuilder();
432     ImmutableMap<ComponentRequirement, ParameterSpec> parameters = initializationParameters();
433     implementInitializationMethod(method, parameters);
434     componentImplementation.setConfigureInitializationMethod(
435         ConfigureInitializationMethod.create(method.build(), parameters.keySet()));
436   }
437 
438   /** Returns a builder for the component's {@code configureInitialization} method. */
configureInitializationMethodBuilder()439   private MethodSpec.Builder configureInitializationMethodBuilder() {
440     String methodName = componentImplementation.getUniqueMethodName("configureInitialization");
441     MethodSpec.Builder configureInitialization = methodBuilder(methodName).addModifiers(PROTECTED);
442     if (overridesSuperclassConfigureInitialization(configureInitialization.build())) {
443       configureInitialization.addAnnotation(Override.class);
444     }
445     return configureInitialization;
446   }
447 
448   /**
449    * Returns whether or not the given method overrides a configureInitialization method from a
450    * superclass.
451    */
overridesSuperclassConfigureInitialization(MethodSpec method)452   private boolean overridesSuperclassConfigureInitialization(MethodSpec method) {
453     for (Optional<ComponentImplementation> currentSuperImplementation =
454             componentImplementation.superclassImplementation();
455         currentSuperImplementation.isPresent();
456         currentSuperImplementation = currentSuperImplementation.get().superclassImplementation()) {
457       Optional<MethodSpec> superConfigureInitializationMethod =
458           currentSuperImplementation.get().configureInitializationMethod().map(m -> m.spec());
459       if (superConfigureInitializationMethod
460           .filter(superMethod -> haveSameSignature(method, superMethod))
461           .isPresent()) {
462         return true;
463       }
464     }
465 
466     return false;
467   }
468 
469   /** Returns whether or not methods {@code a} and {@code b} have the same signature. */
haveSameSignature(MethodSpec a, MethodSpec b)470   private boolean haveSameSignature(MethodSpec a, MethodSpec b) {
471     return a.name.equals(b.name) && types(a.parameters).equals(types(b.parameters));
472   }
473 
types(List<ParameterSpec> parameters)474   private ImmutableList<TypeName> types(List<ParameterSpec> parameters) {
475     return parameters.stream().map(parameter -> parameter.type).collect(toImmutableList());
476   }
477 
478   /**
479    * Adds a call to the superclass's {@code configureInitialization} method to the given {@code
480    * callingMethod}.
481    */
addSuperConfigureInitializationCall( MethodSpec.Builder callingMethod, ImmutableMap<ComponentRequirement, ParameterSpec> parameters, ConfigureInitializationMethod superConfigureInitializationMethod)482   private void addSuperConfigureInitializationCall(
483       MethodSpec.Builder callingMethod,
484       ImmutableMap<ComponentRequirement, ParameterSpec> parameters,
485       ConfigureInitializationMethod superConfigureInitializationMethod) {
486     // This component's constructor may not have all of the parameters that the superclass's
487     // configureInitialization method takes, because the superclass configureInitialization method
488     // necessarily accepts things that it can't know whether will be needed or not. If they aren't
489     // needed (as is the case when the constructor doesn't have a parameter for the module), just
490     // pass null to super.configureInitialization for that parameter; it won't be used.
491     CodeBlock args =
492         superConfigureInitializationMethod.parameters().stream()
493             .map(
494                 requirement ->
495                     parameters.containsKey(requirement)
496                         ? CodeBlock.of("$N", parameters.get(requirement))
497                         : CodeBlock.of("null"))
498             .collect(toParametersCodeBlock());
499 
500     String qualifier =
501         haveSameSignature(callingMethod.build(), superConfigureInitializationMethod.spec())
502             ? "super."
503             : "";
504     callingMethod.addStatement(
505         qualifier + "$N($L)", superConfigureInitializationMethod.spec(), args);
506   }
507 
508   /**
509    * Adds any necessary {@code initialize} methods to the component and adds calls to them to the
510    * given {@code callingMethod}.
511    */
addInitializeMethods( MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters)512   private void addInitializeMethods(
513       MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters) {
514     // TODO(cgdecker): It's not the case that each initialize() method has need for all of the
515     // given parameters. In some cases, those parameters may have already been assigned to fields
516     // which could be referenced instead. In other cases, an initialize method may just not need
517     // some of the parameters because the set of initializations in that partition does not
518     // include any reference to them. Right now, the Dagger code has no way of getting that
519     // information because, among other things, componentImplementation.getImplementations() just
520     // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know
521     // yet whether a field will end up needing to be created for a specific requirement, and we
522     // don't want to create a field that ends up only being used during initialization.
523     CodeBlock args = parameterNames(parameters);
524     ImmutableList<MethodSpec> methods =
525         createPartitionedMethods(
526             "initialize",
527             makeFinal(parameters),
528             componentImplementation.getInitializations(),
529             methodName ->
530                 methodBuilder(methodName)
531                     .addModifiers(PRIVATE)
532                     /* TODO(gak): Strictly speaking, we only need the suppression here if we are
533                      * also initializing a raw field in this method, but the structure of this
534                      * code makes it awkward to pass that bit through.  This will be cleaned up
535                      * when we no longer separate fields and initialization as we do now. */
536                     .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)));
537     for (MethodSpec method : methods) {
538       callingMethod.addStatement("$N($L)", method, args);
539       componentImplementation.addMethod(INITIALIZE_METHOD, method);
540     }
541   }
542 
543   /**
544    * Creates one or more methods, all taking the given {@code parameters}, which partition the given
545    * list of {@code statements} among themselves such that no method has more than {@code
546    * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in order,
547    * will execute the {@code statements} in the given order.
548    */
createPartitionedMethods( String methodName, Iterable<ParameterSpec> parameters, List<CodeBlock> statements, Function<String, MethodSpec.Builder> methodBuilderCreator)549   private ImmutableList<MethodSpec> createPartitionedMethods(
550       String methodName,
551       Iterable<ParameterSpec> parameters,
552       List<CodeBlock> statements,
553       Function<String, MethodSpec.Builder> methodBuilderCreator) {
554     return Lists.partition(statements, STATEMENTS_PER_METHOD).stream()
555         .map(
556             partition ->
557                 methodBuilderCreator
558                     .apply(componentImplementation.getUniqueMethodName(methodName))
559                     .addParameters(parameters)
560                     .addCode(CodeBlocks.concat(partition))
561                     .build())
562         .collect(toImmutableList());
563   }
564 
565   /** Returns the given parameters with a final modifier added. */
makeFinal(Collection<ParameterSpec> parameters)566   private final ImmutableList<ParameterSpec> makeFinal(Collection<ParameterSpec> parameters) {
567     return parameters.stream()
568         .map(param -> param.toBuilder().addModifiers(FINAL).build())
569         .collect(toImmutableList());
570   }
571 
572   /**
573    * Returns the parameters for the constructor or {@code configureInitilization} method as a map
574    * from the requirement the parameter fulfills to the spec for the parameter.
575    */
initializationParameters()576   private final ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters() {
577     Map<ComponentRequirement, ParameterSpec> parameters;
578     if (componentImplementation.componentDescriptor().hasCreator()) {
579       parameters =
580           Maps.toMap(componentImplementation.requirements(), ComponentRequirement::toParameterSpec);
581     } else if (componentImplementation.isAbstract() && componentImplementation.isNested()) {
582       // If we're generating an abstract inner subcomponent, then we are not implementing module
583       // instance bindings and have no need for factory method parameters.
584       parameters = ImmutableMap.of();
585     } else if (graph.factoryMethod().isPresent()) {
586       parameters = getFactoryMethodParameters(graph);
587     } else if (componentImplementation.isAbstract()) {
588       // If we're generating an abstract base implementation of a subcomponent it's acceptable to
589       // have neither a creator nor factory method.
590       parameters = ImmutableMap.of();
591     } else {
592       throw new AssertionError(
593           "Expected either a component creator or factory method but found neither.");
594     }
595 
596     if (componentImplementation.isAbstract()) {
597       parameters = Maps.filterKeys(parameters, in(configureInitializationRequirements()));
598     }
599     return renameParameters(parameters);
600   }
601 
602   /**
603    * Returns the set of requirements for the configureInitialization method: the parameters that are
604    * needed either for initializing a component requirement field or for calling the superclass's
605    * {@code configureInitialization} method.
606    */
configureInitializationRequirements()607   private ImmutableSet<ComponentRequirement> configureInitializationRequirements() {
608     ImmutableSet<ComponentRequirement> initializationParameters =
609         componentImplementation.getComponentRequirementParameters();
610     ImmutableSet<ComponentRequirement> superConfigureInitializationRequirements =
611         componentImplementation
612             .superConfigureInitializationMethod()
613             .map(ConfigureInitializationMethod::parameters)
614             .orElse(ImmutableSet.of());
615     return Sets.union(initializationParameters, superConfigureInitializationRequirements)
616         .immutableCopy();
617   }
618 
619   /**
620    * Renames the given parameters to guarantee their names do not conflict with fields in the
621    * component to ensure that a parameter is never referenced where a reference to a field was
622    * intended.
623    */
624   // TODO(cgdecker): This is a bit kludgy; it would be preferable to either qualify the field
625   // references with "this." or "super." when needed to disambiguate between field and parameter,
626   // but that would require more context than is currently available when the code referencing a
627   // field is generated.
renameParameters( Map<ComponentRequirement, ParameterSpec> parameters)628   private ImmutableMap<ComponentRequirement, ParameterSpec> renameParameters(
629       Map<ComponentRequirement, ParameterSpec> parameters) {
630     return ImmutableMap.copyOf(
631         Maps.transformEntries(
632             parameters,
633             (requirement, parameter) ->
634                 renameParameter(
635                     parameter,
636                     componentImplementation.getParameterName(requirement, parameter.name))));
637   }
638 
renameParameter(ParameterSpec parameter, String newName)639   private ParameterSpec renameParameter(ParameterSpec parameter, String newName) {
640     return ParameterSpec.builder(parameter.type, newName)
641         .addAnnotations(parameter.annotations)
642         .addModifiers(parameter.modifiers)
643         .build();
644   }
645 
646   /** Builds a root component implementation. */
647   static final class RootComponentImplementationBuilder extends ComponentImplementationBuilder {
648     @Inject
RootComponentImplementationBuilder(ComponentImplementation componentImplementation)649     RootComponentImplementationBuilder(ComponentImplementation componentImplementation) {
650       checkArgument(!componentImplementation.superclassImplementation().isPresent());
651     }
652 
653     @Override
addCreatorClass(TypeSpec creator)654     protected void addCreatorClass(TypeSpec creator) {
655       componentImplementation.addType(COMPONENT_CREATOR, creator);
656     }
657 
658     @Override
addFactoryMethods()659     protected void addFactoryMethods() {
660       // Top-level components have a static method that returns a builder or factory for the
661       // component. If the user defined a @Component.Builder or @Component.Factory, an
662       // implementation of their type is returned. Otherwise, an autogenerated Builder type is
663       // returned.
664       // TODO(cgdecker): Replace this abomination with a small class?
665       // Better yet, change things so that an autogenerated builder type has a descriptor of sorts
666       // just like a user-defined creator type.
667       ComponentCreatorKind creatorKind;
668       ClassName creatorType;
669       String factoryMethodName;
670       boolean noArgFactoryMethod;
671       if (creatorDescriptor().isPresent()) {
672         ComponentCreatorDescriptor descriptor = creatorDescriptor().get();
673         creatorKind = descriptor.kind();
674         creatorType = ClassName.get(descriptor.typeElement());
675         factoryMethodName = descriptor.factoryMethod().getSimpleName().toString();
676         noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
677       } else {
678         creatorKind = BUILDER;
679         creatorType = componentCreatorName();
680         factoryMethodName = "build";
681         noArgFactoryMethod = true;
682       }
683 
684       MethodSpec creatorFactoryMethod =
685           methodBuilder(creatorKind.methodName())
686               .addModifiers(PUBLIC, STATIC)
687               .returns(creatorType)
688               .addStatement("return new $T()", componentCreatorName())
689               .build();
690       componentImplementation.addMethod(BUILDER_METHOD, creatorFactoryMethod);
691       if (noArgFactoryMethod && canInstantiateAllRequirements()) {
692         componentImplementation.addMethod(
693             BUILDER_METHOD,
694             methodBuilder("create")
695                 .returns(ClassName.get(super.graph.componentTypeElement()))
696                 .addModifiers(PUBLIC, STATIC)
697                 .addStatement(
698                     "return new $L().$L()", creatorKind.typeName(), factoryMethodName)
699                 .build());
700       }
701     }
702 
creatorDescriptor()703     private Optional<ComponentCreatorDescriptor> creatorDescriptor() {
704       return graph.componentDescriptor().creatorDescriptor();
705     }
706 
707     /** {@code true} if all of the graph's required dependencies can be automatically constructed */
canInstantiateAllRequirements()708     private boolean canInstantiateAllRequirements() {
709       return !Iterables.any(
710           graph.componentRequirements(),
711           dependency -> dependency.requiresAPassedInstance(elements, types));
712     }
713 
componentCreatorName()714     private ClassName componentCreatorName() {
715       return componentImplementation.creatorImplementation().get().name();
716     }
717   }
718 
719   /**
720    * Builds a subcomponent implementation. If generating ahead-of-time subcomponents, this may be an
721    * abstract base class implementation, an abstract inner implementation, or a concrete
722    * implementation that extends an abstract base implementation. Otherwise it represents a private,
723    * inner, concrete, final implementation of a subcomponent which extends a user defined type.
724    */
725   static final class SubcomponentImplementationBuilder extends ComponentImplementationBuilder {
726     final Optional<ComponentImplementationBuilder> parent;
727 
728     @Inject
SubcomponentImplementationBuilder( @arentComponent Optional<ComponentImplementationBuilder> parent)729     SubcomponentImplementationBuilder(
730         @ParentComponent Optional<ComponentImplementationBuilder> parent) {
731       this.parent = parent;
732     }
733 
734     @Override
addCreatorClass(TypeSpec creator)735     protected void addCreatorClass(TypeSpec creator) {
736       if (parent.isPresent()) {
737         // In an inner implementation of a subcomponent the creator is a peer class.
738         parent.get().componentImplementation.addType(SUBCOMPONENT, creator);
739       } else {
740         componentImplementation.addType(SUBCOMPONENT, creator);
741       }
742     }
743 
744     @Override
addFactoryMethods()745     protected void addFactoryMethods() {
746       // Only construct instances of subcomponents that have concrete implementations.
747       if (!componentImplementation.isAbstract()) {
748         // Use the parent's factory method to create this subcomponent if the
749         // subcomponent was not added via {@link dagger.Module#subcomponents()}.
750         graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod);
751       }
752     }
753 
createSubcomponentFactoryMethod(ExecutableElement factoryMethod)754     private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
755       checkState(parent.isPresent());
756 
757       Collection<ParameterSpec> params = getFactoryMethodParameters(graph).values();
758       MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType(), types);
759       params.forEach(
760           param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param));
761       method.addStatement(
762           "return new $T($L)", componentImplementation.name(), parameterNames(params));
763 
764       parent.get().componentImplementation.addMethod(COMPONENT_METHOD, method.build());
765     }
766 
parentType()767     private DeclaredType parentType() {
768       return asDeclared(parent.get().graph.componentTypeElement().asType());
769     }
770 
771     @Override
addInterfaceMethods()772     protected void addInterfaceMethods() {
773       if (componentImplementation.superclassImplementation().isPresent()) {
774         // Since we're overriding a subcomponent implementation we add to its implementation given
775         // an expanded binding graph.
776 
777         ComponentImplementation superclassImplementation =
778             componentImplementation.superclassImplementation().get();
779         for (ModifiableBindingMethod superclassModifiableBindingMethod :
780             superclassImplementation.getModifiableBindingMethods().values()) {
781           bindingExpressions
782               .modifiableBindingExpressions()
783               .possiblyReimplementedMethod(superclassModifiableBindingMethod)
784               .ifPresent(componentImplementation::addImplementedModifiableBindingMethod);
785         }
786       } else {
787         super.addInterfaceMethods();
788       }
789     }
790 
791     @Override
cancelParentStatement()792     protected Optional<CodeBlock> cancelParentStatement() {
793       if (!shouldPropagateCancellationToParent()) {
794         return Optional.empty();
795       }
796       return Optional.of(
797           CodeBlock.builder()
798               .addStatement(
799                   "$T.this.$N($N)",
800                   parent.get().componentImplementation.name(),
801                   CANCELLATION_LISTENER_METHOD_NAME,
802                   MAY_INTERRUPT_IF_RUNNING)
803               .build());
804     }
805 
shouldPropagateCancellationToParent()806     private boolean shouldPropagateCancellationToParent() {
807       return parent.isPresent()
808           && parent
809               .get()
810               .componentImplementation
811               .componentDescriptor()
812               .cancellationPolicy()
813               .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
814               .orElse(false);
815     }
816   }
817 
818   /**
819    * Returns the map of {@link ComponentRequirement}s to {@link ParameterSpec}s for the
820    * given graph's factory method.
821    */
getFactoryMethodParameters( BindingGraph graph)822   private static Map<ComponentRequirement, ParameterSpec> getFactoryMethodParameters(
823       BindingGraph graph) {
824     return Maps.transformValues(graph.factoryMethodParameters(), ParameterSpec::get);
825   }
826 }
827