1 /* 2 * Copyright (C) 2017 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.MoreTypes.asDeclared; 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static com.google.common.base.Preconditions.checkState; 22 import static com.google.common.collect.Iterables.getOnlyElement; 23 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 24 import static com.squareup.javapoet.MethodSpec.methodBuilder; 25 import static com.squareup.javapoet.TypeSpec.classBuilder; 26 import static dagger.internal.codegen.SourceFiles.simpleVariableName; 27 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 28 import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype; 29 import static javax.lang.model.element.Modifier.ABSTRACT; 30 import static javax.lang.model.element.Modifier.FINAL; 31 import static javax.lang.model.element.Modifier.PRIVATE; 32 import static javax.lang.model.element.Modifier.PROTECTED; 33 import static javax.lang.model.element.Modifier.PUBLIC; 34 import static javax.lang.model.element.Modifier.STATIC; 35 36 import com.google.common.collect.ImmutableMap; 37 import com.google.common.collect.ImmutableSet; 38 import com.google.common.collect.Maps; 39 import com.google.common.collect.Sets; 40 import com.squareup.javapoet.ClassName; 41 import com.squareup.javapoet.CodeBlock; 42 import com.squareup.javapoet.FieldSpec; 43 import com.squareup.javapoet.MethodSpec; 44 import com.squareup.javapoet.ParameterSpec; 45 import com.squareup.javapoet.TypeName; 46 import com.squareup.javapoet.TypeSpec; 47 import dagger.internal.Preconditions; 48 import dagger.internal.codegen.ComponentRequirement.NullPolicy; 49 import dagger.internal.codegen.javapoet.TypeNames; 50 import dagger.internal.codegen.langmodel.DaggerElements; 51 import dagger.internal.codegen.langmodel.DaggerTypes; 52 import java.util.Optional; 53 import java.util.Set; 54 import javax.inject.Inject; 55 import javax.lang.model.element.ExecutableElement; 56 import javax.lang.model.element.Modifier; 57 import javax.lang.model.type.DeclaredType; 58 import javax.lang.model.type.TypeKind; 59 60 /** Factory for creating {@link ComponentCreatorImplementation} instances. */ 61 final class ComponentCreatorImplementationFactory { 62 63 private final DaggerElements elements; 64 private final DaggerTypes types; 65 66 @Inject ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types)67 ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types) { 68 this.elements = elements; 69 this.types = types; 70 } 71 72 /** Returns a new creator implementation for the given component, if necessary. */ create( ComponentImplementation componentImplementation, Optional<BindingGraph> graph)73 Optional<ComponentCreatorImplementation> create( 74 ComponentImplementation componentImplementation, Optional<BindingGraph> graph) { 75 if (!componentImplementation.componentDescriptor().hasCreator()) { 76 return Optional.empty(); 77 } 78 79 Optional<ComponentCreatorDescriptor> creatorDescriptor = 80 componentImplementation.componentDescriptor().creatorDescriptor(); 81 82 if (componentImplementation.isAbstract() 83 && (hasNoSetterMethods(creatorDescriptor) 84 || componentImplementation.superclassImplementation().isPresent())) { 85 // 1. Factory-like creators (those with no setter methods) are only generated in concrete 86 // components, because they only have a factory method and the factory method must call 87 // a concrete component's constructor. 88 // 2. The component builder in ahead-of-time mode is generated with the base subcomponent 89 // implementation, with the exception of the build method since that requires invoking the 90 // constructor of a concrete component implementation. Intermediate component 91 // implementations, because they still can't invoke the eventual constructor and have no 92 // additional extensions to the builder, can ignore generating a builder implementation. 93 return Optional.empty(); 94 } 95 96 Builder builder = 97 creatorDescriptor.isPresent() 98 ? new BuilderForCreatorDescriptor( 99 componentImplementation, creatorDescriptor.get(), graph) 100 : new BuilderForGeneratedRootComponentBuilder(componentImplementation); 101 return Optional.of(builder.build()); 102 } 103 hasNoSetterMethods( Optional<ComponentCreatorDescriptor> creatorDescriptor)104 private static boolean hasNoSetterMethods( 105 Optional<ComponentCreatorDescriptor> creatorDescriptor) { 106 return creatorDescriptor.filter(descriptor -> descriptor.setterMethods().isEmpty()).isPresent(); 107 } 108 109 /** Base class for building a creator implementation. */ 110 private abstract class Builder { 111 final ComponentImplementation componentImplementation; 112 final ClassName className; 113 final TypeSpec.Builder classBuilder; 114 115 private ImmutableMap<ComponentRequirement, FieldSpec> fields; 116 Builder(ComponentImplementation componentImplementation)117 Builder(ComponentImplementation componentImplementation) { 118 this.componentImplementation = componentImplementation; 119 this.className = componentImplementation.getCreatorName(); 120 this.classBuilder = classBuilder(className); 121 } 122 123 /** Builds the {@link ComponentCreatorImplementation}. */ build()124 ComponentCreatorImplementation build() { 125 setModifiers(); 126 setSupertype(); 127 this.fields = getOrAddFields(); 128 addConstructor(); 129 addSetterMethods(); 130 addFactoryMethod(); 131 return ComponentCreatorImplementation.create(classBuilder.build(), className, fields); 132 } 133 134 /** Returns the descriptor for the component. */ componentDescriptor()135 final ComponentDescriptor componentDescriptor() { 136 return componentImplementation.componentDescriptor(); 137 } 138 139 /** 140 * The set of requirements that must be passed to the component's constructor in the order 141 * they must be passed. 142 */ componentConstructorRequirements()143 final ImmutableSet<ComponentRequirement> componentConstructorRequirements() { 144 return componentImplementation.requirements(); 145 } 146 147 /** Returns the requirements that have setter methods on the creator type. */ setterMethods()148 abstract ImmutableSet<ComponentRequirement> setterMethods(); 149 150 /** 151 * Returns the component requirements that have factory method parameters, mapped to the name 152 * for that parameter. 153 */ factoryMethodParameters()154 abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters(); 155 156 /** 157 * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status 158 * for each requirement indicating what's needed for that requirement in the implementation 159 * class currently being generated. 160 */ userSettableRequirements()161 abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements(); 162 163 /** 164 * Component requirements that are both settable by the creator and needed to construct the 165 * component. 166 */ neededUserSettableRequirements()167 private Set<ComponentRequirement> neededUserSettableRequirements() { 168 return Sets.intersection( 169 userSettableRequirements().keySet(), componentConstructorRequirements()); 170 } 171 setModifiers()172 private void setModifiers() { 173 visibility().ifPresent(classBuilder::addModifiers); 174 if (!componentImplementation.isNested()) { 175 classBuilder.addModifiers(STATIC); 176 } 177 classBuilder.addModifiers(componentImplementation.isAbstract() ? ABSTRACT : FINAL); 178 } 179 180 /** Returns the visibility modifier the generated class should have, if any. */ visibility()181 protected abstract Optional<Modifier> visibility(); 182 183 /** Sets the superclass being extended or interface being implemented for this creator. */ setSupertype()184 protected abstract void setSupertype(); 185 186 /** Adds a constructor for the creator type, if needed. */ addConstructor()187 protected abstract void addConstructor(); 188 getOrAddFields()189 private ImmutableMap<ComponentRequirement, FieldSpec> getOrAddFields() { 190 // If a base implementation is present, any fields are already defined there and don't need to 191 // be created in this implementation. 192 return componentImplementation 193 .baseCreatorImplementation() 194 .map(ComponentCreatorImplementation::fields) 195 .orElseGet(this::addFields); 196 } 197 addFields()198 private ImmutableMap<ComponentRequirement, FieldSpec> addFields() { 199 // Fields in an abstract creator class need to be visible from subclasses. 200 Modifier modifier = componentImplementation.isAbstract() ? PROTECTED : PRIVATE; 201 UniqueNameSet fieldNames = new UniqueNameSet(); 202 ImmutableMap<ComponentRequirement, FieldSpec> result = 203 Maps.toMap( 204 Sets.intersection(neededUserSettableRequirements(), setterMethods()), 205 requirement -> 206 FieldSpec.builder( 207 TypeName.get(requirement.type()), 208 fieldNames.getUniqueName(requirement.variableName()), 209 modifier) 210 .build()); 211 classBuilder.addFields(result.values()); 212 return result; 213 } 214 addSetterMethods()215 private void addSetterMethods() { 216 Maps.filterKeys(userSettableRequirements(), setterMethods()::contains) 217 .forEach( 218 (requirement, status) -> 219 createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod)); 220 } 221 222 /** Creates a new setter method builder, with no method body, for the given requirement. */ setterMethodBuilder(ComponentRequirement requirement)223 protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement); 224 createSetterMethod( ComponentRequirement requirement, RequirementStatus status)225 private Optional<MethodSpec> createSetterMethod( 226 ComponentRequirement requirement, RequirementStatus status) { 227 switch (status) { 228 case NEEDED: 229 return Optional.of(normalSetterMethod(requirement)); 230 case UNNEEDED: 231 return Optional.of(noopSetterMethod(requirement)); 232 case UNSETTABLE_REPEATED_MODULE: 233 return Optional.of(repeatedModuleSetterMethod(requirement)); 234 case IMPLEMENTED_IN_SUPERTYPE: 235 return Optional.empty(); 236 } 237 throw new AssertionError(); 238 } 239 normalSetterMethod(ComponentRequirement requirement)240 private MethodSpec normalSetterMethod(ComponentRequirement requirement) { 241 MethodSpec.Builder method = setterMethodBuilder(requirement); 242 ParameterSpec parameter = parameter(method.build()); 243 method.addStatement( 244 "this.$N = $L", 245 fields.get(requirement), 246 requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW) 247 ? CodeBlock.of("$N", parameter) 248 : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter)); 249 return maybeReturnThis(method); 250 } 251 noopSetterMethod(ComponentRequirement requirement)252 private MethodSpec noopSetterMethod(ComponentRequirement requirement) { 253 MethodSpec.Builder method = setterMethodBuilder(requirement); 254 ParameterSpec parameter = parameter(method.build()); 255 method 256 .addAnnotation(Deprecated.class) 257 .addJavadoc( 258 "@deprecated This module is declared, but an instance is not used in the component. " 259 + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n") 260 .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter); 261 return maybeReturnThis(method); 262 } 263 repeatedModuleSetterMethod(ComponentRequirement requirement)264 private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) { 265 return setterMethodBuilder(requirement) 266 .addStatement( 267 "throw new $T($T.format($S, $T.class.getCanonicalName()))", 268 UnsupportedOperationException.class, 269 String.class, 270 "%s cannot be set because it is inherited from the enclosing component", 271 TypeNames.rawTypeName(TypeName.get(requirement.type()))) 272 .build(); 273 } 274 parameter(MethodSpec method)275 private ParameterSpec parameter(MethodSpec method) { 276 return getOnlyElement(method.parameters); 277 } 278 maybeReturnThis(MethodSpec.Builder method)279 private MethodSpec maybeReturnThis(MethodSpec.Builder method) { 280 MethodSpec built = method.build(); 281 return built.returnType.equals(TypeName.VOID) 282 ? built 283 : method.addStatement("return this").build(); 284 } 285 addFactoryMethod()286 private void addFactoryMethod() { 287 if (!componentImplementation.isAbstract()) { 288 classBuilder.addMethod(factoryMethod()); 289 } 290 } 291 factoryMethod()292 MethodSpec factoryMethod() { 293 MethodSpec.Builder factoryMethod = factoryMethodBuilder(); 294 factoryMethod 295 .returns(ClassName.get(componentDescriptor().typeElement())) 296 .addModifiers(PUBLIC); 297 298 ImmutableMap<ComponentRequirement, String> factoryMethodParameters = 299 factoryMethodParameters(); 300 userSettableRequirements() 301 .keySet() 302 .forEach( 303 requirement -> { 304 if (fields.containsKey(requirement) 305 && componentConstructorRequirements().contains(requirement)) { 306 // In AOT mode, there can be a field for a requirement even if the component's 307 // constructor doesn't need it, because the base class for the creator was created 308 // before the final graph for the component was known. 309 FieldSpec field = fields.get(requirement); 310 addNullHandlingForField(requirement, field, factoryMethod); 311 } else if (factoryMethodParameters.containsKey(requirement)) { 312 String parameterName = factoryMethodParameters.get(requirement); 313 addNullHandlingForParameter(requirement, parameterName, factoryMethod); 314 } 315 }); 316 factoryMethod.addStatement( 317 "return new $T($L)", 318 componentImplementation.name(), 319 componentConstructorArgs(factoryMethodParameters)); 320 return factoryMethod.build(); 321 } 322 addNullHandlingForField( ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod)323 private void addNullHandlingForField( 324 ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) { 325 switch (requirement.nullPolicy(elements, types)) { 326 case NEW: 327 checkState(requirement.kind().isModule()); 328 factoryMethod 329 .beginControlFlow("if ($N == null)", field) 330 .addStatement("this.$N = $L", field, newModuleInstance(requirement)) 331 .endControlFlow(); 332 break; 333 case THROW: 334 // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for 335 // @BindsInstance requirements, but that's not easily proguardable. 336 factoryMethod.addStatement( 337 "$T.checkBuilderRequirement($N, $T.class)", 338 Preconditions.class, 339 field, 340 TypeNames.rawTypeName(field.type)); 341 break; 342 case ALLOW: 343 break; 344 } 345 } 346 addNullHandlingForParameter( ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod)347 private void addNullHandlingForParameter( 348 ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) { 349 if (!requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)) { 350 // Factory method parameters are always required unless they are a nullable 351 // binds-instance (i.e. ALLOW) 352 factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter); 353 } 354 } 355 356 /** Returns a builder for the creator's factory method. */ factoryMethodBuilder()357 protected abstract MethodSpec.Builder factoryMethodBuilder(); 358 componentConstructorArgs( ImmutableMap<ComponentRequirement, String> factoryMethodParameters)359 private CodeBlock componentConstructorArgs( 360 ImmutableMap<ComponentRequirement, String> factoryMethodParameters) { 361 return componentConstructorRequirements().stream() 362 .map( 363 requirement -> { 364 if (fields.containsKey(requirement)) { 365 return CodeBlock.of("$N", fields.get(requirement)); 366 } else if (factoryMethodParameters.containsKey(requirement)) { 367 return CodeBlock.of("$L", factoryMethodParameters.get(requirement)); 368 } else { 369 return newModuleInstance(requirement); 370 } 371 }) 372 .collect(toParametersCodeBlock()); 373 } 374 newModuleInstance(ComponentRequirement requirement)375 private CodeBlock newModuleInstance(ComponentRequirement requirement) { 376 checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here 377 return ModuleProxies.newModuleInstance(requirement.typeElement(), className, elements); 378 } 379 } 380 381 /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */ 382 private final class BuilderForCreatorDescriptor extends Builder { 383 final ComponentCreatorDescriptor creatorDescriptor; 384 private final Optional<BindingGraph> graph; 385 386 BuilderForCreatorDescriptor( 387 ComponentImplementation componentImplementation, 388 ComponentCreatorDescriptor creatorDescriptor, 389 Optional<BindingGraph> graph) { 390 super(componentImplementation); 391 this.creatorDescriptor = creatorDescriptor; 392 this.graph = graph; 393 } 394 395 @Override 396 protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() { 397 return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus); 398 } 399 400 @Override 401 protected Optional<Modifier> visibility() { 402 if (componentImplementation.isAbstract()) { 403 // The component creator class of a top-level component implementation in ahead-of-time 404 // subcomponents mode must be public, not protected, because the creator's subclass will 405 // be a sibling of the component subclass implementation, not nested. 406 return Optional.of(componentImplementation.isNested() ? PROTECTED : PUBLIC); 407 } 408 return Optional.of(PRIVATE); 409 } 410 411 @Override 412 protected void setSupertype() { 413 if (componentImplementation.baseCreatorImplementation().isPresent()) { 414 // If an abstract base implementation for this creator exists, extend that class. 415 classBuilder.superclass(componentImplementation.baseCreatorImplementation().get().name()); 416 } else { 417 addSupertype(classBuilder, creatorDescriptor.typeElement()); 418 } 419 } 420 421 @Override 422 protected void addConstructor() { 423 // Just use the implicit no-arg public constructor. 424 } 425 426 @Override 427 protected ImmutableSet<ComponentRequirement> setterMethods() { 428 return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet()); 429 } 430 431 @Override 432 protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() { 433 return ImmutableMap.copyOf( 434 Maps.transformValues( 435 creatorDescriptor.factoryParameters(), 436 element -> element.getSimpleName().toString())); 437 } 438 439 private DeclaredType creatorType() { 440 return asDeclared(creatorDescriptor.typeElement().asType()); 441 } 442 443 @Override 444 protected MethodSpec.Builder factoryMethodBuilder() { 445 return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types); 446 } 447 448 private RequirementStatus requirementStatus(ComponentRequirement requirement) { 449 // In ahead-of-time subcomponents mode, all builder methods are defined at the base 450 // implementation. The only case where a method needs to be overridden is for a repeated 451 // module, which is unknown at the point when a base implementation is generated. We do this 452 // at the root for simplicity (and as an aside, repeated modules are never used in google 453 // as of 11/28/18, and thus the additional cost of including these methods at the root is 454 // negligible). 455 if (isRepeatedModule(requirement)) { 456 return RequirementStatus.UNSETTABLE_REPEATED_MODULE; 457 } 458 459 if (hasBaseCreatorImplementation()) { 460 return RequirementStatus.IMPLEMENTED_IN_SUPERTYPE; 461 } 462 463 return componentConstructorRequirements().contains(requirement) 464 ? RequirementStatus.NEEDED 465 : RequirementStatus.UNNEEDED; 466 } 467 468 /** 469 * Returns whether the given requirement is for a repeat of a module inherited from an ancestor 470 * component. This creator is not allowed to set such a module. 471 */ 472 final boolean isRepeatedModule(ComponentRequirement requirement) { 473 return !componentConstructorRequirements().contains(requirement) 474 && !isOwnedModule(requirement); 475 } 476 477 /** 478 * Returns whether the given {@code requirement} is for a module type owned by the component. 479 */ 480 private boolean isOwnedModule(ComponentRequirement requirement) { 481 return graph.map(g -> g.ownedModuleTypes().contains(requirement.typeElement())).orElse(true); 482 } 483 484 private boolean hasBaseCreatorImplementation() { 485 return !componentImplementation.isAbstract() 486 && componentImplementation.baseImplementation().isPresent(); 487 } 488 489 @Override 490 protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) { 491 ExecutableElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement); 492 MethodSpec.Builder method = MethodSpec.overriding(supertypeMethod, creatorType(), types); 493 if (!supertypeMethod.getReturnType().getKind().equals(TypeKind.VOID)) { 494 // Take advantage of covariant returns so that we don't have to worry about type variables 495 method.returns(className); 496 } 497 return method; 498 } 499 } 500 501 /** 502 * Builder for a component builder class that is automatically generated for a root component that 503 * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}). 504 */ 505 private final class BuilderForGeneratedRootComponentBuilder extends Builder { 506 BuilderForGeneratedRootComponentBuilder(ComponentImplementation componentImplementation) { 507 super(componentImplementation); 508 } 509 510 @Override 511 protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() { 512 return Maps.toMap( 513 setterMethods(), 514 requirement -> 515 componentConstructorRequirements().contains(requirement) 516 ? RequirementStatus.NEEDED 517 : RequirementStatus.UNNEEDED); 518 } 519 520 @Override 521 protected Optional<Modifier> visibility() { 522 return componentImplementation 523 .componentDescriptor() 524 .typeElement() 525 .getModifiers() 526 .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty(); 527 } 528 529 @Override 530 protected void setSupertype() { 531 // There's never a supertype for a root component auto-generated builder type. 532 } 533 534 @Override 535 protected void addConstructor() { 536 classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build()); 537 } 538 539 @Override 540 protected ImmutableSet<ComponentRequirement> setterMethods() { 541 return componentDescriptor().dependenciesAndConcreteModules(); 542 } 543 544 @Override 545 protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() { 546 return ImmutableMap.of(); 547 } 548 549 @Override 550 protected MethodSpec.Builder factoryMethodBuilder() { 551 return methodBuilder("build"); 552 } 553 554 @Override 555 protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) { 556 String name = simpleVariableName(requirement.typeElement()); 557 return methodBuilder(name) 558 .addModifiers(PUBLIC) 559 .addParameter(TypeName.get(requirement.type()), name) 560 .returns(className); 561 } 562 } 563 564 /** Enumeration of statuses a component requirement may have in a creator. */ 565 enum RequirementStatus { 566 /** An instance is needed to create the component. */ 567 NEEDED, 568 569 /** 570 * An instance is not needed to create the component, but the requirement is for a module owned 571 * by the component. Setting the requirement is a no-op and any setter method should be marked 572 * deprecated on the generated type as a warning to the user. 573 */ 574 UNNEEDED, 575 576 /** 577 * The requirement may not be set in this creator because the module it is for is already 578 * inherited from an ancestor component. Any setter method for it should throw an exception. 579 */ 580 UNSETTABLE_REPEATED_MODULE, 581 582 /** 583 * The requirement is settable by the creator, but the setter method implementation already 584 * exists in a supertype. 585 */ 586 IMPLEMENTED_IN_SUPERTYPE, 587 ; 588 } 589 } 590