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