1 /* 2 * Copyright (C) 2014 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 com.google.common.base.Preconditions.checkArgument; 20 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 21 import static com.squareup.javapoet.MethodSpec.methodBuilder; 22 import static com.squareup.javapoet.TypeSpec.classBuilder; 23 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameters; 24 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames; 25 import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies; 26 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 27 import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding; 28 import static dagger.internal.codegen.extension.DaggerStreams.presentValues; 29 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 30 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; 31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 32 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings; 33 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock; 34 import static dagger.internal.codegen.javapoet.TypeNames.factoryOf; 35 import static dagger.internal.codegen.model.BindingKind.INJECTION; 36 import static dagger.internal.codegen.model.BindingKind.PROVISION; 37 import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation; 38 import static javax.lang.model.element.Modifier.FINAL; 39 import static javax.lang.model.element.Modifier.PRIVATE; 40 import static javax.lang.model.element.Modifier.PUBLIC; 41 import static javax.lang.model.element.Modifier.STATIC; 42 43 import androidx.room.compiler.processing.XElement; 44 import androidx.room.compiler.processing.XExecutableParameterElement; 45 import androidx.room.compiler.processing.XFiler; 46 import androidx.room.compiler.processing.XProcessingEnv; 47 import com.google.common.collect.ImmutableList; 48 import com.google.common.collect.ImmutableMap; 49 import com.google.common.collect.Lists; 50 import com.squareup.javapoet.AnnotationSpec; 51 import com.squareup.javapoet.ClassName; 52 import com.squareup.javapoet.CodeBlock; 53 import com.squareup.javapoet.FieldSpec; 54 import com.squareup.javapoet.MethodSpec; 55 import com.squareup.javapoet.ParameterSpec; 56 import com.squareup.javapoet.TypeName; 57 import com.squareup.javapoet.TypeSpec; 58 import dagger.internal.Factory; 59 import dagger.internal.codegen.base.SourceFileGenerator; 60 import dagger.internal.codegen.base.UniqueNameSet; 61 import dagger.internal.codegen.binding.Binding; 62 import dagger.internal.codegen.binding.ProvisionBinding; 63 import dagger.internal.codegen.binding.SourceFiles; 64 import dagger.internal.codegen.compileroption.CompilerOptions; 65 import dagger.internal.codegen.javapoet.TypeNames; 66 import dagger.internal.codegen.model.BindingKind; 67 import dagger.internal.codegen.model.DaggerAnnotation; 68 import dagger.internal.codegen.model.DependencyRequest; 69 import dagger.internal.codegen.model.Key; 70 import dagger.internal.codegen.model.Scope; 71 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod; 72 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod; 73 import java.util.List; 74 import java.util.Optional; 75 import java.util.stream.Stream; 76 import javax.inject.Inject; 77 78 /** 79 * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for {@link 80 * Inject} constructors. 81 */ 82 public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> { 83 private final CompilerOptions compilerOptions; 84 private final SourceFiles sourceFiles; 85 86 @Inject FactoryGenerator( XFiler filer, CompilerOptions compilerOptions, SourceFiles sourceFiles, XProcessingEnv processingEnv)87 FactoryGenerator( 88 XFiler filer, 89 CompilerOptions compilerOptions, 90 SourceFiles sourceFiles, 91 XProcessingEnv processingEnv) { 92 super(filer, processingEnv); 93 this.compilerOptions = compilerOptions; 94 this.sourceFiles = sourceFiles; 95 } 96 97 @Override originatingElement(ProvisionBinding binding)98 public XElement originatingElement(ProvisionBinding binding) { 99 // we only create factories for bindings that have a binding element 100 return binding.bindingElement().get(); 101 } 102 103 @Override topLevelTypes(ProvisionBinding binding)104 public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) { 105 // We don't want to write out resolved bindings -- we want to write out the generic version. 106 checkArgument(!binding.unresolved().isPresent()); 107 checkArgument(binding.bindingElement().isPresent()); 108 109 if (binding.kind() == BindingKind.DELEGATE) { 110 return ImmutableList.of(); 111 } 112 113 return ImmutableList.of(factoryBuilder(binding)); 114 } 115 factoryBuilder(ProvisionBinding binding)116 private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) { 117 TypeSpec.Builder factoryBuilder = 118 classBuilder(generatedClassNameForBinding(binding)) 119 .addModifiers(PUBLIC, FINAL) 120 .addTypeVariables(bindingTypeElementTypeVariableNames(binding)); 121 122 if (binding.kind() == BindingKind.INJECTION 123 || binding.kind() == BindingKind.ASSISTED_INJECTION 124 || binding.kind() == BindingKind.PROVISION) { 125 factoryBuilder.addAnnotation(scopeMetadataAnnotation(binding)); 126 factoryBuilder.addAnnotation(qualifierMetadataAnnotation(binding)); 127 } 128 129 factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface); 130 addConstructorAndFields(binding, factoryBuilder); 131 factoryBuilder.addMethod(getMethod(binding)); 132 addCreateMethod(binding, factoryBuilder); 133 134 factoryBuilder.addMethod(ProvisionMethod.create(binding, compilerOptions)); 135 gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation); 136 137 return factoryBuilder; 138 } 139 addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)140 private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) { 141 if (FactoryCreationStrategy.of(binding) == FactoryCreationStrategy.SINGLETON_INSTANCE) { 142 return; 143 } 144 // TODO(bcorso): Make the constructor private? 145 MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC); 146 constructorParams(binding).forEach( 147 param -> { 148 constructor.addParameter(param).addStatement("this.$1N = $1N", param); 149 factoryBuilder.addField( 150 FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build()); 151 }); 152 factoryBuilder.addMethod(constructor.build()); 153 } 154 constructorParams(ProvisionBinding binding)155 private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) { 156 ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder(); 157 moduleParameter(binding).ifPresent(params::add); 158 frameworkFields(binding).values().forEach(field -> params.add(toParameter(field))); 159 return params.build(); 160 } 161 moduleParameter(ProvisionBinding binding)162 private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) { 163 if (binding.requiresModuleInstance()) { 164 // TODO(bcorso, dpb): Should this use contributingModule()? 165 TypeName type = binding.bindingTypeElement().get().getType().getTypeName(); 166 return Optional.of(ParameterSpec.builder(type, "module").build()); 167 } 168 return Optional.empty(); 169 } 170 frameworkFields(ProvisionBinding binding)171 private ImmutableMap<DependencyRequest, FieldSpec> frameworkFields(ProvisionBinding binding) { 172 UniqueNameSet uniqueFieldNames = new UniqueNameSet(); 173 // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module". 174 moduleParameter(binding).ifPresent(module -> uniqueFieldNames.claim(module.name)); 175 // We avoid Maps.transformValues here because it would implicitly depend on the order in which 176 // the transform function is evaluated on each entry in the map. 177 ImmutableMap.Builder<DependencyRequest, FieldSpec> builder = ImmutableMap.builder(); 178 generateBindingFieldsForDependencies(binding).forEach( 179 (dependency, field) -> 180 builder.put(dependency, 181 FieldSpec.builder( 182 field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL) 183 .build())); 184 return builder.build(); 185 } 186 addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)187 private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) { 188 // If constructing a factory for @Inject or @Provides bindings, we use a static create method 189 // so that generated components can avoid having to refer to the generic types 190 // of the factory. (Otherwise they may have visibility problems referring to the types.) 191 MethodSpec.Builder createMethodBuilder = 192 methodBuilder("create") 193 .addModifiers(PUBLIC, STATIC) 194 .returns(parameterizedGeneratedTypeNameForBinding(binding)) 195 .addTypeVariables(bindingTypeElementTypeVariableNames(binding)); 196 197 switch (FactoryCreationStrategy.of(binding)) { 198 case SINGLETON_INSTANCE: 199 FieldSpec.Builder instanceFieldBuilder = 200 FieldSpec.builder( 201 generatedClassNameForBinding(binding), "INSTANCE", PRIVATE, STATIC, FINAL) 202 .initializer("new $T()", generatedClassNameForBinding(binding)); 203 204 if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) { 205 // If the factory has type parameters, ignore them in the field declaration & initializer 206 instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES)); 207 createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED)); 208 } 209 210 ClassName instanceHolderName = 211 generatedClassNameForBinding(binding).nestedClass("InstanceHolder"); 212 createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName); 213 factoryBuilder.addType( 214 TypeSpec.classBuilder(instanceHolderName) 215 .addModifiers(PRIVATE, STATIC, FINAL) 216 .addField(instanceFieldBuilder.build()) 217 .build()); 218 break; 219 case CLASS_CONSTRUCTOR: 220 List<ParameterSpec> params = constructorParams(binding); 221 createMethodBuilder.addParameters(params); 222 createMethodBuilder.addStatement( 223 "return new $T($L)", 224 parameterizedGeneratedTypeNameForBinding(binding), 225 makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input)))); 226 break; 227 default: 228 throw new AssertionError(); 229 } 230 factoryBuilder.addMethod(createMethodBuilder.build()); 231 } 232 getMethod(ProvisionBinding binding)233 private MethodSpec getMethod(ProvisionBinding binding) { 234 UniqueNameSet uniqueFieldNames = new UniqueNameSet(); 235 ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding); 236 frameworkFields.values().forEach(field -> uniqueFieldNames.claim(field.name)); 237 ImmutableMap<XExecutableParameterElement, ParameterSpec> assistedParameters = 238 assistedParameters(binding).stream() 239 .collect( 240 toImmutableMap( 241 parameter -> parameter, 242 parameter -> 243 ParameterSpec.builder( 244 parameter.getType().getTypeName(), 245 uniqueFieldNames.getUniqueName(parameter.getJvmName())) 246 .build())); 247 TypeName providedTypeName = providedTypeName(binding); 248 MethodSpec.Builder getMethod = 249 methodBuilder("get") 250 .addModifiers(PUBLIC) 251 .addParameters(assistedParameters.values()); 252 253 if (factoryTypeName(binding).isPresent()) { 254 getMethod.addAnnotation(Override.class); 255 } 256 CodeBlock invokeNewInstance = 257 ProvisionMethod.invoke( 258 binding, 259 request -> 260 sourceFiles.frameworkTypeUsageStatement( 261 CodeBlock.of("$N", frameworkFields.get(request)), request.kind()), 262 param -> assistedParameters.get(param).name, 263 generatedClassNameForBinding(binding), 264 moduleParameter(binding).map(module -> CodeBlock.of("$N", module)), 265 compilerOptions); 266 267 if (binding.kind().equals(PROVISION)) { 268 binding 269 .nullability() 270 .nullableAnnotations() 271 .forEach(getMethod::addAnnotation); 272 getMethod.returns(providedTypeName); 273 getMethod.addStatement("return $L", invokeNewInstance); 274 } else if (!binding.injectionSites().isEmpty()) { 275 CodeBlock instance = CodeBlock.of("instance"); 276 getMethod 277 .returns(providedTypeName) 278 .addStatement("$T $L = $L", providedTypeName, instance, invokeNewInstance) 279 .addCode( 280 InjectionSiteMethod.invokeAll( 281 binding.injectionSites(), 282 generatedClassNameForBinding(binding), 283 instance, 284 binding.key().type().xprocessing(), 285 sourceFiles.frameworkFieldUsages(binding.dependencies(), frameworkFields)::get)) 286 .addStatement("return $L", instance); 287 288 } else { 289 getMethod 290 .returns(providedTypeName) 291 .addStatement("return $L", invokeNewInstance); 292 } 293 return getMethod.build(); 294 } 295 scopeMetadataAnnotation(ProvisionBinding binding)296 private AnnotationSpec scopeMetadataAnnotation(ProvisionBinding binding) { 297 AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.SCOPE_METADATA); 298 binding.scope() 299 .map(Scope::scopeAnnotation) 300 .map(DaggerAnnotation::className) 301 .map(ClassName::canonicalName) 302 .ifPresent(scopeCanonicalName -> builder.addMember("value", "$S", scopeCanonicalName)); 303 return builder.build(); 304 } 305 qualifierMetadataAnnotation(ProvisionBinding binding)306 private AnnotationSpec qualifierMetadataAnnotation(ProvisionBinding binding) { 307 AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.QUALIFIER_METADATA); 308 // Collect all qualifiers on the binding itself or its dependencies 309 Stream.concat( 310 Stream.of(binding.key()), 311 binding.provisionDependencies().stream().map(DependencyRequest::key)) 312 .map(Key::qualifier) 313 .flatMap(presentValues()) 314 .map(DaggerAnnotation::className) 315 .map(ClassName::canonicalName) 316 .distinct() 317 .forEach(qualifier -> builder.addMember("value", "$S", qualifier)); 318 return builder.build(); 319 } 320 providedTypeName(ProvisionBinding binding)321 private static TypeName providedTypeName(ProvisionBinding binding) { 322 return binding.contributedType().getTypeName(); 323 } 324 factoryTypeName(ProvisionBinding binding)325 private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) { 326 return binding.kind() == BindingKind.ASSISTED_INJECTION 327 ? Optional.empty() 328 : Optional.of(factoryOf(providedTypeName(binding))); 329 } 330 toParameter(FieldSpec field)331 private static ParameterSpec toParameter(FieldSpec field) { 332 return ParameterSpec.builder(field.type, field.name).build(); 333 } 334 335 /** The strategy for getting an instance of a factory for a {@link Binding}. */ 336 private enum FactoryCreationStrategy { 337 /** The factory class is a single instance. */ 338 SINGLETON_INSTANCE, 339 /** The factory must be created by calling the constructor. */ 340 CLASS_CONSTRUCTOR; 341 of(Binding binding)342 static FactoryCreationStrategy of(Binding binding) { 343 switch (binding.kind()) { 344 case DELEGATE: 345 throw new AssertionError("Delegate bindings don't have a factory."); 346 case PROVISION: 347 return binding.dependencies().isEmpty() && !binding.requiresModuleInstance() 348 ? SINGLETON_INSTANCE 349 : CLASS_CONSTRUCTOR; 350 case INJECTION: 351 case MULTIBOUND_SET: 352 case MULTIBOUND_MAP: 353 return binding.dependencies().isEmpty() 354 ? SINGLETON_INSTANCE 355 : CLASS_CONSTRUCTOR; 356 default: 357 return CLASS_CONSTRUCTOR; 358 } 359 } 360 } 361 } 362