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