Skip to content

Commit fdbc63e

Browse files
bcorsoDagger Team
authored andcommitted
[Refactor]: Move ProvisionMethods#create()/InjectionSiteMethods#create() into the generator that uses it.
Note: this CL shouldn't cause any changes to the generated code. `FactoryGenerator` is the only class that uses `ProvisionMethods#create()`, and `MembersInjectorGenerator` is the only class that uses `InjectionSiteMethods#create()`. Thus, I'm moving these methods into these classes for better encapsulation. This will also make the XPoet migration easier since I plan to migrate `FactoryGenerator` and `MembersInjectorGenerator` separately. Note that these methods were sharing some logic that now needs to be duplicated, but IMO this creates more readable code since we're no longer entangling the two use cases. For example, now that `ProvisionMethods` and `InjectionSiteMethods` define their own versions of `methodProxy()` method, we no longer need the multiple enums (i.e. `InstanceCastPolicy` and `CheckNotNullPolicy`) as input to these methods. RELNOTES=N/A PiperOrigin-RevId: 700035303
1 parent 2b84e57 commit fdbc63e

File tree

4 files changed

+262
-256
lines changed

4 files changed

+262
-256
lines changed

java/dagger/internal/codegen/binding/SourceFiles.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
import static dagger.internal.codegen.model.BindingKind.ASSISTED_INJECTION;
3737
import static dagger.internal.codegen.model.BindingKind.INJECTION;
3838
import static dagger.internal.codegen.xprocessing.XElements.asExecutable;
39+
import static dagger.internal.codegen.xprocessing.XElements.asMethod;
3940
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
4041
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
4142
import static dagger.internal.codegen.xprocessing.XTypeElements.typeVariableNames;
4243
import static javax.lang.model.SourceVersion.isName;
4344

4445
import androidx.room.compiler.processing.XExecutableElement;
4546
import androidx.room.compiler.processing.XFieldElement;
47+
import androidx.room.compiler.processing.XMethodElement;
4648
import androidx.room.compiler.processing.XTypeElement;
4749
import com.google.common.base.Joiner;
4850
import com.google.common.collect.ImmutableList;
@@ -58,6 +60,7 @@
5860
import com.squareup.javapoet.TypeVariableName;
5961
import dagger.internal.codegen.base.MapType;
6062
import dagger.internal.codegen.base.SetType;
63+
import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
6164
import dagger.internal.codegen.javapoet.TypeNames;
6265
import dagger.internal.codegen.model.DependencyRequest;
6366
import dagger.internal.codegen.model.RequestKind;
@@ -144,6 +147,23 @@ public ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
144147
dep -> frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep)), dep.kind()));
145148
}
146149

150+
public static String generatedProxyMethodName(ContributionBinding binding) {
151+
switch (binding.kind()) {
152+
case INJECTION:
153+
case ASSISTED_INJECTION:
154+
return "newInstance";
155+
case PROVISION:
156+
XMethodElement method = asMethod(binding.bindingElement().get());
157+
String simpleName = getSimpleName(method);
158+
// If the simple name is already defined in the factory, prepend "proxy" to the name.
159+
return simpleName.contentEquals("get") || simpleName.contentEquals("create")
160+
? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, simpleName)
161+
: simpleName;
162+
default:
163+
throw new AssertionError("Unexpected binding kind: " + binding);
164+
}
165+
}
166+
147167
/** Returns the generated factory or members injector name for a binding. */
148168
public static ClassName generatedClassNameForBinding(Binding binding) {
149169
switch (binding.kind()) {
@@ -206,6 +226,29 @@ public static String memberInjectedFieldSignatureForVariable(XFieldElement field
206226
return field.getEnclosingElement().getClassName().canonicalName() + "." + getSimpleName(field);
207227
}
208228

229+
/*
230+
* TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
231+
*
232+
* - @Inject void members() {} will generate a method that conflicts with the instance
233+
* method `injectMembers(T)`
234+
* - Adding the index could conflict with another member:
235+
* @Inject void a(Object o) {}
236+
* @Inject void a(String s) {}
237+
* @Inject void a1(String s) {}
238+
*
239+
* Here, Method a(String) will add the suffix "1", which will conflict with the method
240+
* generated for a1(String)
241+
* - Members named "members" or "methods" could also conflict with the {@code static} injection
242+
* method.
243+
*/
244+
public static String membersInjectorMethodName(InjectionSite injectionSite) {
245+
int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
246+
String indexString = index == 0 ? "" : String.valueOf(index + 1);
247+
return "inject"
248+
+ LOWER_CAMEL.to(UPPER_CAMEL, getSimpleName(injectionSite.element()))
249+
+ indexString;
250+
}
251+
209252
public static String classFileName(ClassName className) {
210253
return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
211254
}

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

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
2525
import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
2626
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
27+
import static dagger.internal.codegen.binding.SourceFiles.generatedProxyMethodName;
2728
import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
2829
import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
2930
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
@@ -36,15 +37,26 @@
3637
import static dagger.internal.codegen.model.BindingKind.INJECTION;
3738
import static dagger.internal.codegen.model.BindingKind.PROVISION;
3839
import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
40+
import static dagger.internal.codegen.writing.InjectionMethods.copyParameter;
41+
import static dagger.internal.codegen.writing.InjectionMethods.copyParameters;
42+
import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
43+
import static dagger.internal.codegen.xprocessing.XElements.asMethod;
44+
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
45+
import static dagger.internal.codegen.xprocessing.XTypeElements.typeVariableNames;
3946
import static javax.lang.model.element.Modifier.FINAL;
4047
import static javax.lang.model.element.Modifier.PRIVATE;
4148
import static javax.lang.model.element.Modifier.PUBLIC;
4249
import static javax.lang.model.element.Modifier.STATIC;
4350

51+
import androidx.room.compiler.processing.XConstructorElement;
4452
import androidx.room.compiler.processing.XElement;
53+
import androidx.room.compiler.processing.XExecutableElement;
4554
import androidx.room.compiler.processing.XExecutableParameterElement;
4655
import androidx.room.compiler.processing.XFiler;
56+
import androidx.room.compiler.processing.XMethodElement;
4757
import androidx.room.compiler.processing.XProcessingEnv;
58+
import androidx.room.compiler.processing.XType;
59+
import androidx.room.compiler.processing.XTypeElement;
4860
import com.google.common.collect.ImmutableList;
4961
import com.google.common.collect.ImmutableMap;
5062
import com.google.common.collect.ImmutableSet;
@@ -56,6 +68,7 @@
5668
import com.squareup.javapoet.ParameterSpec;
5769
import com.squareup.javapoet.TypeName;
5870
import com.squareup.javapoet.TypeSpec;
71+
import dagger.internal.Preconditions;
5972
import dagger.internal.codegen.base.SourceFileGenerator;
6073
import dagger.internal.codegen.base.UniqueNameSet;
6174
import dagger.internal.codegen.binding.AssistedInjectionBinding;
@@ -73,6 +86,7 @@
7386
import dagger.internal.codegen.model.Scope;
7487
import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
7588
import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
89+
import dagger.internal.codegen.xprocessing.Nullability;
7690
import java.util.Optional;
7791
import java.util.stream.Stream;
7892
import javax.inject.Inject;
@@ -136,7 +150,7 @@ private TypeSpec.Builder factoryBuilder(ContributionBinding binding) {
136150
return factoryBuilder
137151
.addMethod(getMethod(binding, factoryFields))
138152
.addMethod(staticCreateMethod(binding, factoryFields))
139-
.addMethod(staticProvisionMethod(binding));
153+
.addMethod(staticProxyMethod(binding));
140154
}
141155

142156
// private static final class InstanceHolder {
@@ -298,17 +312,101 @@ private MethodSpec getMethod(ContributionBinding binding, FactoryFields factoryF
298312
return getMethod.build();
299313
}
300314

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-
// }
315+
private MethodSpec staticProxyMethod(ContributionBinding binding) {
316+
switch (binding.kind()) {
317+
case INJECTION:
318+
case ASSISTED_INJECTION:
319+
return staticProxyMethodForInjection(binding);
320+
case PROVISION:
321+
return staticProxyMethodForProvision((ProvisionBinding) binding);
322+
default:
323+
throw new AssertionError("Unexpected binding kind: " + binding);
324+
}
325+
}
326+
327+
// Example:
305328
//
306-
// Example 2: Injection binding
307329
// public static Foo newInstance(Bar bar, Baz baz) {
308330
// return new Foo(bar, baz);
309331
// }
310-
private MethodSpec staticProvisionMethod(ContributionBinding binding) {
311-
return ProvisionMethod.create(binding, compilerOptions);
332+
private static MethodSpec staticProxyMethodForInjection(ContributionBinding binding) {
333+
XConstructorElement constructor = asConstructor(binding.bindingElement().get());
334+
XTypeElement enclosingType = constructor.getEnclosingElement();
335+
MethodSpec.Builder builder =
336+
methodBuilder(generatedProxyMethodName(binding))
337+
.addModifiers(PUBLIC, STATIC)
338+
.varargs(constructor.isVarArgs())
339+
.returns(enclosingType.getType().getTypeName())
340+
.addTypeVariables(typeVariableNames(enclosingType))
341+
.addExceptions(getThrownTypes(constructor));
342+
CodeBlock arguments = copyParameters(builder, new UniqueNameSet(), constructor.getParameters());
343+
return builder
344+
.addStatement("return new $T($L)", enclosingType.getType().getTypeName(), arguments)
345+
.build();
346+
}
347+
348+
// Example:
349+
//
350+
// public static Foo provideFoo(FooModule module, Bar bar, Baz baz) {
351+
// return Preconditions.checkNotNullFromProvides(module.provideFoo(bar, baz));
352+
// }
353+
private MethodSpec staticProxyMethodForProvision(ProvisionBinding binding) {
354+
XMethodElement method = asMethod(binding.bindingElement().get());
355+
MethodSpec.Builder builder =
356+
methodBuilder(generatedProxyMethodName(binding))
357+
.addModifiers(PUBLIC, STATIC)
358+
.varargs(method.isVarArgs())
359+
.addExceptions(getThrownTypes(method));
360+
361+
XTypeElement enclosingType = asTypeElement(method.getEnclosingElement());
362+
UniqueNameSet parameterNameSet = new UniqueNameSet();
363+
CodeBlock module;
364+
if (method.isStatic() || enclosingType.isCompanionObject()) {
365+
module = CodeBlock.of("$T", enclosingType.getClassName());
366+
} else if (enclosingType.isKotlinObject()) {
367+
// Call through the singleton instance.
368+
// See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods
369+
module = CodeBlock.of("$T.INSTANCE", enclosingType.getClassName());
370+
} else {
371+
builder.addTypeVariables(typeVariableNames(enclosingType));
372+
module = copyInstance(builder, parameterNameSet, enclosingType.getType());
373+
}
374+
CodeBlock arguments = copyParameters(builder, parameterNameSet, method.getParameters());
375+
CodeBlock invocation = CodeBlock.of("$L.$L($L)", module, method.getJvmName(), arguments);
376+
377+
Nullability nullability = Nullability.of(method);
378+
nullability
379+
.nonTypeUseNullableAnnotations()
380+
.forEach(builder::addAnnotation);
381+
return builder
382+
.returns(
383+
method.getReturnType().getTypeName()
384+
.annotated(
385+
nullability.typeUseNullableAnnotations().stream()
386+
.map(annotation -> AnnotationSpec.builder(annotation).build())
387+
.collect(toImmutableList())))
388+
.addStatement("return $L", maybeWrapInCheckForNull(binding, invocation))
389+
.build();
390+
}
391+
392+
private CodeBlock maybeWrapInCheckForNull(ProvisionBinding binding, CodeBlock codeBlock) {
393+
return binding.shouldCheckForNull(compilerOptions)
394+
? CodeBlock.of("$T.checkNotNullFromProvides($L)", Preconditions.class, codeBlock)
395+
: codeBlock;
396+
}
397+
398+
private static CodeBlock copyInstance(
399+
MethodSpec.Builder methodBuilder, UniqueNameSet parameterNameSet, XType type) {
400+
return copyParameter(
401+
methodBuilder,
402+
type,
403+
parameterNameSet.getUniqueName("instance"),
404+
/* useObject= */ false,
405+
Nullability.NOT_NULLABLE);
406+
}
407+
408+
private static ImmutableList<TypeName> getThrownTypes(XExecutableElement executable) {
409+
return executable.getThrownTypes().stream().map(XType::getTypeName).collect(toImmutableList());
312410
}
313411

314412
private AnnotationSpec scopeMetadataAnnotation(ContributionBinding binding) {

0 commit comments

Comments
 (0)