1 /* 2 * Copyright (C) 2015 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; 18 19 import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; 20 import static com.google.auto.common.MoreTypes.asDeclared; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static com.google.common.base.Preconditions.checkState; 23 import static com.google.common.base.Predicates.in; 24 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 25 import static com.squareup.javapoet.MethodSpec.methodBuilder; 26 import static dagger.internal.codegen.BindingRequest.bindingRequest; 27 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER; 28 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.BUILDER_METHOD; 29 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD; 30 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD; 31 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CONSTRUCTOR; 32 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.INITIALIZE_METHOD; 33 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MODIFIABLE_BINDING_METHOD; 34 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR; 35 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.SUBCOMPONENT; 36 import static dagger.internal.codegen.DaggerStreams.toImmutableList; 37 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 38 import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames; 39 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 40 import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE; 41 import static javax.lang.model.element.Modifier.ABSTRACT; 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.PROTECTED; 45 import static javax.lang.model.element.Modifier.PUBLIC; 46 import static javax.lang.model.element.Modifier.STATIC; 47 48 import com.google.auto.common.MoreTypes; 49 import com.google.common.collect.ImmutableList; 50 import com.google.common.collect.ImmutableListMultimap; 51 import com.google.common.collect.ImmutableMap; 52 import com.google.common.collect.ImmutableSet; 53 import com.google.common.collect.Iterables; 54 import com.google.common.collect.Lists; 55 import com.google.common.collect.Maps; 56 import com.google.common.collect.Multimaps; 57 import com.google.common.collect.Sets; 58 import com.squareup.javapoet.AnnotationSpec; 59 import com.squareup.javapoet.ClassName; 60 import com.squareup.javapoet.CodeBlock; 61 import com.squareup.javapoet.MethodSpec; 62 import com.squareup.javapoet.ParameterSpec; 63 import com.squareup.javapoet.TypeName; 64 import com.squareup.javapoet.TypeSpec; 65 import dagger.internal.ComponentDefinitionType; 66 import dagger.internal.Preconditions; 67 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; 68 import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod; 69 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod; 70 import dagger.internal.codegen.javapoet.AnnotationSpecs; 71 import dagger.internal.codegen.javapoet.CodeBlocks; 72 import dagger.internal.codegen.langmodel.DaggerElements; 73 import dagger.internal.codegen.langmodel.DaggerTypes; 74 import dagger.model.Key; 75 import dagger.producers.internal.CancellationListener; 76 import dagger.producers.internal.Producers; 77 import java.util.Collection; 78 import java.util.List; 79 import java.util.Map; 80 import java.util.Optional; 81 import java.util.function.Function; 82 import javax.inject.Inject; 83 import javax.lang.model.element.ExecutableElement; 84 import javax.lang.model.type.DeclaredType; 85 86 /** A builder of {@link ComponentImplementation}s. */ 87 abstract class ComponentImplementationBuilder { 88 private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning"; 89 90 /** 91 * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method 92 * before they get partitioned. 93 */ 94 private static final int STATEMENTS_PER_METHOD = 100; 95 96 private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled"; 97 98 // TODO(ronshapiro): replace this with composition instead of inheritance so we don't have 99 // non-final fields 100 @Inject BindingGraph graph; 101 @Inject ComponentBindingExpressions bindingExpressions; 102 @Inject ComponentRequirementExpressions componentRequirementExpressions; 103 @Inject ComponentImplementation componentImplementation; 104 @Inject ComponentCreatorImplementationFactory componentCreatorImplementationFactory; 105 @Inject DaggerTypes types; 106 @Inject DaggerElements elements; 107 @Inject CompilerOptions compilerOptions; 108 @Inject ComponentImplementationFactory componentImplementationFactory; 109 @Inject TopLevelImplementationComponent topLevelImplementationComponent; 110 private boolean done; 111 112 /** 113 * Returns a {@link ComponentImplementation} for this component. This is only intended to be 114 * called once (and will throw on successive invocations). If the component must be regenerated, 115 * use a new instance. 116 */ build()117 final ComponentImplementation build() { 118 checkState( 119 !done, 120 "ComponentImplementationBuilder has already built the ComponentImplementation for [%s].", 121 componentImplementation.name()); 122 setSupertype(); 123 componentImplementation.setCreatorImplementation( 124 componentCreatorImplementationFactory.create( 125 componentImplementation, Optional.of(componentImplementation.graph()))); 126 componentImplementation 127 .creatorImplementation() 128 .map(ComponentCreatorImplementation::spec) 129 .ifPresent(this::addCreatorClass); 130 131 getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements) 132 .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName())); 133 componentImplementation 134 .superclassImplementation() 135 .ifPresent( 136 superclassImplementation -> { 137 superclassImplementation 138 .getAllModifiableMethodNames() 139 .forEach(componentImplementation::claimMethodName); 140 }); 141 142 addFactoryMethods(); 143 addInterfaceMethods(); 144 addChildComponents(); 145 implementModifiableModuleMethods(); 146 147 addConstructorAndInitializationMethods(); 148 149 if (graph.componentDescriptor().isProduction()) { 150 addCancellationListenerImplementation(); 151 } 152 153 if (componentImplementation.isAbstract() 154 && !componentImplementation.baseImplementation().isPresent()) { 155 componentImplementation.addAnnotation(compilerOptions.toGenerationOptionsAnnotation()); 156 } 157 158 if (componentImplementation.shouldEmitModifiableMetadataAnnotations()) { 159 componentImplementation.addAnnotation( 160 AnnotationSpec.builder(ComponentDefinitionType.class) 161 .addMember("value", "$T.class", graph.componentTypeElement()) 162 .build()); 163 } 164 165 done = true; 166 return componentImplementation; 167 } 168 169 /** Set the supertype for this generated class. */ setSupertype()170 private void setSupertype() { 171 if (componentImplementation.superclassImplementation().isPresent()) { 172 componentImplementation.addSuperclass( 173 componentImplementation.superclassImplementation().get().name()); 174 } else { 175 componentImplementation.addSupertype(graph.componentTypeElement()); 176 } 177 } 178 179 /** 180 * Adds {@code creator} as a nested creator class. Root components and subcomponents will nest 181 * this in different classes. 182 */ addCreatorClass(TypeSpec creator)183 protected abstract void addCreatorClass(TypeSpec creator); 184 185 /** Adds component factory methods. */ addFactoryMethods()186 protected abstract void addFactoryMethods(); 187 addInterfaceMethods()188 protected void addInterfaceMethods() { 189 // Each component method may have been declared by several supertypes. We want to implement 190 // only one method for each distinct signature. 191 ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature = 192 Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature); 193 for (List<ComponentMethodDescriptor> methodsWithSameSignature : 194 Multimaps.asMap(componentMethodsBySignature).values()) { 195 ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get(); 196 MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod); 197 198 if (compilerOptions.aheadOfTimeSubcomponents()) { 199 addPossiblyModifiableInterfaceMethod(anyOneMethod, methodSpec); 200 } else { 201 componentImplementation.addMethod(COMPONENT_METHOD, methodSpec); 202 } 203 } 204 } 205 206 /** 207 * Adds a component interface method in ahead-of-time subcomponents mode. If the binding that 208 * implements the method is modifiable, registers the method. 209 */ addPossiblyModifiableInterfaceMethod( ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod)210 private void addPossiblyModifiableInterfaceMethod( 211 ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod) { 212 if (methodDescriptor.dependencyRequest().isPresent() 213 && componentImplementation 214 .getModifiableBindingMethod(bindingRequest(methodDescriptor.dependencyRequest().get())) 215 .isPresent()) { 216 // If there are multiple component methods that are modifiable and for the same binding 217 // request, implement all but one in the base implementation to delegate to the one that 218 // will remain (and be registered) modifiable 219 checkState(componentImplementation.isAbstract() && !componentImplementation.isNested()); 220 componentImplementation.addMethod( 221 COMPONENT_METHOD, implementedComponentMethod.toBuilder().addModifiers(FINAL).build()); 222 } else { 223 // TODO(b/117833324): Can this class be the one to interface with ComponentImplementation 224 // instead of having it go through ModifiableBindingExpressions? 225 bindingExpressions 226 .modifiableBindingExpressions() 227 .addPossiblyModifiableComponentMethod(methodDescriptor, implementedComponentMethod); 228 } 229 } 230 addCancellationListenerImplementation()231 private void addCancellationListenerImplementation() { 232 componentImplementation.addSupertype(elements.getTypeElement(CancellationListener.class)); 233 componentImplementation.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME); 234 235 ImmutableList<ParameterSpec> parameters = 236 ImmutableList.of(ParameterSpec.builder(boolean.class, MAY_INTERRUPT_IF_RUNNING).build()); 237 238 MethodSpec.Builder methodBuilder = 239 methodBuilder(CANCELLATION_LISTENER_METHOD_NAME) 240 .addModifiers(PUBLIC) 241 .addAnnotation(Override.class) 242 .addParameters(parameters); 243 if (componentImplementation.superclassImplementation().isPresent()) { 244 methodBuilder.addStatement( 245 "super.$L($L)", CANCELLATION_LISTENER_METHOD_NAME, MAY_INTERRUPT_IF_RUNNING); 246 } 247 248 ImmutableList<CodeBlock> cancellationStatements = cancellationStatements(); 249 250 if (cancellationStatements.size() < STATEMENTS_PER_METHOD) { 251 methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build(); 252 } else { 253 ImmutableList<MethodSpec> cancelProducersMethods = 254 createPartitionedMethods( 255 "cancelProducers", 256 parameters, 257 cancellationStatements, 258 methodName -> methodBuilder(methodName).addModifiers(PRIVATE)); 259 for (MethodSpec cancelProducersMethod : cancelProducersMethods) { 260 methodBuilder.addStatement("$N($L)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING); 261 componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, cancelProducersMethod); 262 } 263 } 264 265 Optional<CodeBlock> cancelParentStatement = cancelParentStatement(); 266 cancelParentStatement.ifPresent(methodBuilder::addCode); 267 268 if (cancellationStatements.isEmpty() 269 && !cancelParentStatement.isPresent() 270 && componentImplementation.superclassImplementation().isPresent()) { 271 // Partial child implementations that have no new cancellations don't need to override 272 // the method just to call super(). 273 return; 274 } 275 276 componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build()); 277 } 278 cancellationStatements()279 private ImmutableList<CodeBlock> cancellationStatements() { 280 // Reversing should order cancellations starting from entry points and going down to leaves 281 // rather than the other way around. This shouldn't really matter but seems *slightly* 282 // preferable because: 283 // When a future that another future depends on is cancelled, that cancellation will propagate 284 // up the future graph toward the entry point. Cancelling in reverse order should ensure that 285 // everything that depends on a particular node has already been cancelled when that node is 286 // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might 287 // propagate through most of the graph, making most of the cancel calls that follow in the 288 // onProducerFutureCancelled method do nothing. 289 ImmutableList<Key> cancellationKeys = 290 componentImplementation.getCancellableProducerKeys().reverse(); 291 292 ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder(); 293 for (Key cancellationKey : cancellationKeys) { 294 cancellationStatements.add( 295 CodeBlock.of( 296 "$T.cancel($L, $N);", 297 Producers.class, 298 bindingExpressions 299 .getDependencyExpression( 300 bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE), 301 componentImplementation.name()) 302 .codeBlock(), 303 MAY_INTERRUPT_IF_RUNNING)); 304 } 305 return cancellationStatements.build(); 306 } 307 cancelParentStatement()308 protected Optional<CodeBlock> cancelParentStatement() { 309 // Returns empty by default. Overridden in subclass(es) to add a statement if and only if the 310 // component being generated is a concrete subcomponent implementation with a parent that 311 // allows cancellation to propagate to it from subcomponents. 312 return Optional.empty(); 313 } 314 315 /** 316 * For final components, reimplements all modifiable module methods that may have been modified. 317 */ implementModifiableModuleMethods()318 private void implementModifiableModuleMethods() { 319 if (componentImplementation.isAbstract()) { 320 return; 321 } 322 componentImplementation 323 .getAllModifiableModuleMethods() 324 .forEach(this::implementModifiableModuleMethod); 325 } 326 implementModifiableModuleMethod(ComponentRequirement module, String methodName)327 private void implementModifiableModuleMethod(ComponentRequirement module, String methodName) { 328 // TODO(b/117833324): only reimplement methods for modules that were abstract or were repeated 329 // by an ancestor component. 330 componentImplementation.addMethod( 331 MODIFIABLE_BINDING_METHOD, 332 methodBuilder(methodName) 333 .addAnnotation(Override.class) 334 .addModifiers(PROTECTED) 335 .returns(TypeName.get(module.type())) 336 .addStatement( 337 componentRequirementExpressions 338 .getExpression(module) 339 .getModifiableModuleMethodExpression(componentImplementation.name())) 340 .build()); 341 } 342 getMethodSignature(ComponentMethodDescriptor method)343 private MethodSignature getMethodSignature(ComponentMethodDescriptor method) { 344 return MethodSignature.forComponentMethod( 345 method, MoreTypes.asDeclared(graph.componentTypeElement().asType()), types); 346 } 347 addChildComponents()348 private void addChildComponents() { 349 for (BindingGraph subgraph : graph.subgraphs()) { 350 // TODO(b/117833324): Can an abstract inner subcomponent implementation be elided if it's 351 // totally empty? 352 componentImplementation.addChild( 353 subgraph.componentDescriptor(), buildChildImplementation(subgraph)); 354 } 355 } 356 buildChildImplementation(BindingGraph childGraph)357 private ComponentImplementation buildChildImplementation(BindingGraph childGraph) { 358 ComponentImplementation childImplementation = 359 compilerOptions.aheadOfTimeSubcomponents() 360 ? abstractInnerSubcomponent(childGraph) 361 : concreteSubcomponent(childGraph); 362 return topLevelImplementationComponent 363 .currentImplementationSubcomponentBuilder() 364 .componentImplementation(childImplementation) 365 .bindingGraph(childGraph) 366 .parentBuilder(Optional.of(this)) 367 .parentBindingExpressions(Optional.of(bindingExpressions)) 368 .parentRequirementExpressions(Optional.of(componentRequirementExpressions)) 369 .build() 370 .subcomponentBuilder() 371 .build(); 372 } 373 374 /** Creates an inner abstract subcomponent implementation. */ abstractInnerSubcomponent(BindingGraph childGraph)375 private ComponentImplementation abstractInnerSubcomponent(BindingGraph childGraph) { 376 return componentImplementation.childComponentImplementation( 377 childGraph, 378 Optional.of( 379 componentImplementationFactory.findChildSuperclassImplementation( 380 childGraph.componentDescriptor(), componentImplementation)), 381 PROTECTED, 382 componentImplementation.isAbstract() ? ABSTRACT : FINAL); 383 } 384 385 /** Creates a concrete inner subcomponent implementation. */ concreteSubcomponent(BindingGraph childGraph)386 private ComponentImplementation concreteSubcomponent(BindingGraph childGraph) { 387 return componentImplementation.childComponentImplementation( 388 childGraph, 389 Optional.empty(), // superclassImplementation 390 PRIVATE, 391 FINAL); 392 } 393 394 /** Creates and adds the constructor and methods needed for initializing the component. */ addConstructorAndInitializationMethods()395 private void addConstructorAndInitializationMethods() { 396 MethodSpec.Builder constructor = componentConstructorBuilder(); 397 if (!componentImplementation.isAbstract()) { 398 implementInitializationMethod(constructor, initializationParameters()); 399 } else if (componentImplementation.hasInitializations()) { 400 addConfigureInitializationMethod(); 401 } 402 componentImplementation.addMethod(CONSTRUCTOR, constructor.build()); 403 } 404 405 /** Returns a builder for the component's constructor. */ componentConstructorBuilder()406 private MethodSpec.Builder componentConstructorBuilder() { 407 return constructorBuilder() 408 .addModifiers(componentImplementation.isAbstract() ? PROTECTED : PRIVATE); 409 } 410 411 /** Adds parameters and code to the given {@code initializationMethod}. */ implementInitializationMethod( MethodSpec.Builder initializationMethod, ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters)412 private void implementInitializationMethod( 413 MethodSpec.Builder initializationMethod, 414 ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters) { 415 initializationMethod.addParameters(initializationParameters.values()); 416 initializationMethod.addCode( 417 CodeBlocks.concat(componentImplementation.getComponentRequirementInitializations())); 418 componentImplementation 419 .superConfigureInitializationMethod() 420 .ifPresent( 421 superConfigureInitializationMethod -> 422 addSuperConfigureInitializationCall( 423 initializationMethod, 424 initializationParameters, 425 superConfigureInitializationMethod)); 426 addInitializeMethods(initializationMethod, initializationParameters.values().asList()); 427 } 428 429 /** Creates and adds a {@code configureInitializatoin} method to the component. */ addConfigureInitializationMethod()430 private void addConfigureInitializationMethod() { 431 MethodSpec.Builder method = configureInitializationMethodBuilder(); 432 ImmutableMap<ComponentRequirement, ParameterSpec> parameters = initializationParameters(); 433 implementInitializationMethod(method, parameters); 434 componentImplementation.setConfigureInitializationMethod( 435 ConfigureInitializationMethod.create(method.build(), parameters.keySet())); 436 } 437 438 /** Returns a builder for the component's {@code configureInitialization} method. */ configureInitializationMethodBuilder()439 private MethodSpec.Builder configureInitializationMethodBuilder() { 440 String methodName = componentImplementation.getUniqueMethodName("configureInitialization"); 441 MethodSpec.Builder configureInitialization = methodBuilder(methodName).addModifiers(PROTECTED); 442 if (overridesSuperclassConfigureInitialization(configureInitialization.build())) { 443 configureInitialization.addAnnotation(Override.class); 444 } 445 return configureInitialization; 446 } 447 448 /** 449 * Returns whether or not the given method overrides a configureInitialization method from a 450 * superclass. 451 */ overridesSuperclassConfigureInitialization(MethodSpec method)452 private boolean overridesSuperclassConfigureInitialization(MethodSpec method) { 453 for (Optional<ComponentImplementation> currentSuperImplementation = 454 componentImplementation.superclassImplementation(); 455 currentSuperImplementation.isPresent(); 456 currentSuperImplementation = currentSuperImplementation.get().superclassImplementation()) { 457 Optional<MethodSpec> superConfigureInitializationMethod = 458 currentSuperImplementation.get().configureInitializationMethod().map(m -> m.spec()); 459 if (superConfigureInitializationMethod 460 .filter(superMethod -> haveSameSignature(method, superMethod)) 461 .isPresent()) { 462 return true; 463 } 464 } 465 466 return false; 467 } 468 469 /** Returns whether or not methods {@code a} and {@code b} have the same signature. */ haveSameSignature(MethodSpec a, MethodSpec b)470 private boolean haveSameSignature(MethodSpec a, MethodSpec b) { 471 return a.name.equals(b.name) && types(a.parameters).equals(types(b.parameters)); 472 } 473 types(List<ParameterSpec> parameters)474 private ImmutableList<TypeName> types(List<ParameterSpec> parameters) { 475 return parameters.stream().map(parameter -> parameter.type).collect(toImmutableList()); 476 } 477 478 /** 479 * Adds a call to the superclass's {@code configureInitialization} method to the given {@code 480 * callingMethod}. 481 */ addSuperConfigureInitializationCall( MethodSpec.Builder callingMethod, ImmutableMap<ComponentRequirement, ParameterSpec> parameters, ConfigureInitializationMethod superConfigureInitializationMethod)482 private void addSuperConfigureInitializationCall( 483 MethodSpec.Builder callingMethod, 484 ImmutableMap<ComponentRequirement, ParameterSpec> parameters, 485 ConfigureInitializationMethod superConfigureInitializationMethod) { 486 // This component's constructor may not have all of the parameters that the superclass's 487 // configureInitialization method takes, because the superclass configureInitialization method 488 // necessarily accepts things that it can't know whether will be needed or not. If they aren't 489 // needed (as is the case when the constructor doesn't have a parameter for the module), just 490 // pass null to super.configureInitialization for that parameter; it won't be used. 491 CodeBlock args = 492 superConfigureInitializationMethod.parameters().stream() 493 .map( 494 requirement -> 495 parameters.containsKey(requirement) 496 ? CodeBlock.of("$N", parameters.get(requirement)) 497 : CodeBlock.of("null")) 498 .collect(toParametersCodeBlock()); 499 500 String qualifier = 501 haveSameSignature(callingMethod.build(), superConfigureInitializationMethod.spec()) 502 ? "super." 503 : ""; 504 callingMethod.addStatement( 505 qualifier + "$N($L)", superConfigureInitializationMethod.spec(), args); 506 } 507 508 /** 509 * Adds any necessary {@code initialize} methods to the component and adds calls to them to the 510 * given {@code callingMethod}. 511 */ addInitializeMethods( MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters)512 private void addInitializeMethods( 513 MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters) { 514 // TODO(cgdecker): It's not the case that each initialize() method has need for all of the 515 // given parameters. In some cases, those parameters may have already been assigned to fields 516 // which could be referenced instead. In other cases, an initialize method may just not need 517 // some of the parameters because the set of initializations in that partition does not 518 // include any reference to them. Right now, the Dagger code has no way of getting that 519 // information because, among other things, componentImplementation.getImplementations() just 520 // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know 521 // yet whether a field will end up needing to be created for a specific requirement, and we 522 // don't want to create a field that ends up only being used during initialization. 523 CodeBlock args = parameterNames(parameters); 524 ImmutableList<MethodSpec> methods = 525 createPartitionedMethods( 526 "initialize", 527 makeFinal(parameters), 528 componentImplementation.getInitializations(), 529 methodName -> 530 methodBuilder(methodName) 531 .addModifiers(PRIVATE) 532 /* TODO(gak): Strictly speaking, we only need the suppression here if we are 533 * also initializing a raw field in this method, but the structure of this 534 * code makes it awkward to pass that bit through. This will be cleaned up 535 * when we no longer separate fields and initialization as we do now. */ 536 .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED))); 537 for (MethodSpec method : methods) { 538 callingMethod.addStatement("$N($L)", method, args); 539 componentImplementation.addMethod(INITIALIZE_METHOD, method); 540 } 541 } 542 543 /** 544 * Creates one or more methods, all taking the given {@code parameters}, which partition the given 545 * list of {@code statements} among themselves such that no method has more than {@code 546 * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in order, 547 * will execute the {@code statements} in the given order. 548 */ createPartitionedMethods( String methodName, Iterable<ParameterSpec> parameters, List<CodeBlock> statements, Function<String, MethodSpec.Builder> methodBuilderCreator)549 private ImmutableList<MethodSpec> createPartitionedMethods( 550 String methodName, 551 Iterable<ParameterSpec> parameters, 552 List<CodeBlock> statements, 553 Function<String, MethodSpec.Builder> methodBuilderCreator) { 554 return Lists.partition(statements, STATEMENTS_PER_METHOD).stream() 555 .map( 556 partition -> 557 methodBuilderCreator 558 .apply(componentImplementation.getUniqueMethodName(methodName)) 559 .addParameters(parameters) 560 .addCode(CodeBlocks.concat(partition)) 561 .build()) 562 .collect(toImmutableList()); 563 } 564 565 /** Returns the given parameters with a final modifier added. */ makeFinal(Collection<ParameterSpec> parameters)566 private final ImmutableList<ParameterSpec> makeFinal(Collection<ParameterSpec> parameters) { 567 return parameters.stream() 568 .map(param -> param.toBuilder().addModifiers(FINAL).build()) 569 .collect(toImmutableList()); 570 } 571 572 /** 573 * Returns the parameters for the constructor or {@code configureInitilization} method as a map 574 * from the requirement the parameter fulfills to the spec for the parameter. 575 */ initializationParameters()576 private final ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters() { 577 Map<ComponentRequirement, ParameterSpec> parameters; 578 if (componentImplementation.componentDescriptor().hasCreator()) { 579 parameters = 580 Maps.toMap(componentImplementation.requirements(), ComponentRequirement::toParameterSpec); 581 } else if (componentImplementation.isAbstract() && componentImplementation.isNested()) { 582 // If we're generating an abstract inner subcomponent, then we are not implementing module 583 // instance bindings and have no need for factory method parameters. 584 parameters = ImmutableMap.of(); 585 } else if (graph.factoryMethod().isPresent()) { 586 parameters = getFactoryMethodParameters(graph); 587 } else if (componentImplementation.isAbstract()) { 588 // If we're generating an abstract base implementation of a subcomponent it's acceptable to 589 // have neither a creator nor factory method. 590 parameters = ImmutableMap.of(); 591 } else { 592 throw new AssertionError( 593 "Expected either a component creator or factory method but found neither."); 594 } 595 596 if (componentImplementation.isAbstract()) { 597 parameters = Maps.filterKeys(parameters, in(configureInitializationRequirements())); 598 } 599 return renameParameters(parameters); 600 } 601 602 /** 603 * Returns the set of requirements for the configureInitialization method: the parameters that are 604 * needed either for initializing a component requirement field or for calling the superclass's 605 * {@code configureInitialization} method. 606 */ configureInitializationRequirements()607 private ImmutableSet<ComponentRequirement> configureInitializationRequirements() { 608 ImmutableSet<ComponentRequirement> initializationParameters = 609 componentImplementation.getComponentRequirementParameters(); 610 ImmutableSet<ComponentRequirement> superConfigureInitializationRequirements = 611 componentImplementation 612 .superConfigureInitializationMethod() 613 .map(ConfigureInitializationMethod::parameters) 614 .orElse(ImmutableSet.of()); 615 return Sets.union(initializationParameters, superConfigureInitializationRequirements) 616 .immutableCopy(); 617 } 618 619 /** 620 * Renames the given parameters to guarantee their names do not conflict with fields in the 621 * component to ensure that a parameter is never referenced where a reference to a field was 622 * intended. 623 */ 624 // TODO(cgdecker): This is a bit kludgy; it would be preferable to either qualify the field 625 // references with "this." or "super." when needed to disambiguate between field and parameter, 626 // but that would require more context than is currently available when the code referencing a 627 // field is generated. renameParameters( Map<ComponentRequirement, ParameterSpec> parameters)628 private ImmutableMap<ComponentRequirement, ParameterSpec> renameParameters( 629 Map<ComponentRequirement, ParameterSpec> parameters) { 630 return ImmutableMap.copyOf( 631 Maps.transformEntries( 632 parameters, 633 (requirement, parameter) -> 634 renameParameter( 635 parameter, 636 componentImplementation.getParameterName(requirement, parameter.name)))); 637 } 638 renameParameter(ParameterSpec parameter, String newName)639 private ParameterSpec renameParameter(ParameterSpec parameter, String newName) { 640 return ParameterSpec.builder(parameter.type, newName) 641 .addAnnotations(parameter.annotations) 642 .addModifiers(parameter.modifiers) 643 .build(); 644 } 645 646 /** Builds a root component implementation. */ 647 static final class RootComponentImplementationBuilder extends ComponentImplementationBuilder { 648 @Inject RootComponentImplementationBuilder(ComponentImplementation componentImplementation)649 RootComponentImplementationBuilder(ComponentImplementation componentImplementation) { 650 checkArgument(!componentImplementation.superclassImplementation().isPresent()); 651 } 652 653 @Override addCreatorClass(TypeSpec creator)654 protected void addCreatorClass(TypeSpec creator) { 655 componentImplementation.addType(COMPONENT_CREATOR, creator); 656 } 657 658 @Override addFactoryMethods()659 protected void addFactoryMethods() { 660 // Top-level components have a static method that returns a builder or factory for the 661 // component. If the user defined a @Component.Builder or @Component.Factory, an 662 // implementation of their type is returned. Otherwise, an autogenerated Builder type is 663 // returned. 664 // TODO(cgdecker): Replace this abomination with a small class? 665 // Better yet, change things so that an autogenerated builder type has a descriptor of sorts 666 // just like a user-defined creator type. 667 ComponentCreatorKind creatorKind; 668 ClassName creatorType; 669 String factoryMethodName; 670 boolean noArgFactoryMethod; 671 if (creatorDescriptor().isPresent()) { 672 ComponentCreatorDescriptor descriptor = creatorDescriptor().get(); 673 creatorKind = descriptor.kind(); 674 creatorType = ClassName.get(descriptor.typeElement()); 675 factoryMethodName = descriptor.factoryMethod().getSimpleName().toString(); 676 noArgFactoryMethod = descriptor.factoryParameters().isEmpty(); 677 } else { 678 creatorKind = BUILDER; 679 creatorType = componentCreatorName(); 680 factoryMethodName = "build"; 681 noArgFactoryMethod = true; 682 } 683 684 MethodSpec creatorFactoryMethod = 685 methodBuilder(creatorKind.methodName()) 686 .addModifiers(PUBLIC, STATIC) 687 .returns(creatorType) 688 .addStatement("return new $T()", componentCreatorName()) 689 .build(); 690 componentImplementation.addMethod(BUILDER_METHOD, creatorFactoryMethod); 691 if (noArgFactoryMethod && canInstantiateAllRequirements()) { 692 componentImplementation.addMethod( 693 BUILDER_METHOD, 694 methodBuilder("create") 695 .returns(ClassName.get(super.graph.componentTypeElement())) 696 .addModifiers(PUBLIC, STATIC) 697 .addStatement( 698 "return new $L().$L()", creatorKind.typeName(), factoryMethodName) 699 .build()); 700 } 701 } 702 creatorDescriptor()703 private Optional<ComponentCreatorDescriptor> creatorDescriptor() { 704 return graph.componentDescriptor().creatorDescriptor(); 705 } 706 707 /** {@code true} if all of the graph's required dependencies can be automatically constructed */ canInstantiateAllRequirements()708 private boolean canInstantiateAllRequirements() { 709 return !Iterables.any( 710 graph.componentRequirements(), 711 dependency -> dependency.requiresAPassedInstance(elements, types)); 712 } 713 componentCreatorName()714 private ClassName componentCreatorName() { 715 return componentImplementation.creatorImplementation().get().name(); 716 } 717 } 718 719 /** 720 * Builds a subcomponent implementation. If generating ahead-of-time subcomponents, this may be an 721 * abstract base class implementation, an abstract inner implementation, or a concrete 722 * implementation that extends an abstract base implementation. Otherwise it represents a private, 723 * inner, concrete, final implementation of a subcomponent which extends a user defined type. 724 */ 725 static final class SubcomponentImplementationBuilder extends ComponentImplementationBuilder { 726 final Optional<ComponentImplementationBuilder> parent; 727 728 @Inject SubcomponentImplementationBuilder( @arentComponent Optional<ComponentImplementationBuilder> parent)729 SubcomponentImplementationBuilder( 730 @ParentComponent Optional<ComponentImplementationBuilder> parent) { 731 this.parent = parent; 732 } 733 734 @Override addCreatorClass(TypeSpec creator)735 protected void addCreatorClass(TypeSpec creator) { 736 if (parent.isPresent()) { 737 // In an inner implementation of a subcomponent the creator is a peer class. 738 parent.get().componentImplementation.addType(SUBCOMPONENT, creator); 739 } else { 740 componentImplementation.addType(SUBCOMPONENT, creator); 741 } 742 } 743 744 @Override addFactoryMethods()745 protected void addFactoryMethods() { 746 // Only construct instances of subcomponents that have concrete implementations. 747 if (!componentImplementation.isAbstract()) { 748 // Use the parent's factory method to create this subcomponent if the 749 // subcomponent was not added via {@link dagger.Module#subcomponents()}. 750 graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod); 751 } 752 } 753 createSubcomponentFactoryMethod(ExecutableElement factoryMethod)754 private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) { 755 checkState(parent.isPresent()); 756 757 Collection<ParameterSpec> params = getFactoryMethodParameters(graph).values(); 758 MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType(), types); 759 params.forEach( 760 param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param)); 761 method.addStatement( 762 "return new $T($L)", componentImplementation.name(), parameterNames(params)); 763 764 parent.get().componentImplementation.addMethod(COMPONENT_METHOD, method.build()); 765 } 766 parentType()767 private DeclaredType parentType() { 768 return asDeclared(parent.get().graph.componentTypeElement().asType()); 769 } 770 771 @Override addInterfaceMethods()772 protected void addInterfaceMethods() { 773 if (componentImplementation.superclassImplementation().isPresent()) { 774 // Since we're overriding a subcomponent implementation we add to its implementation given 775 // an expanded binding graph. 776 777 ComponentImplementation superclassImplementation = 778 componentImplementation.superclassImplementation().get(); 779 for (ModifiableBindingMethod superclassModifiableBindingMethod : 780 superclassImplementation.getModifiableBindingMethods().values()) { 781 bindingExpressions 782 .modifiableBindingExpressions() 783 .possiblyReimplementedMethod(superclassModifiableBindingMethod) 784 .ifPresent(componentImplementation::addImplementedModifiableBindingMethod); 785 } 786 } else { 787 super.addInterfaceMethods(); 788 } 789 } 790 791 @Override cancelParentStatement()792 protected Optional<CodeBlock> cancelParentStatement() { 793 if (!shouldPropagateCancellationToParent()) { 794 return Optional.empty(); 795 } 796 return Optional.of( 797 CodeBlock.builder() 798 .addStatement( 799 "$T.this.$N($N)", 800 parent.get().componentImplementation.name(), 801 CANCELLATION_LISTENER_METHOD_NAME, 802 MAY_INTERRUPT_IF_RUNNING) 803 .build()); 804 } 805 shouldPropagateCancellationToParent()806 private boolean shouldPropagateCancellationToParent() { 807 return parent.isPresent() 808 && parent 809 .get() 810 .componentImplementation 811 .componentDescriptor() 812 .cancellationPolicy() 813 .map(policy -> policy.fromSubcomponents().equals(PROPAGATE)) 814 .orElse(false); 815 } 816 } 817 818 /** 819 * Returns the map of {@link ComponentRequirement}s to {@link ParameterSpec}s for the 820 * given graph's factory method. 821 */ getFactoryMethodParameters( BindingGraph graph)822 private static Map<ComponentRequirement, ParameterSpec> getFactoryMethodParameters( 823 BindingGraph graph) { 824 return Maps.transformValues(graph.factoryMethodParameters(), ParameterSpec::get); 825 } 826 } 827