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