• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.writing;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
20 import static com.google.auto.common.MoreTypes.asDeclared;
21 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
22 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
23 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
24 import static com.google.common.base.Preconditions.checkArgument;
25 import static com.google.common.base.Preconditions.checkState;
26 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
27 import static com.squareup.javapoet.MethodSpec.methodBuilder;
28 import static com.squareup.javapoet.TypeSpec.classBuilder;
29 import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER;
30 import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
31 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
32 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
33 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
34 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
35 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
36 import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
37 import static dagger.internal.codegen.langmodel.Accessibility.isProtectedMemberOf;
38 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
39 import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
40 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
41 import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
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.PUBLIC;
45 import static javax.lang.model.element.Modifier.STATIC;
46 import static javax.tools.Diagnostic.Kind.ERROR;
47 
48 import androidx.room.compiler.processing.XMessager;
49 import androidx.room.compiler.processing.XType;
50 import androidx.room.compiler.processing.XTypeElement;
51 import androidx.room.compiler.processing.compat.XConverters;
52 import com.google.auto.common.MoreElements;
53 import com.google.auto.common.MoreTypes;
54 import com.google.common.base.Function;
55 import com.google.common.base.Supplier;
56 import com.google.common.collect.ImmutableList;
57 import com.google.common.collect.ImmutableMap;
58 import com.google.common.collect.ImmutableSet;
59 import com.google.common.collect.Iterables;
60 import com.google.common.collect.ListMultimap;
61 import com.google.common.collect.Lists;
62 import com.google.common.collect.Maps;
63 import com.google.common.collect.MultimapBuilder;
64 import com.google.common.collect.Sets;
65 import com.squareup.javapoet.ClassName;
66 import com.squareup.javapoet.CodeBlock;
67 import com.squareup.javapoet.FieldSpec;
68 import com.squareup.javapoet.MethodSpec;
69 import com.squareup.javapoet.ParameterSpec;
70 import com.squareup.javapoet.TypeName;
71 import com.squareup.javapoet.TypeSpec;
72 import dagger.internal.Preconditions;
73 import dagger.internal.codegen.base.ComponentCreatorKind;
74 import dagger.internal.codegen.base.UniqueNameSet;
75 import dagger.internal.codegen.binding.Binding;
76 import dagger.internal.codegen.binding.BindingGraph;
77 import dagger.internal.codegen.binding.BindingNode;
78 import dagger.internal.codegen.binding.BindingRequest;
79 import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
80 import dagger.internal.codegen.binding.ComponentDescriptor;
81 import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
82 import dagger.internal.codegen.binding.ComponentRequirement;
83 import dagger.internal.codegen.binding.KeyVariableNamer;
84 import dagger.internal.codegen.binding.MethodSignature;
85 import dagger.internal.codegen.compileroption.CompilerOptions;
86 import dagger.internal.codegen.javapoet.CodeBlocks;
87 import dagger.internal.codegen.javapoet.TypeNames;
88 import dagger.internal.codegen.javapoet.TypeSpecs;
89 import dagger.internal.codegen.langmodel.DaggerElements;
90 import dagger.internal.codegen.langmodel.DaggerTypes;
91 import dagger.spi.model.BindingGraph.Node;
92 import dagger.spi.model.Key;
93 import dagger.spi.model.RequestKind;
94 import java.util.ArrayList;
95 import java.util.Collection;
96 import java.util.HashMap;
97 import java.util.LinkedHashMap;
98 import java.util.List;
99 import java.util.Map;
100 import java.util.Optional;
101 import java.util.Set;
102 import javax.inject.Inject;
103 import javax.inject.Provider;
104 import javax.lang.model.element.ExecutableElement;
105 import javax.lang.model.element.Modifier;
106 import javax.lang.model.element.VariableElement;
107 import javax.lang.model.type.DeclaredType;
108 import javax.lang.model.type.TypeMirror;
109 
110 /** The implementation of a component type. */
111 @PerComponentImplementation
112 public final class ComponentImplementation {
113   /** A factory for creating a {@link ComponentImplementation}. */
114   public interface ChildComponentImplementationFactory {
115     /** Creates a {@link ComponentImplementation} for the given {@code childGraph}. */
create(BindingGraph childGraph)116     ComponentImplementation create(BindingGraph childGraph);
117   }
118 
119   /** Compiler Modes. */
120   public enum CompilerMode {
121     DEFAULT,
122     FAST_INIT,
123     EXPERIMENTAL_MERGED_MODE;
124 
isFastInit()125     public boolean isFastInit() {
126       return this == CompilerMode.FAST_INIT;
127     }
128 
isExperimentalMergedMode()129     public boolean isExperimentalMergedMode() {
130       return this == CompilerMode.EXPERIMENTAL_MERGED_MODE;
131     }
132   }
133 
134   /** A type of field that this component can contain. */
135   public enum FieldSpecKind {
136     /** A field for a component shard. */
137     COMPONENT_SHARD_FIELD,
138 
139     /** A field required by the component, e.g. module instances. */
140     COMPONENT_REQUIREMENT_FIELD,
141 
142     /** A framework field for type T, e.g. {@code Provider<T>}. */
143     FRAMEWORK_FIELD,
144 
145     /** A static field that always returns an absent {@code Optional} value for the binding. */
146     ABSENT_OPTIONAL_FIELD
147   }
148 
149   /** A type of method that this component can contain. */
150   // TODO(bcorso, dpb): Change the oder to constructor, initialize, component, then private
151   // (including MIM and AOM—why treat those separately?).
152   public enum MethodSpecKind {
153     /** The component constructor. */
154     CONSTRUCTOR,
155 
156     /** A builder method for the component. (Only used by the root component.) */
157     BUILDER_METHOD,
158 
159     /** A private method that wraps dependency expressions. */
160     PRIVATE_METHOD,
161 
162     /** An initialization method that initializes component requirements and framework types. */
163     INITIALIZE_METHOD,
164 
165     /** An implementation of a component interface method. */
166     COMPONENT_METHOD,
167 
168     /** A private method that encapsulates members injection logic for a binding. */
169     MEMBERS_INJECTION_METHOD,
170 
171     /** A static method that always returns an absent {@code Optional} value for the binding. */
172     ABSENT_OPTIONAL_METHOD,
173 
174     /**
175      * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
176      * method for a production component.
177      */
178     CANCELLATION_LISTENER_METHOD
179   }
180 
181   /** A type of nested class that this component can contain. */
182   public enum TypeSpecKind {
183     /** A factory class for a present optional binding. */
184     PRESENT_FACTORY,
185 
186     /** A class for the component creator (only used by the root component.) */
187     COMPONENT_CREATOR,
188 
189     /** A provider class for a component provision. */
190     COMPONENT_PROVISION_FACTORY,
191 
192     /** A class for a component shard. */
193     COMPONENT_SHARD_TYPE,
194 
195     /** A class for the subcomponent or subcomponent builder. */
196     SUBCOMPONENT
197   }
198 
199   /**
200    * Returns the {@link ShardImplementation} for each binding in this graph.
201    *
202    * <p>Each shard contains approximately {@link CompilerOptions#keysPerComponentShard()} bindings.
203    *
204    * <p>If more than 1 shard is needed, we iterate the strongly connected nodes to make sure of two
205    * things: 1) bindings are put in shards in reverse topological order (i.e., bindings in Shard{i}
206    * do not depend on bindings in Shard{i+j}) and 2) bindings belonging to the same cycle are put in
207    * the same shard. These two guarantees allow us to initialize each shard in a well defined order.
208    */
createShardsByBinding( ShardImplementation componentShard, BindingGraph graph, CompilerOptions compilerOptions)209   private static ImmutableMap<Binding, ShardImplementation> createShardsByBinding(
210       ShardImplementation componentShard, BindingGraph graph, CompilerOptions compilerOptions) {
211     ImmutableList<ImmutableList<Binding>> partitions = bindingPartitions(graph, compilerOptions);
212     ImmutableMap.Builder<Binding, ShardImplementation> builder = ImmutableMap.builder();
213     for (int i = 0; i < partitions.size(); i++) {
214       ShardImplementation shard = i == 0 ? componentShard : componentShard.createShard("Shard" + i);
215       partitions.get(i).forEach(binding -> builder.put(binding, shard));
216     }
217     return builder.build();
218   }
219 
bindingPartitions( BindingGraph graph, CompilerOptions compilerOptions)220   private static ImmutableList<ImmutableList<Binding>> bindingPartitions(
221       BindingGraph graph, CompilerOptions compilerOptions) {
222     int bindingsPerShard = compilerOptions.keysPerComponentShard(graph.componentTypeElement());
223     int maxPartitions = (graph.localBindingNodes().size() / bindingsPerShard) + 1;
224     if (maxPartitions <= 1) {
225       return ImmutableList.of(
226           graph.localBindingNodes().stream().map(BindingNode::delegate).collect(toImmutableList()));
227     }
228 
229     // Iterate through all SCCs in order until all bindings local to this component are partitioned.
230     List<Binding> currPartition = new ArrayList<>(bindingsPerShard);
231     ImmutableList.Builder<ImmutableList<Binding>> partitions =
232         ImmutableList.builderWithExpectedSize(maxPartitions);
233     for (ImmutableSet<Node> nodes : graph.topLevelBindingGraph().stronglyConnectedNodes()) {
234       nodes.stream()
235           .flatMap(instancesOf(BindingNode.class))
236           .filter(bindingNode -> bindingNode.componentPath().equals(graph.componentPath()))
237           .map(BindingNode::delegate)
238           .forEach(currPartition::add);
239       if (currPartition.size() >= bindingsPerShard) {
240         partitions.add(ImmutableList.copyOf(currPartition));
241         currPartition = new ArrayList<>(bindingsPerShard);
242       }
243     }
244     if (!currPartition.isEmpty()) {
245       partitions.add(ImmutableList.copyOf(currPartition));
246     }
247     return partitions.build();
248   }
249 
250   /** The boolean parameter of the onProducerFutureCancelled method. */
251   public static final ParameterSpec MAY_INTERRUPT_IF_RUNNING_PARAM =
252       ParameterSpec.builder(boolean.class, "mayInterruptIfRunning").build();
253 
254   private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
255 
256   /**
257    * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
258    * before they get partitioned.
259    */
260   private static final int STATEMENTS_PER_METHOD = 100;
261 
262   private final ShardImplementation componentShard;
263   private final ImmutableMap<Binding, ShardImplementation> shardsByBinding;
264   private final Map<ShardImplementation, FieldSpec> shardFieldsByImplementation = new HashMap<>();
265   private final List<CodeBlock> shardInitializations = new ArrayList<>();
266   private final List<CodeBlock> shardCancellations = new ArrayList<>();
267   private final Optional<ComponentImplementation> parent;
268   private final ChildComponentImplementationFactory childComponentImplementationFactory;
269   private final Provider<ComponentRequestRepresentations> bindingExpressionsProvider;
270   private final Provider<ComponentCreatorImplementationFactory>
271       componentCreatorImplementationFactoryProvider;
272   private final BindingGraph graph;
273   private final ComponentNames componentNames;
274   private final DaggerElements elements;
275   private final DaggerTypes types;
276   private final ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation;
277   private final XMessager messager;
278   private final CompilerMode compilerMode;
279 
280   @Inject
ComponentImplementation( @arentComponent Optional<ComponentImplementation> parent, ChildComponentImplementationFactory childComponentImplementationFactory, Provider<ComponentRequestRepresentations> bindingExpressionsProvider, Provider<ComponentCreatorImplementationFactory> componentCreatorImplementationFactoryProvider, BindingGraph graph, ComponentNames componentNames, CompilerOptions compilerOptions, DaggerElements elements, DaggerTypes types, XMessager messager)281   ComponentImplementation(
282       @ParentComponent Optional<ComponentImplementation> parent,
283       ChildComponentImplementationFactory childComponentImplementationFactory,
284       // Inject as Provider<> to prevent a cycle.
285       Provider<ComponentRequestRepresentations> bindingExpressionsProvider,
286       Provider<ComponentCreatorImplementationFactory> componentCreatorImplementationFactoryProvider,
287       BindingGraph graph,
288       ComponentNames componentNames,
289       CompilerOptions compilerOptions,
290       DaggerElements elements,
291       DaggerTypes types,
292       XMessager messager) {
293     this.parent = parent;
294     this.childComponentImplementationFactory = childComponentImplementationFactory;
295     this.bindingExpressionsProvider = bindingExpressionsProvider;
296     this.componentCreatorImplementationFactoryProvider =
297         componentCreatorImplementationFactoryProvider;
298     this.graph = graph;
299     this.componentNames = componentNames;
300     this.elements = elements;
301     this.types = types;
302 
303     // The first group of keys belong to the component itself. We call this the componentShard.
304     this.componentShard = new ShardImplementation(componentNames.get(graph.componentPath()));
305 
306     // Claim the method names for all local and inherited methods on the component type.
307     elements
308         .getLocalAndInheritedMethods(toJavac(graph.componentTypeElement()))
309         .forEach(method -> componentShard.componentMethodNames.claim(method.getSimpleName()));
310 
311     // Create the shards for this component, indexed by binding.
312     this.shardsByBinding = createShardsByBinding(componentShard, graph, compilerOptions);
313 
314     // Create and claim the fields for this and all ancestor components stored as fields.
315     this.componentFieldsByImplementation =
316         createComponentFieldsByImplementation(this, compilerOptions);
317     this.messager = messager;
318     XTypeElement typeElement = rootComponentImplementation().componentDescriptor().typeElement();
319     this.compilerMode =
320         compilerOptions.fastInit(typeElement)
321             ? CompilerMode.FAST_INIT
322             : (compilerOptions.experimentalMergedMode(typeElement)
323                 ? CompilerMode.EXPERIMENTAL_MERGED_MODE
324                 : CompilerMode.DEFAULT);
325   }
326 
327   /**
328    * Returns the shard for a given {@link Binding}.
329    *
330    * <p>Each set of {@link CompilerOptions#keysPerShard()} will get its own shard instance.
331    */
shardImplementation(Binding binding)332   public ShardImplementation shardImplementation(Binding binding) {
333     checkState(shardsByBinding.containsKey(binding), "No shard in %s for: %s", name(), binding);
334     return shardsByBinding.get(binding);
335   }
336 
337   /** Returns the root {@link ComponentImplementation}. */
rootComponentImplementation()338   ComponentImplementation rootComponentImplementation() {
339     return parent.map(ComponentImplementation::rootComponentImplementation).orElse(this);
340   }
341 
342   /** Returns a reference to this implementation when called from a different class. */
componentFieldReference()343   public CodeBlock componentFieldReference() {
344     // TODO(bcorso): This currently relies on all requesting classes having a reference to the
345     // component with the same name, which is kind of sketchy. Try to think of a better way that
346     // can accomodate the component missing in some classes if it's not used.
347     return CodeBlock.of("$N", componentFieldsByImplementation.get(this));
348   }
349 
350   /** Returns the fields for all components in the component path. */
componentFields()351   public ImmutableList<FieldSpec> componentFields() {
352     return ImmutableList.copyOf(componentFieldsByImplementation.values());
353   }
354 
355   /** Returns the fields for all components in the component path except the current component. */
creatorComponentFields()356   public ImmutableList<FieldSpec> creatorComponentFields() {
357     return componentFieldsByImplementation.entrySet().stream()
358         .filter(entry -> !this.equals(entry.getKey()))
359         .map(Map.Entry::getValue)
360         .collect(toImmutableList());
361   }
362 
363   private static ImmutableMap<ComponentImplementation, FieldSpec>
createComponentFieldsByImplementation( ComponentImplementation componentImplementation, CompilerOptions compilerOptions)364       createComponentFieldsByImplementation(
365           ComponentImplementation componentImplementation, CompilerOptions compilerOptions) {
366     checkArgument(
367         componentImplementation.componentShard != null,
368         "The component shard must be set before computing the component fields.");
369     ImmutableList.Builder<ComponentImplementation> builder = ImmutableList.builder();
370     for (ComponentImplementation curr = componentImplementation;
371         curr != null;
372         curr = curr.parent.orElse(null)) {
373       builder.add(curr);
374     }
375     // For better readability when adding these fields/parameters to generated code, we collect the
376     // component implementations in reverse order so that parents appear before children.
377     return builder.build().reverse().stream()
378         .collect(
379             toImmutableMap(
380                 componentImpl -> componentImpl,
381                 componentImpl -> {
382                   ClassName component =
383                       componentImpl.graph.componentPath().currentComponent().className();
384                   ClassName fieldType = componentImpl.name();
385                   String fieldName =
386                       componentImpl.isNested()
387                           ? simpleVariableName(componentImpl.name())
388                           : simpleVariableName(component);
389                   FieldSpec.Builder field = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
390                   componentImplementation.componentShard.componentFieldNames.claim(fieldName);
391 
392                   return field.build();
393                 }));
394   }
395   /** Returns the shard representing the {@link ComponentImplementation} itself. */
getComponentShard()396   public ShardImplementation getComponentShard() {
397     return componentShard;
398   }
399 
400   /** Returns the binding graph for the component being generated. */
graph()401   public BindingGraph graph() {
402     return componentShard.graph();
403   }
404 
405   /** Returns the descriptor for the component being generated. */
componentDescriptor()406   public ComponentDescriptor componentDescriptor() {
407     return componentShard.componentDescriptor();
408   }
409 
410   /** Returns the name of the component. */
name()411   public ClassName name() {
412     return componentShard.name;
413   }
414 
415   /** Returns if the current compile mode is fast init. */
compilerMode()416   public CompilerMode compilerMode() {
417     return compilerMode;
418   }
419 
420   /** Returns whether or not the implementation is nested within another class. */
isNested()421   private boolean isNested() {
422     return name().enclosingClassName() != null;
423   }
424 
425   /**
426    * Returns the name of the creator class for this component. It will be a sibling of this
427    * generated class unless this is a top-level component, in which case it will be nested.
428    */
getCreatorName()429   public ClassName getCreatorName() {
430     return componentNames.getCreatorName(graph.componentPath());
431   }
432 
433   /** Generates the component and returns the resulting {@link TypeSpec}. */
generate()434   public TypeSpec generate() {
435     return componentShard.generate();
436   }
437 
438   /**
439    * The implementation of a shard.
440    *
441    * <p>The purpose of a shard is to allow a component implemenation to be split into multiple
442    * classes, where each class owns the creation logic for a set of keys. Sharding is useful for
443    * large component implementations, where a single component implementation class can reach size
444    * limitations, such as the constant pool size.
445    *
446    * <p>When generating the actual sources, the creation logic within the first instance of {@link
447    * ShardImplementation} will go into the component implementation class itself (e.g. {@code
448    * MySubcomponentImpl}). Each subsequent instance of {@link ShardImplementation} will generate a
449    * nested "shard" class within the component implementation (e.g. {@code
450    * MySubcomponentImpl.Shard1}, {@code MySubcomponentImpl.Shard2}, etc).
451    */
452   public final class ShardImplementation {
453     private final ClassName name;
454     private final UniqueNameSet componentFieldNames = new UniqueNameSet();
455     private final UniqueNameSet componentMethodNames = new UniqueNameSet();
456     private final UniqueNameSet componentClassNames = new UniqueNameSet();
457     private final UniqueNameSet assistedParamNames = new UniqueNameSet();
458     private final List<CodeBlock> initializations = new ArrayList<>();
459     private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>();
460     private final Map<VariableElement, String> uniqueAssistedName = new LinkedHashMap<>();
461     private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
462     private final ImmutableMap<ComponentRequirement, ParameterSpec> constructorParameters;
463     private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
464         MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
465     private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
466         MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
467     private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
468         MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
469     private final List<Supplier<TypeSpec>> typeSuppliers = new ArrayList<>();
470     private boolean initialized = false; // This is used for initializing assistedParamNames.
471 
ShardImplementation(ClassName name)472     private ShardImplementation(ClassName name) {
473       this.name = name;
474       if (graph.componentDescriptor().isProduction()) {
475         claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
476       }
477 
478       // Build the map of constructor parameters for this shard and claim the field names to prevent
479       // collisions between the constructor parameters and fields.
480       constructorParameters =
481           constructorRequirements(graph).stream()
482               .collect(
483                   toImmutableMap(
484                       requirement -> requirement,
485                       requirement ->
486                           ParameterSpec.builder(
487                                   requirement.type().getTypeName(),
488                                   getUniqueFieldName(requirement.variableName() + "Param"))
489                               .build()));
490     }
491 
createShard(String shardName)492     private ShardImplementation createShard(String shardName) {
493       checkState(isComponentShard(), "Only the componentShard can create other shards.");
494       return new ShardImplementation(name.nestedClass(shardName));
495     }
496 
497     /** Returns the {@link ComponentImplementation} that owns this shard. */
getComponentImplementation()498     public ComponentImplementation getComponentImplementation() {
499       return ComponentImplementation.this;
500     }
501 
502     /**
503      * Returns {@code true} if this shard represents the component implementation rather than a
504      * separate {@code Shard} class.
505      */
isComponentShard()506     public boolean isComponentShard() {
507       return this == componentShard;
508     }
509 
510     /** Returns the fields for all components in the component path by component implementation. */
componentFieldsByImplementation()511     public ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation() {
512       return componentFieldsByImplementation;
513     }
514 
515     /** Returns a reference to this implementation when called from a different class. */
shardFieldReference()516     public CodeBlock shardFieldReference() {
517       if (!isComponentShard() && !shardFieldsByImplementation.containsKey(this)) {
518         // Add the shard if this is the first time it's requested by something.
519         String shardFieldName =
520             componentShard.getUniqueFieldName(UPPER_CAMEL.to(LOWER_CAMEL, name.simpleName()));
521         FieldSpec shardField = FieldSpec.builder(name, shardFieldName, PRIVATE).build();
522 
523         shardFieldsByImplementation.put(this, shardField);
524       }
525       // TODO(bcorso): This currently relies on all requesting classes having a reference to the
526       // component with the same name, which is kind of sketchy. Try to think of a better way that
527       // can accomodate the component missing in some classes if it's not used.
528       return isComponentShard()
529           ? componentFieldReference()
530           : CodeBlock.of("$L.$N", componentFieldReference(), shardFieldsByImplementation.get(this));
531     }
532 
533     // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
534     // need it.
535     /** Returns the binding graph for the component being generated. */
graph()536     public BindingGraph graph() {
537       return graph;
538     }
539 
540     /** Returns the descriptor for the component being generated. */
componentDescriptor()541     public ComponentDescriptor componentDescriptor() {
542       return graph.componentDescriptor();
543     }
544 
545     /** Returns the name of the component. */
name()546     public ClassName name() {
547       return name;
548     }
549 
550     /**
551      * Returns the name of the creator implementation class for the given subcomponent creator
552      * {@link Key}.
553      */
getSubcomponentCreatorSimpleName(Key creatorKey)554     ClassName getSubcomponentCreatorSimpleName(Key creatorKey) {
555       return componentNames.getSubcomponentCreatorName(graph.componentPath(), creatorKey);
556     }
557 
558     /**
559      * Returns an accessible type for this shard implementation, returns Object if the type is not
560      * accessible.
561      *
562      * <p>This method checks accessibility for public types and package private types, and it also
563      * checks protected types' accessibility.
564      */
accessibleType(TypeMirror type)565     TypeMirror accessibleType(TypeMirror type) {
566       // Returns the original type if the type is accessible from this shard, or returns original
567       // type's raw type if only its raw type is accessible. Otherwise, returns Object.
568       TypeMirror castedType = types.accessibleType(type, name());
569       // Previous check marks protected type as inaccessible, so a second check is needed to check
570       // if the type is protected type and accessible.
571       if (TypeName.get(castedType).equals(TypeName.OBJECT) && isTypeAccessible(type)) {
572         castedType = type;
573       }
574       return castedType;
575     }
576 
577     /**
578      * Returns {@code true} if {@code type} is accessible from the generated component.
579      *
580      * <p>This method checks accessibility for public types and package private types, and it also
581      * checks protected types' accessibility.
582      */
isTypeAccessible(TypeMirror type)583     boolean isTypeAccessible(TypeMirror type) {
584       if (isTypeAccessibleFrom(type, name.packageName())) {
585         return true;
586       }
587       // Check if the type is protected and accessible from current component.
588       if (type instanceof DeclaredType
589           && isProtectedMemberOf(
590               MoreTypes.asDeclared(type),
591               getComponentImplementation().componentDescriptor().typeElement())) {
592         return true;
593       }
594       return false;
595     }
596 
597     // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name?
598     /** Adds the given field to the component. */
addField(FieldSpecKind fieldKind, FieldSpec fieldSpec)599     public void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
600       fieldSpecsMap.put(fieldKind, fieldSpec);
601     }
602 
603     // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name?
604     /** Adds the given method to the component. */
addMethod(MethodSpecKind methodKind, MethodSpec methodSpec)605     public void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
606       methodSpecsMap.put(methodKind, methodSpec);
607     }
608 
609     /** Adds the given type to the component. */
addType(TypeSpecKind typeKind, TypeSpec typeSpec)610     public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
611       typeSpecsMap.put(typeKind, typeSpec);
612     }
613 
614     /** Adds a {@link Supplier} for the SwitchingProvider for the component. */
addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier)615     void addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier) {
616       typeSuppliers.add(typeSpecSupplier);
617     }
618 
619     /** Adds the given code block to the initialize methods of the component. */
addInitialization(CodeBlock codeBlock)620     void addInitialization(CodeBlock codeBlock) {
621       initializations.add(codeBlock);
622     }
623 
624     /** Adds the given code block that initializes a {@link ComponentRequirement}. */
addComponentRequirementInitialization(CodeBlock codeBlock)625     void addComponentRequirementInitialization(CodeBlock codeBlock) {
626       componentRequirementInitializations.add(codeBlock);
627     }
628 
629     /**
630      * Adds the given cancellation statement to the cancellation listener method of the component.
631      */
addCancellation(Key key, CodeBlock codeBlock)632     void addCancellation(Key key, CodeBlock codeBlock) {
633       // Store cancellations by key to avoid adding the same cancellation twice.
634       cancellations.putIfAbsent(key, codeBlock);
635     }
636 
637     /** Returns a new, unique field name for the component based on the given name. */
getUniqueFieldName(String name)638     String getUniqueFieldName(String name) {
639       return componentFieldNames.getUniqueName(name);
640     }
641 
getUniqueAssistedParamName(String name)642     String getUniqueAssistedParamName(String name) {
643       if (!initialized) {
644         // Assisted params will be used in switching provider, so they can't conflict with component
645         // field names in switching provider. {@link UniqueNameSet#getUniqueName} will add the
646         // component field names to the unique set if it does not exists. If the name already exists
647         // in the set, then a dedupe will be performed automatically on the passed in name, and the
648         // newly created unique name will then be added to the set.
649         componentFieldsByImplementation()
650             .values()
651             .forEach(fieldSpec -> assistedParamNames.getUniqueName(fieldSpec.name));
652         initialized = true;
653       }
654       return assistedParamNames.getUniqueName(name);
655     }
656 
getUniqueFieldNameForAssistedParam(VariableElement element)657     public String getUniqueFieldNameForAssistedParam(VariableElement element) {
658       if (uniqueAssistedName.containsKey(element)) {
659         return uniqueAssistedName.get(element);
660       }
661       String name = getUniqueAssistedParamName(element.getSimpleName().toString());
662       uniqueAssistedName.put(element, name);
663       return name;
664     }
665 
666     /** Returns a new, unique nested class name for the component based on the given name. */
getUniqueMethodName(String name)667     public String getUniqueMethodName(String name) {
668       return componentMethodNames.getUniqueName(name);
669     }
670 
671     /** Returns a new, unique method name for a getter method for the given request. */
getUniqueMethodName(BindingRequest request)672     String getUniqueMethodName(BindingRequest request) {
673       return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
674     }
675 
676     /** Returns a new, unique method name for the component based on the given name. */
getUniqueClassName(String name)677     public String getUniqueClassName(String name) {
678       return componentClassNames.getUniqueName(name);
679     }
680 
uniqueMethodName(BindingRequest request, String bindingName)681     private String uniqueMethodName(BindingRequest request, String bindingName) {
682       // This name is intentionally made to match the name for fields in fastInit
683       // in order to reduce the constant pool size. b/162004246
684       String baseMethodName =
685           bindingName
686               + (request.isRequestKind(RequestKind.INSTANCE)
687                   ? ""
688                   : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName()));
689       return getUniqueMethodName(baseMethodName);
690     }
691 
692     /**
693      * Gets the parameter name to use for the given requirement for this component, starting with
694      * the given base name if no parameter name has already been selected for the requirement.
695      */
getParameterName(ComponentRequirement requirement)696     public String getParameterName(ComponentRequirement requirement) {
697       return constructorParameters.get(requirement).name;
698     }
699 
700     /** Claims a new method name for the component. Does nothing if method name already exists. */
claimMethodName(CharSequence name)701     public void claimMethodName(CharSequence name) {
702       componentMethodNames.claim(name);
703     }
704 
705     /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
generate()706     private TypeSpec generate() {
707       TypeSpec.Builder builder = classBuilder(name);
708 
709       if (isComponentShard()) {
710         TypeSpecs.addSupertype(builder, graph.componentTypeElement());
711         addCreator();
712         addFactoryMethods();
713         addInterfaceMethods();
714         addChildComponents();
715         addShards();
716       }
717 
718       addConstructorAndInitializationMethods();
719 
720       if (graph.componentDescriptor().isProduction()) {
721         if (isComponentShard() || !cancellations.isEmpty()) {
722           TypeSpecs.addSupertype(
723               builder, elements.getTypeElement(TypeNames.CANCELLATION_LISTENER.canonicalName()));
724           addCancellationListenerImplementation();
725         }
726       }
727 
728       modifiers().forEach(builder::addModifiers);
729       fieldSpecsMap.asMap().values().forEach(builder::addFields);
730       methodSpecsMap.asMap().values().forEach(builder::addMethods);
731       typeSpecsMap.asMap().values().forEach(builder::addTypes);
732       typeSuppliers.stream().map(Supplier::get).forEach(builder::addType);
733       return builder.build();
734     }
735 
modifiers()736     private ImmutableSet<Modifier> modifiers() {
737       if (!isComponentShard()) {
738         // TODO(bcorso): Consider making shards static and unnested too?
739         return ImmutableSet.of(PRIVATE, FINAL);
740       } else if (isNested()) {
741         return ImmutableSet.of(PRIVATE, STATIC, FINAL);
742       }
743       return graph.componentTypeElement().isPublic()
744           // TODO(ronshapiro): perhaps all generated components should be non-public?
745           ? ImmutableSet.of(PUBLIC, FINAL)
746           : ImmutableSet.of(FINAL);
747     }
748 
addCreator()749     private void addCreator() {
750       componentCreatorImplementationFactoryProvider
751           .get()
752           .create()
753           .map(ComponentCreatorImplementation::spec)
754           .ifPresent(
755               creator ->
756                   rootComponentImplementation()
757                       .getComponentShard()
758                       .addType(TypeSpecKind.COMPONENT_CREATOR, creator));
759     }
760 
addFactoryMethods()761     private void addFactoryMethods() {
762       if (parent.isPresent()) {
763         graph
764             .factoryMethod()
765             .map(XConverters::toJavac)
766             .ifPresent(this::createSubcomponentFactoryMethod);
767       } else {
768         createRootComponentFactoryMethod();
769       }
770     }
771 
createRootComponentFactoryMethod()772     private void createRootComponentFactoryMethod() {
773       checkState(!parent.isPresent());
774       // Top-level components have a static method that returns a builder or factory for the
775       // component. If the user defined a @Component.Builder or @Component.Factory, an
776       // implementation of their type is returned. Otherwise, an autogenerated Builder type is
777       // returned.
778       // TODO(cgdecker): Replace this abomination with a small class?
779       // Better yet, change things so that an autogenerated builder type has a descriptor of sorts
780       // just like a user-defined creator type.
781       ComponentCreatorKind creatorKind;
782       ClassName creatorType;
783       String factoryMethodName;
784       boolean noArgFactoryMethod;
785       Optional<ComponentCreatorDescriptor> creatorDescriptor =
786           graph.componentDescriptor().creatorDescriptor();
787       if (creatorDescriptor.isPresent()) {
788         ComponentCreatorDescriptor descriptor = creatorDescriptor.get();
789         creatorKind = descriptor.kind();
790         creatorType = descriptor.typeElement().getClassName();
791         factoryMethodName = getSimpleName(descriptor.factoryMethod());
792         noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
793       } else {
794         creatorKind = BUILDER;
795         creatorType = getCreatorName();
796         factoryMethodName = "build";
797         noArgFactoryMethod = true;
798       }
799       validateMethodNameDoesNotOverrideGeneratedCreator(creatorKind.methodName());
800       claimMethodName(creatorKind.methodName());
801       MethodSpec creatorFactoryMethod =
802           methodBuilder(creatorKind.methodName())
803               .addModifiers(PUBLIC, STATIC)
804               .returns(creatorType)
805               .addStatement("return new $T()", getCreatorName())
806               .build();
807       addMethod(MethodSpecKind.BUILDER_METHOD, creatorFactoryMethod);
808       if (noArgFactoryMethod && canInstantiateAllRequirements()) {
809         validateMethodNameDoesNotOverrideGeneratedCreator("create");
810         claimMethodName("create");
811         addMethod(
812             MethodSpecKind.BUILDER_METHOD,
813             methodBuilder("create")
814                 .returns(graph.componentTypeElement().getClassName())
815                 .addModifiers(PUBLIC, STATIC)
816                 .addStatement("return new $L().$L()", creatorKind.typeName(), factoryMethodName)
817                 .build());
818       }
819     }
820 
validateMethodNameDoesNotOverrideGeneratedCreator(String creatorName)821     private void validateMethodNameDoesNotOverrideGeneratedCreator(String creatorName) {
822       // Check if there is any client added method has the same signature as generated creatorName.
823       MoreElements.getAllMethods(toJavac(graph.componentTypeElement()), types, elements).stream()
824           .filter(method -> method.getSimpleName().contentEquals(creatorName))
825           .filter(method -> method.getParameters().isEmpty())
826           .filter(method -> !method.getModifiers().contains(Modifier.STATIC))
827           .forEach(
828               (ExecutableElement method) ->
829                   messager.printMessage(
830                       ERROR,
831                       String.format(
832                           "Cannot override generated method: %s.%s()",
833                           method.getEnclosingElement().getSimpleName(), method.getSimpleName())));
834     }
835 
836     /** {@code true} if all of the graph's required dependencies can be automatically constructed */
canInstantiateAllRequirements()837     private boolean canInstantiateAllRequirements() {
838       return !Iterables.any(
839           graph.componentRequirements(), ComponentRequirement::requiresAPassedInstance);
840     }
841 
createSubcomponentFactoryMethod(ExecutableElement factoryMethod)842     private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
843       checkState(parent.isPresent());
844       Collection<ParameterSpec> params =
845           Maps.transformValues(
846                   graph.factoryMethodParameters(),
847                   parameter -> ParameterSpec.get(toJavac(parameter)))
848               .values();
849       DeclaredType parentType =
850           asDeclared(toJavac(parent.get().graph().componentTypeElement()).asType());
851       MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType, types);
852       params.forEach(
853           param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param));
854       method.addStatement(
855           "return new $T($L)",
856           name(),
857           parameterNames(
858               ImmutableList.<ParameterSpec>builder()
859                   .addAll(
860                       creatorComponentFields().stream()
861                           .map(field -> ParameterSpec.builder(field.type, field.name).build())
862                           .collect(toImmutableList()))
863                   .addAll(params)
864                   .build()));
865 
866       parent.get().getComponentShard().addMethod(COMPONENT_METHOD, method.build());
867     }
868 
addInterfaceMethods()869     private void addInterfaceMethods() {
870       // Each component method may have been declared by several supertypes. We want to implement
871       // only one method for each distinct signature.
872       XType componentType = graph.componentTypeElement().getType();
873       Set<MethodSignature> signatures = Sets.newHashSet();
874       for (ComponentMethodDescriptor method : graph.componentDescriptor().entryPointMethods()) {
875         if (signatures.add(MethodSignature.forComponentMethod(method, componentType))) {
876           addMethod(COMPONENT_METHOD, bindingExpressionsProvider.get().getComponentMethod(method));
877         }
878       }
879     }
880 
addChildComponents()881     private void addChildComponents() {
882       for (BindingGraph subgraph : graph.subgraphs()) {
883         rootComponentImplementation()
884             .getComponentShard()
885             .addType(
886                 TypeSpecKind.SUBCOMPONENT,
887                 childComponentImplementationFactory.create(subgraph).generate());
888       }
889     }
890 
addShards()891     private void addShards() {
892       // Generate all shards and add them to this component implementation.
893       for (ShardImplementation shard : ImmutableSet.copyOf(shardsByBinding.values())) {
894         if (shardFieldsByImplementation.containsKey(shard)) {
895           addField(FieldSpecKind.COMPONENT_SHARD_FIELD, shardFieldsByImplementation.get(shard));
896           TypeSpec shardTypeSpec = shard.generate();
897           addType(TypeSpecKind.COMPONENT_SHARD_TYPE, shardTypeSpec);
898         }
899       }
900     }
901 
902     /** Creates and adds the constructor and methods needed for initializing the component. */
addConstructorAndInitializationMethods()903     private void addConstructorAndInitializationMethods() {
904       MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE);
905       ImmutableList<ParameterSpec> parameters = constructorParameters.values().asList();
906 
907       if (isComponentShard()) {
908         // Add a constructor parameter and initialization for each component field. We initialize
909         // these fields immediately so that we don't need to be pass them to each initialize method
910         // and shard constructor.
911         componentFieldsByImplementation()
912             .forEach(
913                 (componentImplementation, field) -> {
914                   if (componentImplementation.equals(ComponentImplementation.this)) {
915                     // For the self-referenced component field,
916                     // just initialize it in the initializer.
917                     addField(
918                         FieldSpecKind.COMPONENT_REQUIREMENT_FIELD,
919                         field.toBuilder().initializer("this").build());
920                   } else {
921                     addField(FieldSpecKind.COMPONENT_REQUIREMENT_FIELD, field);
922                     constructor.addStatement("this.$1N = $1N", field);
923                     constructor.addParameter(field.type, field.name);
924                   }
925                 });
926         constructor.addCode(CodeBlocks.concat(componentRequirementInitializations));
927       }
928       constructor.addParameters(parameters);
929 
930       // TODO(cgdecker): It's not the case that each initialize() method has need for all of the
931       // given parameters. In some cases, those parameters may have already been assigned to fields
932       // which could be referenced instead. In other cases, an initialize method may just not need
933       // some of the parameters because the set of initializations in that partition does not
934       // include any reference to them. Right now, the Dagger code has no way of getting that
935       // information because, among other things, componentImplementation.getImplementations() just
936       // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know
937       // yet whether a field will end up needing to be created for a specific requirement, and we
938       // don't want to create a field that ends up only being used during initialization.
939       CodeBlock args = parameterNames(parameters);
940       ImmutableList<MethodSpec> initializationMethods =
941           createPartitionedMethods(
942               "initialize",
943               // TODO(bcorso): Rather than passing in all of the constructor parameters, keep track
944               // of which parameters are used during initialization and only pass those. This could
945               // be useful for FastInit, where most of the initializations are just calling
946               // SwitchingProvider with no parameters.
947               makeFinal(parameters),
948               initializations,
949               methodName ->
950                   methodBuilder(methodName)
951                       /* TODO(gak): Strictly speaking, we only need the suppression here if we are
952                        * also initializing a raw field in this method, but the structure of this
953                        * code makes it awkward to pass that bit through.  This will be cleaned up
954                        * when we no longer separate fields and initialization as we do now. */
955                       .addAnnotation(suppressWarnings(UNCHECKED)));
956 
957       for (MethodSpec initializationMethod : initializationMethods) {
958         constructor.addStatement("$N($L)", initializationMethod, args);
959         addMethod(MethodSpecKind.INITIALIZE_METHOD, initializationMethod);
960       }
961 
962       if (isComponentShard()) {
963         constructor.addCode(CodeBlocks.concat(shardInitializations));
964       } else {
965         // This initialization is called from the componentShard, so we need to use those args.
966         CodeBlock componentArgs =
967             parameterNames(componentShard.constructorParameters.values().asList());
968         FieldSpec shardField = shardFieldsByImplementation.get(this);
969         shardInitializations.add(CodeBlock.of("$N = new $T($L);", shardField, name, componentArgs));
970       }
971 
972       addMethod(MethodSpecKind.CONSTRUCTOR, constructor.build());
973     }
974 
addCancellationListenerImplementation()975     private void addCancellationListenerImplementation() {
976       MethodSpec.Builder methodBuilder =
977           methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
978               .addModifiers(PUBLIC)
979               .addAnnotation(Override.class)
980               .addParameter(MAY_INTERRUPT_IF_RUNNING_PARAM);
981 
982       // Reversing should order cancellations starting from entry points and going down to leaves
983       // rather than the other way around. This shouldn't really matter but seems *slightly*
984       // preferable because:
985       // When a future that another future depends on is cancelled, that cancellation will propagate
986       // up the future graph toward the entry point. Cancelling in reverse order should ensure that
987       // everything that depends on a particular node has already been cancelled when that node is
988       // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might
989       // propagate through most of the graph, making most of the cancel calls that follow in the
990       // onProducerFutureCancelled method do nothing.
991       if (isComponentShard()) {
992         methodBuilder.addCode(
993             CodeBlocks.concat(ImmutableList.copyOf(shardCancellations).reverse()));
994       } else if (!cancellations.isEmpty()) {
995         shardCancellations.add(
996             CodeBlock.of(
997                 "$N.$N($N);",
998                 shardFieldsByImplementation.get(this),
999                 CANCELLATION_LISTENER_METHOD_NAME,
1000                 MAY_INTERRUPT_IF_RUNNING_PARAM));
1001       }
1002 
1003       ImmutableList<CodeBlock> cancellationStatements =
1004           ImmutableList.copyOf(cancellations.values()).reverse();
1005       if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
1006         methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
1007       } else {
1008         ImmutableList<MethodSpec> cancelProducersMethods =
1009             createPartitionedMethods(
1010                 "cancelProducers",
1011                 ImmutableList.of(MAY_INTERRUPT_IF_RUNNING_PARAM),
1012                 cancellationStatements,
1013                 methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
1014         for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
1015           methodBuilder.addStatement(
1016               "$N($N)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING_PARAM);
1017           addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
1018         }
1019       }
1020 
1021       if (isComponentShard()) {
1022         cancelParentStatement().ifPresent(methodBuilder::addCode);
1023       }
1024 
1025       addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, methodBuilder.build());
1026     }
1027 
cancelParentStatement()1028     private Optional<CodeBlock> cancelParentStatement() {
1029       if (!shouldPropagateCancellationToParent()) {
1030         return Optional.empty();
1031       }
1032       return Optional.of(
1033           CodeBlock.builder()
1034               .addStatement(
1035                   "$L.$N($N)",
1036                   parent.get().componentFieldReference(),
1037                   CANCELLATION_LISTENER_METHOD_NAME,
1038                   MAY_INTERRUPT_IF_RUNNING_PARAM)
1039               .build());
1040     }
1041 
shouldPropagateCancellationToParent()1042     private boolean shouldPropagateCancellationToParent() {
1043       return parent.isPresent()
1044           && parent
1045               .get()
1046               .componentDescriptor()
1047               .cancellationPolicy()
1048               .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
1049               .orElse(false);
1050     }
1051 
1052     /**
1053      * Creates one or more methods, all taking the given {@code parameters}, which partition the
1054      * given list of {@code statements} among themselves such that no method has more than {@code
1055      * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in
1056      * order, will execute the {@code statements} in the given order.
1057      */
createPartitionedMethods( String methodName, Iterable<ParameterSpec> parameters, List<CodeBlock> statements, Function<String, MethodSpec.Builder> methodBuilderCreator)1058     private ImmutableList<MethodSpec> createPartitionedMethods(
1059         String methodName,
1060         Iterable<ParameterSpec> parameters,
1061         List<CodeBlock> statements,
1062         Function<String, MethodSpec.Builder> methodBuilderCreator) {
1063       return Lists.partition(statements, STATEMENTS_PER_METHOD).stream()
1064           .map(
1065               partition ->
1066                   methodBuilderCreator
1067                       .apply(getUniqueMethodName(methodName))
1068                       .addModifiers(PRIVATE)
1069                       .addParameters(parameters)
1070                       .addCode(CodeBlocks.concat(partition))
1071                       .build())
1072           .collect(toImmutableList());
1073     }
1074   }
1075 
constructorRequirements(BindingGraph graph)1076   private static ImmutableList<ComponentRequirement> constructorRequirements(BindingGraph graph) {
1077     if (graph.componentDescriptor().hasCreator()) {
1078       return graph.componentRequirements().asList();
1079     } else if (graph.factoryMethod().isPresent()) {
1080       return graph.factoryMethodParameters().keySet().asList();
1081     } else {
1082       throw new AssertionError(
1083           "Expected either a component creator or factory method but found neither.");
1084     }
1085   }
1086 
makeFinal(List<ParameterSpec> parameters)1087   private static ImmutableList<ParameterSpec> makeFinal(List<ParameterSpec> parameters) {
1088     return parameters.stream()
1089         .map(param -> param.toBuilder().addModifiers(FINAL).build())
1090         .collect(toImmutableList());
1091   }
1092 }
1093