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