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