• 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 com.google.common.base.CaseFormat.LOWER_CAMEL;
20 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
21 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.base.Preconditions.checkState;
24 import static com.squareup.javapoet.TypeSpec.classBuilder;
25 import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
26 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
27 import static javax.lang.model.element.Modifier.FINAL;
28 import static javax.lang.model.element.Modifier.PRIVATE;
29 import static javax.lang.model.element.Modifier.PUBLIC;
30 
31 import com.google.common.base.Supplier;
32 import com.google.common.collect.ImmutableList;
33 import com.google.common.collect.ImmutableSet;
34 import com.google.common.collect.ListMultimap;
35 import com.google.common.collect.MultimapBuilder;
36 import com.squareup.javapoet.AnnotationSpec;
37 import com.squareup.javapoet.ClassName;
38 import com.squareup.javapoet.CodeBlock;
39 import com.squareup.javapoet.FieldSpec;
40 import com.squareup.javapoet.MethodSpec;
41 import com.squareup.javapoet.TypeSpec;
42 import dagger.internal.codegen.base.UniqueNameSet;
43 import dagger.internal.codegen.binding.BindingGraph;
44 import dagger.internal.codegen.binding.BindingRequest;
45 import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
46 import dagger.internal.codegen.binding.ComponentCreatorKind;
47 import dagger.internal.codegen.binding.ComponentDescriptor;
48 import dagger.internal.codegen.binding.ComponentRequirement;
49 import dagger.internal.codegen.binding.KeyVariableNamer;
50 import dagger.internal.codegen.compileroption.CompilerOptions;
51 import dagger.internal.codegen.javapoet.TypeSpecs;
52 import dagger.model.Key;
53 import dagger.model.RequestKind;
54 import java.util.ArrayList;
55 import java.util.HashMap;
56 import java.util.LinkedHashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Optional;
60 import java.util.Set;
61 import javax.lang.model.element.Modifier;
62 import javax.lang.model.element.TypeElement;
63 import javax.lang.model.type.TypeMirror;
64 
65 /** The implementation of a component type. */
66 public final class ComponentImplementation {
67   /** A type of field that this component can contain. */
68   public enum FieldSpecKind {
69     /** A field for a component shard. */
70     COMPONENT_SHARD,
71 
72     /** A field required by the component, e.g. module instances. */
73     COMPONENT_REQUIREMENT_FIELD,
74 
75     /**
76      * A field for the lock and cached value for {@linkplain PrivateMethodBindingExpression
77      * private-method scoped bindings}.
78      */
79     PRIVATE_METHOD_SCOPED_FIELD,
80 
81     /** A framework field for type T, e.g. {@code Provider<T>}. */
82     FRAMEWORK_FIELD,
83 
84     /** A static field that always returns an absent {@code Optional} value for the binding. */
85     ABSENT_OPTIONAL_FIELD
86   }
87 
88   /** A type of method that this component can contain. */
89   // TODO(bcorso, dpb): Change the oder to constructor, initialize, component, then private
90   // (including MIM and AOM—why treat those separately?).
91   public enum MethodSpecKind {
92     /** The component constructor. */
93     CONSTRUCTOR,
94 
95     /** A builder method for the component. (Only used by the root component.) */
96     BUILDER_METHOD,
97 
98     /** A private method that wraps dependency expressions. */
99     PRIVATE_METHOD,
100 
101     /** An initialization method that initializes component requirements and framework types. */
102     INITIALIZE_METHOD,
103 
104     /** An implementation of a component interface method. */
105     COMPONENT_METHOD,
106 
107     /** A private method that encapsulates members injection logic for a binding. */
108     MEMBERS_INJECTION_METHOD,
109 
110     /** A static method that always returns an absent {@code Optional} value for the binding. */
111     ABSENT_OPTIONAL_METHOD,
112 
113     /**
114      * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
115      * method for a production component.
116      */
117     CANCELLATION_LISTENER_METHOD,
118     ;
119   }
120 
121   /** A type of nested class that this component can contain. */
122   public enum TypeSpecKind {
123     /** A factory class for a present optional binding. */
124     PRESENT_FACTORY,
125 
126     /** A class for the component creator (only used by the root component.) */
127     COMPONENT_CREATOR,
128 
129     /** A provider class for a component provision. */
130     COMPONENT_PROVISION_FACTORY,
131 
132     /** A class for the subcomponent or subcomponent builder. */
133     SUBCOMPONENT
134   }
135 
136   private ComponentImplementation currentShard = this;
137   private final Map<Key, ComponentImplementation> shardsByKey = new HashMap<>();
138   private final Optional<ComponentImplementation> shardOwner;
139   private final BindingGraph graph;
140   private final ClassName name;
141   private final TypeSpec.Builder component;
142   private final SubcomponentNames subcomponentNames;
143   private final CompilerOptions compilerOptions;
144   private final CodeBlock externalReferenceBlock;
145   private final UniqueNameSet componentFieldNames = new UniqueNameSet();
146   private final UniqueNameSet componentMethodNames = new UniqueNameSet();
147   private final List<CodeBlock> initializations = new ArrayList<>();
148   private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
149   private final Map<ComponentRequirement, String> componentRequirementParameterNames =
150       new HashMap<>();
151   private final Set<Key> cancellableProducerKeys = new LinkedHashSet<>();
152   private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
153       MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
154   private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
155       MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
156   private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
157       MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
158   private final List<Supplier<TypeSpec>> typeSuppliers = new ArrayList<>();
159 
ComponentImplementation( BindingGraph graph, ClassName name, SubcomponentNames subcomponentNames, CompilerOptions compilerOptions)160   private ComponentImplementation(
161       BindingGraph graph,
162       ClassName name,
163       SubcomponentNames subcomponentNames,
164       CompilerOptions compilerOptions) {
165     this.graph = graph;
166     this.name = name;
167     this.component = classBuilder(name);
168     this.subcomponentNames = subcomponentNames;
169     this.shardOwner = Optional.empty();
170     this.externalReferenceBlock = CodeBlock.of("$T.this", name);
171     this.compilerOptions = compilerOptions;
172   }
173 
ComponentImplementation(ComponentImplementation shardOwner, ClassName shardName)174   private ComponentImplementation(ComponentImplementation shardOwner, ClassName shardName) {
175     this.graph = shardOwner.graph;
176     this.name = shardName;
177     this.component = classBuilder(shardName);
178     this.subcomponentNames = shardOwner.subcomponentNames;
179     this.compilerOptions = shardOwner.compilerOptions;
180     this.shardOwner = Optional.of(shardOwner);
181     String fieldName = UPPER_CAMEL.to(LOWER_CAMEL, name.simpleName());
182     String uniqueFieldName = shardOwner.getUniqueFieldName(fieldName);
183     this.externalReferenceBlock = CodeBlock.of("$T.this.$N", shardOwner.name, uniqueFieldName);
184     shardOwner.addTypeSupplier(() -> generate().build());
185     shardOwner.addField(
186         FieldSpecKind.COMPONENT_SHARD,
187         FieldSpec.builder(name, uniqueFieldName, PRIVATE, FINAL)
188             .initializer("new $T()", name)
189             .build());
190   }
191 
192   /** Returns a component implementation for a top-level component. */
topLevelComponentImplementation( BindingGraph graph, ClassName name, SubcomponentNames subcomponentNames, CompilerOptions compilerOptions)193   public static ComponentImplementation topLevelComponentImplementation(
194       BindingGraph graph,
195       ClassName name,
196       SubcomponentNames subcomponentNames,
197       CompilerOptions compilerOptions) {
198     return new ComponentImplementation(graph, name, subcomponentNames, compilerOptions);
199   }
200 
201   /** Returns a component implementation that is a child of the current implementation. */
childComponentImplementation(BindingGraph graph)202   public ComponentImplementation childComponentImplementation(BindingGraph graph) {
203     checkState(!shardOwner.isPresent(), "Shards cannot create child components.");
204     ClassName childName = getSubcomponentName(graph.componentDescriptor());
205     return new ComponentImplementation(graph, childName, subcomponentNames, compilerOptions);
206   }
207 
208   /** Returns a component implementation that is a shard of the current implementation. */
shardImplementation(Key key)209   public ComponentImplementation shardImplementation(Key key) {
210     checkState(!shardOwner.isPresent(), "Shards cannot create other shards.");
211     if (!shardsByKey.containsKey(key)) {
212       int keysPerShard = compilerOptions.keysPerComponentShard(graph.componentTypeElement());
213       if (!shardsByKey.isEmpty() && shardsByKey.size() % keysPerShard == 0) {
214         ClassName shardName = name.nestedClass("Shard" + shardsByKey.size() / keysPerShard);
215         currentShard = new ComponentImplementation(this, shardName);
216       }
217       shardsByKey.put(key, currentShard);
218     }
219     return shardsByKey.get(key);
220   }
221 
222   /** Returns a reference to this compenent when called from a class nested in this component. */
externalReferenceBlock()223   public CodeBlock externalReferenceBlock() {
224     return externalReferenceBlock;
225   }
226 
227   // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
228   // need it.
229   /** Returns the binding graph for the component being generated. */
graph()230   public BindingGraph graph() {
231     return graph;
232   }
233 
234   /** Returns the descriptor for the component being generated. */
componentDescriptor()235   public ComponentDescriptor componentDescriptor() {
236     return graph.componentDescriptor();
237   }
238 
239   /** Returns the name of the component. */
name()240   public ClassName name() {
241     return name;
242   }
243 
244   /** Returns whether or not the implementation is nested within another class. */
isNested()245   public boolean isNested() {
246     return name.enclosingClassName() != null;
247   }
248 
249   /**
250    * Returns the kind of this component's creator.
251    *
252    * @throws IllegalStateException if the component has no creator
253    */
creatorKind()254   private ComponentCreatorKind creatorKind() {
255     checkState(componentDescriptor().hasCreator());
256     return componentDescriptor()
257         .creatorDescriptor()
258         .map(ComponentCreatorDescriptor::kind)
259         .orElse(BUILDER);
260   }
261 
262   /**
263    * Returns the name of the creator class for this component. It will be a sibling of this
264    * generated class unless this is a top-level component, in which case it will be nested.
265    */
getCreatorName()266   public ClassName getCreatorName() {
267     return isNested()
268         ? name.peerClass(subcomponentNames.getCreatorName(componentDescriptor()))
269         : name.nestedClass(creatorKind().typeName());
270   }
271 
272   /** Returns the name of the nested implementation class for a child component. */
getSubcomponentName(ComponentDescriptor childDescriptor)273   private ClassName getSubcomponentName(ComponentDescriptor childDescriptor) {
274     checkArgument(
275         componentDescriptor().childComponents().contains(childDescriptor),
276         "%s is not a child component of %s",
277         childDescriptor.typeElement(),
278         componentDescriptor().typeElement());
279     return name.nestedClass(subcomponentNames.get(childDescriptor) + "Impl");
280   }
281 
282   /**
283    * Returns the simple name of the creator implementation class for the given subcomponent creator
284    * {@link Key}.
285    */
getSubcomponentCreatorSimpleName(Key key)286   String getSubcomponentCreatorSimpleName(Key key) {
287     return subcomponentNames.getCreatorName(key);
288   }
289 
290   /** Returns {@code true} if {@code type} is accessible from the generated component. */
isTypeAccessible(TypeMirror type)291   boolean isTypeAccessible(TypeMirror type) {
292     return isTypeAccessibleFrom(type, name.packageName());
293   }
294 
295   /** Adds the given super type to the component. */
addSupertype(TypeElement supertype)296   public void addSupertype(TypeElement supertype) {
297     TypeSpecs.addSupertype(component, supertype);
298   }
299 
300   // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name?
301   /** Adds the given field to the component. */
addField(FieldSpecKind fieldKind, FieldSpec fieldSpec)302   public void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
303     fieldSpecsMap.put(fieldKind, fieldSpec);
304   }
305 
306   // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name?
307   /** Adds the given method to the component. */
addMethod(MethodSpecKind methodKind, MethodSpec methodSpec)308   public void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
309     methodSpecsMap.put(methodKind, methodSpec);
310   }
311 
312   /** Adds the given annotation to the component. */
addAnnotation(AnnotationSpec annotation)313   public void addAnnotation(AnnotationSpec annotation) {
314     component.addAnnotation(annotation);
315   }
316 
317   /** Adds the given type to the component. */
addType(TypeSpecKind typeKind, TypeSpec typeSpec)318   public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
319     typeSpecsMap.put(typeKind, typeSpec);
320   }
321 
322   /** Adds a {@link Supplier} for the SwitchingProvider for the component. */
addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier)323   void addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier) {
324     typeSuppliers.add(typeSpecSupplier);
325   }
326 
327   /** Adds the given code block to the initialize methods of the component. */
addInitialization(CodeBlock codeBlock)328   void addInitialization(CodeBlock codeBlock) {
329     initializations.add(codeBlock);
330   }
331 
332   /** Adds the given code block that initializes a {@link ComponentRequirement}. */
addComponentRequirementInitialization(CodeBlock codeBlock)333   void addComponentRequirementInitialization(CodeBlock codeBlock) {
334     componentRequirementInitializations.add(codeBlock);
335   }
336 
337   /**
338    * Marks the given key of a producer as one that should have a cancellation statement in the
339    * cancellation listener method of the component.
340    */
addCancellableProducerKey(Key key)341   void addCancellableProducerKey(Key key) {
342     cancellableProducerKeys.add(key);
343   }
344 
345   /** Returns a new, unique field name for the component based on the given name. */
getUniqueFieldName(String name)346   String getUniqueFieldName(String name) {
347     return componentFieldNames.getUniqueName(name);
348   }
349 
350   /** Returns a new, unique method name for the component based on the given name. */
getUniqueMethodName(String name)351   public String getUniqueMethodName(String name) {
352     return componentMethodNames.getUniqueName(name);
353   }
354 
355   /** Returns a new, unique method name for a getter method for the given request. */
getUniqueMethodName(BindingRequest request)356   String getUniqueMethodName(BindingRequest request) {
357     return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
358   }
359 
uniqueMethodName(BindingRequest request, String bindingName)360   private String uniqueMethodName(BindingRequest request, String bindingName) {
361     // This name is intentionally made to match the name for fields in fastInit
362     // in order to reduce the constant pool size. b/162004246
363     String baseMethodName = bindingName
364         + (request.isRequestKind(RequestKind.INSTANCE)
365             ? ""
366             : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName()));
367     return getUniqueMethodName(baseMethodName);
368   }
369 
370   /**
371    * Gets the parameter name to use for the given requirement for this component, starting with the
372    * given base name if no parameter name has already been selected for the requirement.
373    */
getParameterName(ComponentRequirement requirement, String baseName)374   public String getParameterName(ComponentRequirement requirement, String baseName) {
375     return componentRequirementParameterNames.computeIfAbsent(
376         requirement, r -> getUniqueFieldName(baseName));
377   }
378 
379   /** Claims a new method name for the component. Does nothing if method name already exists. */
claimMethodName(CharSequence name)380   public void claimMethodName(CharSequence name) {
381     componentMethodNames.claim(name);
382   }
383 
384   /** Returns the list of {@link CodeBlock}s that need to go in the initialize method. */
getInitializations()385   public ImmutableList<CodeBlock> getInitializations() {
386     return ImmutableList.copyOf(initializations);
387   }
388 
389   /**
390    * Returns a list of {@link CodeBlock}s for initializing {@link ComponentRequirement}s.
391    *
392    * <p>These initializations are kept separate from {@link #getInitializations()} because they must
393    * be executed before the initializations of any framework instance initializations in a
394    * superclass implementation that may depend on the instances. We cannot use the same strategy
395    * that we use for framework instances (i.e. wrap in a {@link dagger.internal.DelegateFactory} or
396    * {@link dagger.producers.internal.DelegateProducer} since the types of these initialized fields
397    * have no interface type that we can write a proxy for.
398    */
399   // TODO(cgdecker): can these be inlined with getInitializations() now that we've turned down
400   // ahead-of-time subcomponents?
getComponentRequirementInitializations()401   public ImmutableList<CodeBlock> getComponentRequirementInitializations() {
402     return ImmutableList.copyOf(componentRequirementInitializations);
403   }
404 
405   /**
406    * Returns the list of producer {@link Key}s that need cancellation statements in the cancellation
407    * listener method.
408    */
getCancellableProducerKeys()409   public ImmutableList<Key> getCancellableProducerKeys() {
410     return ImmutableList.copyOf(cancellableProducerKeys);
411   }
412 
413   /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
generate()414   public TypeSpec.Builder generate() {
415     modifiers().forEach(component::addModifiers);
416     fieldSpecsMap.asMap().values().forEach(component::addFields);
417     methodSpecsMap.asMap().values().forEach(component::addMethods);
418     typeSpecsMap.asMap().values().forEach(component::addTypes);
419     typeSuppliers.stream().map(Supplier::get).forEach(component::addType);
420     return component;
421   }
422 
modifiers()423   private ImmutableSet<Modifier> modifiers() {
424     if (isNested()) {
425       return ImmutableSet.of(PRIVATE, FINAL);
426     }
427     return graph.componentTypeElement().getModifiers().contains(PUBLIC)
428         // TODO(ronshapiro): perhaps all generated components should be non-public?
429         ? ImmutableSet.of(PUBLIC, FINAL)
430         : ImmutableSet.of(FINAL);
431   }
432 }
433