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