Skip to content

Commit cb76f99

Browse files
bcorsoDagger Team
authored andcommitted
Refactor factory generators for better consistency and readability.
This CL refactors the factory generators into smaller functions. Specifically, each function encapsulates a generated method on the factory. I've also added code snippets above the functions to document examples of the generated methods they return, and aligned the classes so that the overall style is consistent. This CL is a no-op change. RELNOTES=N/A PiperOrigin-RevId: 643999088
1 parent 81512af commit cb76f99

File tree

4 files changed

+577
-586
lines changed

4 files changed

+577
-586
lines changed

java/dagger/internal/codegen/binding/ProductionBinding.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,14 @@ && isFutureType(SetType.from(producesMethod.getReturnType()).elementType())) {
9393
* production bindings from {@code @Produces} methods will have an executor request, but
9494
* synthetic production bindings may not.
9595
*/
96-
abstract Optional<DependencyRequest> executorRequest();
96+
public abstract Optional<DependencyRequest> executorRequest();
9797

98-
/** If this production requires a monitor, this will be the corresponding request. All
98+
/**
99+
* If this production requires a monitor, this will be the corresponding request. All
99100
* production bindings from {@code @Produces} methods will have a monitor request, but synthetic
100101
* production bindings may not.
101102
*/
102-
abstract Optional<DependencyRequest> monitorRequest();
103+
public abstract Optional<DependencyRequest> monitorRequest();
103104

104105
// Profiling determined that this method is called enough times that memoizing it had a measurable
105106
// performance improvement for large components.

java/dagger/internal/codegen/writing/FactoryGenerator.java

Lines changed: 166 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
2727
import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
2828
import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
29+
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
2930
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
3031
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
3132
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
3233
import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
33-
import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
34+
import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
3435
import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
35-
import static dagger.internal.codegen.model.BindingKind.INJECTION;
3636
import static dagger.internal.codegen.model.BindingKind.PROVISION;
3737
import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
3838
import static javax.lang.model.element.Modifier.FINAL;
@@ -47,7 +47,6 @@
4747
import com.google.common.collect.ImmutableList;
4848
import com.google.common.collect.ImmutableMap;
4949
import com.google.common.collect.ImmutableSet;
50-
import com.google.common.collect.Lists;
5150
import com.squareup.javapoet.AnnotationSpec;
5251
import com.squareup.javapoet.ClassName;
5352
import com.squareup.javapoet.CodeBlock;
@@ -59,7 +58,6 @@
5958
import dagger.internal.Factory;
6059
import dagger.internal.codegen.base.SourceFileGenerator;
6160
import dagger.internal.codegen.base.UniqueNameSet;
62-
import dagger.internal.codegen.binding.Binding;
6361
import dagger.internal.codegen.binding.ProvisionBinding;
6462
import dagger.internal.codegen.binding.SourceFiles;
6563
import dagger.internal.codegen.compileroption.CompilerOptions;
@@ -71,7 +69,6 @@
7169
import dagger.internal.codegen.model.Scope;
7270
import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
7371
import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
74-
import java.util.List;
7572
import java.util.Optional;
7673
import java.util.stream.Stream;
7774
import javax.inject.Inject;
@@ -123,113 +120,124 @@ private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
123120
.addAnnotation(qualifierMetadataAnnotation(binding));
124121

125122
factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface);
126-
addConstructorAndFields(binding, factoryBuilder);
127-
factoryBuilder.addMethod(getMethod(binding));
128-
addCreateMethod(binding, factoryBuilder);
129-
130-
factoryBuilder.addMethod(ProvisionMethod.create(binding, compilerOptions));
123+
FactoryFields factoryFields = FactoryFields.create(binding);
124+
// If the factory has no input fields we can use a static instance holder to create a
125+
// singleton instance of the factory. Otherwise, we create a new instance via the constructor.
126+
if (factoryFields.isEmpty()) {
127+
factoryBuilder.addType(staticInstanceHolderType(binding));
128+
} else {
129+
factoryBuilder
130+
.addFields(factoryFields.getAll())
131+
.addMethod(constructorMethod(factoryFields));
132+
}
131133
gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
132134

133-
return factoryBuilder;
135+
return factoryBuilder
136+
.addMethod(getMethod(binding, factoryFields))
137+
.addMethod(staticCreateMethod(binding, factoryFields))
138+
.addMethod(staticProvisionMethod(binding));
134139
}
135140

136-
private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
137-
if (FactoryCreationStrategy.of(binding) == FactoryCreationStrategy.SINGLETON_INSTANCE) {
138-
return;
141+
// private static final class InstanceHolder {
142+
// private static final FooModule_ProvidesFooFactory INSTANCE =
143+
// new FooModule_ProvidesFooFactory();
144+
// }
145+
private TypeSpec staticInstanceHolderType(ProvisionBinding binding) {
146+
ClassName generatedClassName = generatedClassNameForBinding(binding);
147+
FieldSpec.Builder instanceHolderFieldBuilder =
148+
FieldSpec.builder(generatedClassName, "INSTANCE", PRIVATE, STATIC, FINAL)
149+
.initializer("new $T()", generatedClassName);
150+
if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
151+
// If the factory has type parameters, ignore them in the field declaration & initializer
152+
instanceHolderFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
139153
}
140-
// TODO(bcorso): Make the constructor private?
141-
MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
142-
constructorParams(binding).forEach(
143-
param -> {
144-
constructor.addParameter(param).addStatement("this.$1N = $1N", param);
145-
factoryBuilder.addField(
146-
FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
147-
});
148-
factoryBuilder.addMethod(constructor.build());
149-
}
150-
151-
private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
152-
ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
153-
moduleParameter(binding).ifPresent(params::add);
154-
frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
155-
return params.build();
154+
return TypeSpec.classBuilder(instanceHolderClassName(binding))
155+
.addModifiers(PRIVATE, STATIC, FINAL)
156+
.addField(instanceHolderFieldBuilder.build())
157+
.build();
156158
}
157159

158-
private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
159-
if (binding.requiresModuleInstance()) {
160-
// TODO(bcorso, dpb): Should this use contributingModule()?
161-
TypeName type = binding.bindingTypeElement().get().getType().getTypeName();
162-
return Optional.of(ParameterSpec.builder(type, "module").build());
163-
}
164-
return Optional.empty();
160+
private static ClassName instanceHolderClassName(ProvisionBinding binding) {
161+
return generatedClassNameForBinding(binding).nestedClass("InstanceHolder");
165162
}
166163

167-
private ImmutableMap<DependencyRequest, FieldSpec> frameworkFields(ProvisionBinding binding) {
168-
UniqueNameSet uniqueFieldNames = new UniqueNameSet();
169-
// TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
170-
moduleParameter(binding).ifPresent(module -> uniqueFieldNames.claim(module.name));
171-
// We avoid Maps.transformValues here because it would implicitly depend on the order in which
172-
// the transform function is evaluated on each entry in the map.
173-
ImmutableMap.Builder<DependencyRequest, FieldSpec> builder = ImmutableMap.builder();
174-
generateBindingFieldsForDependencies(binding).forEach(
175-
(dependency, field) ->
176-
builder.put(dependency,
177-
FieldSpec.builder(
178-
field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
179-
.build()));
180-
return builder.build();
164+
// public FooModule_ProvidesFooFactory(
165+
// FooModule module,
166+
// Provider<Bar> barProvider,
167+
// Provider<Baz> bazProvider) {
168+
// this.module = module;
169+
// this.barProvider = barProvider;
170+
// this.bazProvider = bazProvider;
171+
// }
172+
private MethodSpec constructorMethod(FactoryFields factoryFields) {
173+
// TODO(bcorso): Make the constructor private?
174+
MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
175+
factoryFields.getAll().forEach(
176+
field ->
177+
constructor
178+
.addParameter(field.type, field.name)
179+
.addStatement("this.$1N = $1N", field));
180+
return constructor.build();
181181
}
182182

183-
private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
184-
// If constructing a factory for @Inject or @Provides bindings, we use a static create method
185-
// so that generated components can avoid having to refer to the generic types
186-
// of the factory. (Otherwise they may have visibility problems referring to the types.)
183+
// Example 1: no dependencies.
184+
// public static FooModule_ProvidesFooFactory create() {
185+
// return InstanceHolder.INSTANCE;
186+
// }
187+
//
188+
// Example 2: with dependencies.
189+
// public static FooModule_ProvidesFooFactory create(
190+
// FooModule module,
191+
// Provider<Bar> barProvider,
192+
// Provider<Baz> bazProvider) {
193+
// return new FooModule_ProvidesFooFactory(module, barProvider, bazProvider);
194+
// }
195+
private MethodSpec staticCreateMethod(ProvisionBinding binding, FactoryFields factoryFields) {
196+
// We use a static create method so that generated components can avoid having to refer to the
197+
// generic types of the factory. (Otherwise they may have visibility problems referring to the
198+
// types.)
187199
MethodSpec.Builder createMethodBuilder =
188200
methodBuilder("create")
189201
.addModifiers(PUBLIC, STATIC)
190202
.returns(parameterizedGeneratedTypeNameForBinding(binding))
191203
.addTypeVariables(bindingTypeElementTypeVariableNames(binding));
192204

193-
switch (FactoryCreationStrategy.of(binding)) {
194-
case SINGLETON_INSTANCE:
195-
FieldSpec.Builder instanceFieldBuilder =
196-
FieldSpec.builder(
197-
generatedClassNameForBinding(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
198-
.initializer("new $T()", generatedClassNameForBinding(binding));
199-
200-
if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
201-
// If the factory has type parameters, ignore them in the field declaration & initializer
202-
instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
203-
createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
204-
}
205-
206-
ClassName instanceHolderName =
207-
generatedClassNameForBinding(binding).nestedClass("InstanceHolder");
208-
createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName);
209-
factoryBuilder.addType(
210-
TypeSpec.classBuilder(instanceHolderName)
211-
.addModifiers(PRIVATE, STATIC, FINAL)
212-
.addField(instanceFieldBuilder.build())
213-
.build());
214-
break;
215-
case CLASS_CONSTRUCTOR:
216-
List<ParameterSpec> params = constructorParams(binding);
217-
createMethodBuilder.addParameters(params);
218-
createMethodBuilder.addStatement(
219-
"return new $T($L)",
220-
parameterizedGeneratedTypeNameForBinding(binding),
221-
makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
222-
break;
223-
default:
224-
throw new AssertionError();
205+
if (factoryFields.isEmpty()) {
206+
if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
207+
createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
208+
}
209+
createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderClassName(binding));
210+
} else {
211+
ImmutableList<ParameterSpec> parameters =
212+
factoryFields.getAll().stream()
213+
.map(field -> ParameterSpec.builder(field.type, field.name).build())
214+
.collect(toImmutableList());
215+
createMethodBuilder
216+
.addParameters(parameters)
217+
.addStatement(
218+
"return new $T($L)",
219+
parameterizedGeneratedTypeNameForBinding(binding),
220+
parameterNames(parameters));
225221
}
226-
factoryBuilder.addMethod(createMethodBuilder.build());
222+
return createMethodBuilder.build();
227223
}
228224

229-
private MethodSpec getMethod(ProvisionBinding binding) {
225+
// Example 1: Provision binding.
226+
// @Override
227+
// public Foo get() {
228+
// return provideFoo(module, barProvider.get(), bazProvider.get());
229+
// }
230+
//
231+
// Example 2: Injection binding with some inject field.
232+
// @Override
233+
// public Foo get() {
234+
// Foo instance = newInstance(barProvider.get(), bazProvider.get());
235+
// Foo_MembersInjector.injectSomeField(instance, someFieldProvider.get());
236+
// return instance;
237+
// }
238+
private MethodSpec getMethod(ProvisionBinding binding, FactoryFields factoryFields) {
230239
UniqueNameSet uniqueFieldNames = new UniqueNameSet();
231-
ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
232-
frameworkFields.values().forEach(field -> uniqueFieldNames.claim(field.name));
240+
factoryFields.getAll().forEach(field -> uniqueFieldNames.claim(field.name));
233241
ImmutableMap<XExecutableParameterElement, ParameterSpec> assistedParameters =
234242
assistedParameters(binding).stream()
235243
.collect(
@@ -254,10 +262,10 @@ private MethodSpec getMethod(ProvisionBinding binding) {
254262
binding,
255263
request ->
256264
sourceFiles.frameworkTypeUsageStatement(
257-
CodeBlock.of("$N", frameworkFields.get(request)), request.kind()),
265+
CodeBlock.of("$N", factoryFields.get(request)), request.kind()),
258266
param -> assistedParameters.get(param).name,
259267
generatedClassNameForBinding(binding),
260-
moduleParameter(binding).map(module -> CodeBlock.of("$N", module)),
268+
factoryFields.moduleField.map(module -> CodeBlock.of("$N", module)),
261269
compilerOptions);
262270

263271
if (binding.kind().equals(PROVISION)) {
@@ -278,7 +286,8 @@ private MethodSpec getMethod(ProvisionBinding binding) {
278286
generatedClassNameForBinding(binding),
279287
instance,
280288
binding.key().type().xprocessing(),
281-
sourceFiles.frameworkFieldUsages(binding.dependencies(), frameworkFields)::get))
289+
sourceFiles.frameworkFieldUsages(
290+
binding.dependencies(), factoryFields.frameworkFields)::get))
282291
.addStatement("return $L", instance);
283292

284293
} else {
@@ -289,6 +298,19 @@ private MethodSpec getMethod(ProvisionBinding binding) {
289298
return getMethod.build();
290299
}
291300

301+
// Example 1: Provision binding
302+
// public static Foo provideFoo(FooModule module, Bar bar, Baz baz) {
303+
// return Preconditions.checkNotNullFromProvides(module.provideFoo(bar, baz));
304+
// }
305+
//
306+
// Example 2: Injection binding
307+
// public static Foo newInstance(Bar bar, Baz baz) {
308+
// return new Foo(bar, baz);
309+
// }
310+
private MethodSpec staticProvisionMethod(ProvisionBinding binding) {
311+
return ProvisionMethod.create(binding, compilerOptions);
312+
}
313+
292314
private AnnotationSpec scopeMetadataAnnotation(ProvisionBinding binding) {
293315
AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.SCOPE_METADATA);
294316
binding.scope()
@@ -324,31 +346,57 @@ private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
324346
: Optional.of(factoryOf(providedTypeName(binding)));
325347
}
326348

327-
private static ParameterSpec toParameter(FieldSpec field) {
328-
return ParameterSpec.builder(field.type, field.name).build();
329-
}
349+
/** Represents the available fields in the generated factory class. */
350+
private static final class FactoryFields {
351+
static FactoryFields create(ProvisionBinding binding) {
352+
UniqueNameSet nameSet = new UniqueNameSet();
353+
// TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
354+
Optional<FieldSpec> moduleField =
355+
binding.requiresModuleInstance()
356+
? Optional.of(
357+
createField(
358+
binding.bindingTypeElement().get().getType().getTypeName(),
359+
nameSet.getUniqueName("module")))
360+
: Optional.empty();
361+
362+
ImmutableMap.Builder<DependencyRequest, FieldSpec> frameworkFields = ImmutableMap.builder();
363+
generateBindingFieldsForDependencies(binding).forEach(
364+
(dependency, field) ->
365+
frameworkFields.put(
366+
dependency, createField(field.type(), nameSet.getUniqueName(field.name()))));
367+
368+
return new FactoryFields(moduleField, frameworkFields.buildOrThrow());
369+
}
330370

331-
/** The strategy for getting an instance of a factory for a {@link Binding}. */
332-
private enum FactoryCreationStrategy {
333-
/** The factory class is a single instance. */
334-
SINGLETON_INSTANCE,
335-
/** The factory must be created by calling the constructor. */
336-
CLASS_CONSTRUCTOR;
337-
338-
static FactoryCreationStrategy of(Binding binding) {
339-
switch (binding.kind()) {
340-
case PROVISION:
341-
return binding.dependencies().isEmpty() && !binding.requiresModuleInstance()
342-
? SINGLETON_INSTANCE
343-
: CLASS_CONSTRUCTOR;
344-
case INJECTION:
345-
case ASSISTED_INJECTION:
346-
return binding.dependencies().isEmpty()
347-
? SINGLETON_INSTANCE
348-
: CLASS_CONSTRUCTOR;
349-
default:
350-
throw new AssertionError("Unexpected binding kind: " + binding.kind());
351-
}
371+
private static FieldSpec createField(TypeName type, String name) {
372+
return FieldSpec.builder(type, name, PRIVATE, FINAL).build();
373+
}
374+
375+
private final Optional<FieldSpec> moduleField;
376+
private final ImmutableMap<DependencyRequest, FieldSpec> frameworkFields;
377+
378+
private FactoryFields(
379+
Optional<FieldSpec> moduleField,
380+
ImmutableMap<DependencyRequest, FieldSpec> frameworkFields) {
381+
this.moduleField = moduleField;
382+
this.frameworkFields = frameworkFields;
383+
}
384+
385+
FieldSpec get(DependencyRequest request) {
386+
return frameworkFields.get(request);
387+
}
388+
389+
ImmutableList<FieldSpec> getAll() {
390+
return moduleField.isPresent()
391+
? ImmutableList.<FieldSpec>builder()
392+
.add(moduleField.get())
393+
.addAll(frameworkFields.values())
394+
.build()
395+
: frameworkFields.values().asList();
396+
}
397+
398+
boolean isEmpty() {
399+
return getAll().isEmpty();
352400
}
353401
}
354402
}

0 commit comments

Comments
 (0)