Inline members injection in components and factories.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=164619091
diff --git a/java/dagger/internal/codegen/AbstractComponentWriter.java b/java/dagger/internal/codegen/AbstractComponentWriter.java
index b8ae53a..add096a 100644
--- a/java/dagger/internal/codegen/AbstractComponentWriter.java
+++ b/java/dagger/internal/codegen/AbstractComponentWriter.java
@@ -16,6 +16,7 @@
package dagger.internal.codegen;
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.Preconditions.checkArgument;
@@ -26,6 +27,7 @@
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
import static dagger.internal.codegen.BindingKey.contribution;
import static dagger.internal.codegen.BindingType.PRODUCTION;
@@ -62,8 +64,10 @@
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -79,6 +83,8 @@
import dagger.internal.Preconditions;
import dagger.internal.TypedReleasableReferenceManagerDecorator;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.releasablereferences.CanReleaseReferences;
@@ -118,6 +124,7 @@
protected final ImmutableMap<ComponentDescriptor, String> subcomponentNames;
protected final TypeSpec.Builder component;
private final UniqueNameSet componentFieldNames = new UniqueNameSet();
+ private final UniqueNameSet componentMethodNames = new UniqueNameSet();
// TODO(user): Merge these two maps if we refactor BindingKey to allow us to unify
// these two key spaces
private final Map<BindingKey, BindingExpression> bindingExpressions = new LinkedHashMap<>();
@@ -126,6 +133,8 @@
private final List<CodeBlock> initializations = new ArrayList<>();
protected final List<MethodSpec> interfaceMethods = new ArrayList<>();
private final BindingExpression.Factory bindingExpressionFactory;
+
+ private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
protected final MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE);
private final OptionalFactories optionalFactories;
private ComponentBuilder builder;
@@ -297,12 +306,18 @@
if (hasBuilder()) {
addBuilder();
}
+
+ getLocalAndInheritedMethods(
+ graph.componentDescriptor().componentDefinitionType(), types, elements)
+ .forEach(method -> componentMethodNames.claim(method.getSimpleName()));
+
addFactoryMethods();
addReferenceReleasingProviderManagerFields();
createBindingExpressions();
implementInterfaceMethods();
addSubcomponents();
writeInitializeAndInterfaceMethods();
+ writeMembersInjectionMethods();
component.addMethod(constructor.build());
if (graph.componentDescriptor().kind().isTopLevel()) {
optionalFactories.addMembers(component);
@@ -473,17 +488,29 @@
interfaceMethodSignatures.add(signature);
MethodSpec.Builder interfaceMethod =
methodSpecForComponentMethod(methodElement, requestType);
- CodeBlock codeBlock = getRequestFulfillment(interfaceRequest);
List<? extends VariableElement> parameters = methodElement.getParameters();
if (interfaceRequest.kind().equals(DependencyRequest.Kind.MEMBERS_INJECTOR)
&& !parameters.isEmpty() /* i.e. it's not a request for a MembersInjector<T> */) {
- Name parameterName = getOnlyElement(parameters).getSimpleName();
- interfaceMethod.addStatement("$L.injectMembers($L)", codeBlock, parameterName);
- if (!requestType.getReturnType().getKind().equals(VOID)) {
- interfaceMethod.addStatement("return $L", parameterName);
+ ParameterSpec parameter = ParameterSpec.get(getOnlyElement(parameters));
+ MembersInjectionBinding binding =
+ graph
+ .resolvedBindings()
+ .get(interfaceRequest.bindingKey())
+ .membersInjectionBinding()
+ .get();
+ if (requestType.getReturnType().getKind().equals(VOID)) {
+ if (!binding.injectionSites().isEmpty()) {
+ interfaceMethod.addStatement(
+ "$N($N)", getMembersInjectionMethod(binding.key()), parameter);
+ }
+ } else if (binding.injectionSites().isEmpty()) {
+ interfaceMethod.addStatement("return $N", parameter);
+ } else {
+ interfaceMethod.addStatement(
+ "return $N($N)", getMembersInjectionMethod(binding.key()), parameter);
}
} else {
- interfaceMethod.addStatement("return $L", codeBlock);
+ interfaceMethod.addStatement("return $L", getRequestFulfillment(interfaceRequest));
}
interfaceMethods.add(interfaceMethod.build());
}
@@ -494,7 +521,7 @@
private MethodSpec.Builder methodSpecForComponentMethod(
ExecutableElement method, ExecutableType methodType) {
String methodName = method.getSimpleName().toString();
- MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName);
+ MethodSpec.Builder methodBuilder = methodBuilder(methodName);
methodBuilder.addAnnotation(Override.class);
@@ -614,6 +641,60 @@
}
}
+ private void writeMembersInjectionMethods() {
+ component.addMethods(membersInjectionMethods.values());
+ }
+
+ @Override
+ public MethodSpec getMembersInjectionMethod(Key key) {
+ return membersInjectionMethods.computeIfAbsent(key, this::membersInjectionMethod);
+ }
+
+ private MethodSpec membersInjectionMethod(Key key) {
+ Binding binding =
+ MoreObjects.firstNonNull(
+ graph.resolvedBindings().get(BindingKey.membersInjection(key)),
+ graph.resolvedBindings().get(BindingKey.contribution(key)))
+ .binding();
+ TypeMirror keyType = binding.key().type();
+ TypeName membersInjectedType =
+ isTypeAccessibleFrom(keyType, name.packageName()) ? TypeName.get(keyType) : TypeName.OBJECT;
+ Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName();
+ // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class
+ // simple names Foo.Builder -> injectFooBuilder
+ String methodName = componentMethodNames.getUniqueName("inject" + bindingTypeName);
+ ParameterSpec parameter =
+ ParameterSpec.builder(
+ membersInjectedType, UPPER_CAMEL.to(LOWER_CAMEL, bindingTypeName.toString()))
+ .build();
+ MethodSpec.Builder method =
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .returns(membersInjectedType)
+ .addParameter(parameter);
+ TypeElement canIgnoreReturnValue =
+ elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
+ if (canIgnoreReturnValue != null) {
+ method.addAnnotation(ClassName.get(canIgnoreReturnValue));
+ }
+ CodeBlock instance = CodeBlock.of("$N", parameter);
+ method.addCode(
+ InjectionSiteMethod.invokeAll(
+ injectionSites(binding), name, instance, this::getRequestFulfillment));
+ method.addStatement("return $L", instance);
+
+ return method.build();
+ }
+
+ static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
+ if (binding instanceof ProvisionBinding) {
+ return ((ProvisionBinding) binding).injectionSites();
+ } else if (binding instanceof MembersInjectionBinding) {
+ return ((MembersInjectionBinding) binding).injectionSites();
+ }
+ throw new IllegalArgumentException(binding.key().toString());
+ }
+
private CodeBlock membersInjectionBindingInitialization(BindingExpression bindingExpression) {
BindingKey bindingKey = bindingExpression.bindingKey();
MembersInjectionBinding binding =
diff --git a/java/dagger/internal/codegen/Binding.java b/java/dagger/internal/codegen/Binding.java
index bb1e073..1820692 100644
--- a/java/dagger/internal/codegen/Binding.java
+++ b/java/dagger/internal/codegen/Binding.java
@@ -76,20 +76,27 @@
* user-defined injection site. This returns an unmodifiable set.
*/
// TODO(gak): this will eventually get changed to return a set of FrameworkDependency
- Set<DependencyRequest> implicitDependencies() {
+ ImmutableSet<DependencyRequest> implicitDependencies() {
return ImmutableSet.of();
}
+ private final Supplier<ImmutableSet<DependencyRequest>> dependencies =
+ memoize(
+ () -> {
+ ImmutableSet<DependencyRequest> implicitDependencies = implicitDependencies();
+ return ImmutableSet.copyOf(
+ implicitDependencies.isEmpty()
+ ? explicitDependencies()
+ : Sets.union(implicitDependencies, explicitDependencies()));
+ });
+
/**
* The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is the
* union of {@link #explicitDependencies()} and {@link #implicitDependencies()}. This returns an
* unmodifiable set.
*/
- final Set<DependencyRequest> dependencies() {
- Set<DependencyRequest> implicitDependencies = implicitDependencies();
- return implicitDependencies.isEmpty()
- ? explicitDependencies()
- : Sets.union(implicitDependencies, explicitDependencies());
+ final ImmutableSet<DependencyRequest> dependencies() {
+ return dependencies.get();
}
private final Supplier<ImmutableList<FrameworkDependency>> frameworkDependencies =
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 59a2bd1..d84c86a 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -199,7 +199,7 @@
this.hasBindingExpressions = checkNotNull(hasBindingExpressions);
this.subcomponentNames = checkNotNull(subcomponentNames);
this.graph = checkNotNull(graph);
- this.elements = elements;
+ this.elements = checkNotNull(elements);
}
/** Creates a binding expression for a field. */
@@ -300,8 +300,7 @@
hasBindingExpressions);
case INJECTION:
case PROVISION:
- if (provisionBinding.implicitDependencies().isEmpty()
- && !provisionBinding.scope().isPresent()
+ if (!provisionBinding.scope().isPresent()
&& !provisionBinding.requiresModuleInstance()
&& provisionBinding.bindingElement().isPresent()) {
return new SimpleMethodRequestFulfillment(
diff --git a/java/dagger/internal/codegen/ComponentProcessor.java b/java/dagger/internal/codegen/ComponentProcessor.java
index f140bc7..b9b8c51 100644
--- a/java/dagger/internal/codegen/ComponentProcessor.java
+++ b/java/dagger/internal/codegen/ComponentProcessor.java
@@ -104,6 +104,20 @@
elements, types, moduleValidator, subcomponentValidator, builderValidator);
MapKeyValidator mapKeyValidator = new MapKeyValidator(elements);
+ DependencyRequest.Factory dependencyRequestFactory =
+ new DependencyRequest.Factory(keyFactory);
+ MembersInjectionBinding.Factory membersInjectionBindingFactory =
+ new MembersInjectionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
+ ProvisionBinding.Factory provisionBindingFactory =
+ new ProvisionBinding.Factory(
+ types, keyFactory, dependencyRequestFactory, membersInjectionBindingFactory);
+ ProductionBinding.Factory productionBindingFactory =
+ new ProductionBinding.Factory(types, keyFactory, dependencyRequestFactory);
+ MultibindingDeclaration.Factory multibindingDeclarationFactory =
+ new MultibindingDeclaration.Factory(elements, types, keyFactory);
+ SubcomponentDeclaration.Factory subcomponentDeclarationFactory =
+ new SubcomponentDeclaration.Factory(keyFactory);
+
this.factoryGenerator =
new FactoryGenerator(filer, elements, compilerOptions, injectValidatorWhenGeneratingCode);
this.membersInjectorGenerator =
@@ -117,20 +131,6 @@
ProductionExecutorModuleGenerator productionExecutorModuleGenerator =
new ProductionExecutorModuleGenerator(filer, elements);
- DependencyRequest.Factory dependencyRequestFactory =
- new DependencyRequest.Factory(keyFactory);
- ProvisionBinding.Factory provisionBindingFactory =
- new ProvisionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
- ProductionBinding.Factory productionBindingFactory =
- new ProductionBinding.Factory(types, keyFactory, dependencyRequestFactory);
- MultibindingDeclaration.Factory multibindingDeclarationFactory =
- new MultibindingDeclaration.Factory(elements, types, keyFactory);
- SubcomponentDeclaration.Factory subcomponentDeclarationFactory =
- new SubcomponentDeclaration.Factory(keyFactory);
-
- MembersInjectionBinding.Factory membersInjectionBindingFactory =
- new MembersInjectionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
-
DelegateDeclaration.Factory bindingDelegateDeclarationFactory =
new DelegateDeclaration.Factory(types, keyFactory, dependencyRequestFactory);
OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory =
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index b95f04a..81eb8d9 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -285,10 +285,6 @@
abstract B key(Key key);
- abstract B explicitDependencies(Iterable<DependencyRequest> dependencies);
-
- abstract B explicitDependencies(DependencyRequest... dependencies);
-
abstract B nullableType(Optional<DeclaredType> nullableType);
abstract B wrappedMapKey(Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKey);
diff --git a/java/dagger/internal/codegen/DependencyRequest.java b/java/dagger/internal/codegen/DependencyRequest.java
index 2dc8a28..bbd899a 100644
--- a/java/dagger/internal/codegen/DependencyRequest.java
+++ b/java/dagger/internal/codegen/DependencyRequest.java
@@ -27,6 +27,7 @@
import static dagger.internal.codegen.Optionals.firstPresent;
import static dagger.internal.codegen.TypeNames.lazyOf;
import static dagger.internal.codegen.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.TypeNames.membersInjectorOf;
import static dagger.internal.codegen.TypeNames.producedOf;
import static dagger.internal.codegen.TypeNames.producerOf;
import static dagger.internal.codegen.TypeNames.providerOf;
@@ -153,6 +154,9 @@
case FUTURE:
return listenableFutureOf(keyType);
+ case MEMBERS_INJECTOR:
+ return membersInjectorOf(keyType);
+
default:
throw new AssertionError(this);
}
@@ -182,6 +186,17 @@
/** The element that declares this dependency request. Absent for synthetic requests. */
abstract Optional<Element> requestElement();
+ /**
+ * Returns {@code true} if {@code requestElement}'s type is a primitive type.
+ *
+ * <p>Because the {@link #key()} of a {@link DependencyRequest} is {@linkplain
+ * Key.Factory#boxPrimitives(TypeMirror) boxed} to normalize it with other keys, this inspects the
+ * {@link #requestElement()} directly.
+ */
+ boolean requestsPrimitiveType() {
+ return requestElement().map(element -> element.asType().getKind().isPrimitive()).orElse(false);
+ }
+
/** Returns true if this request allows null objects. */
abstract boolean isNullable();
diff --git a/java/dagger/internal/codegen/FactoryGenerator.java b/java/dagger/internal/codegen/FactoryGenerator.java
index 8d6b513..52a4903 100644
--- a/java/dagger/internal/codegen/FactoryGenerator.java
+++ b/java/dagger/internal/codegen/FactoryGenerator.java
@@ -28,10 +28,8 @@
import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD;
import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
-import static dagger.internal.codegen.Proxies.createProxy;
-import static dagger.internal.codegen.Proxies.shouldGenerateProxy;
import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
@@ -41,7 +39,6 @@
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
-import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -56,15 +53,15 @@
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import dagger.internal.Factory;
-import dagger.internal.MembersInjectors;
import dagger.internal.Preconditions;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.processing.Filer;
import javax.inject.Inject;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
import javax.lang.model.util.Elements;
/**
@@ -220,13 +217,9 @@
factoryBuilder.addMethod(constructorBuilder.get().build());
}
- List<CodeBlock> parameters = Lists.newArrayList();
- for (DependencyRequest dependency : binding.explicitDependencies()) {
- parameters.add(
- frameworkTypeUsageStatement(
- CodeBlock.of("$N", fields.get(dependency.bindingKey())), dependency.kind()));
- }
- CodeBlock parametersCodeBlock = makeParametersCodeBlock(parameters);
+ CodeBlock parametersCodeBlock =
+ makeParametersCodeBlock(
+ frameworkFieldUsages(binding.provisionDependencies(), fields).values());
MethodSpec.Builder getMethodBuilder =
methodBuilder("get")
@@ -235,6 +228,8 @@
.addModifiers(PUBLIC);
if (binding.bindingKind().equals(PROVISION)) {
+ // TODO(dpb): take advantage of the code in InjectionMethods so this doesn't have to be
+ // duplicated
binding
.nullableType()
.ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethodBuilder, nullableType));
@@ -251,13 +246,17 @@
!binding.nullableType().isPresent() && compilerOptions.doCheckForNulls()
? checkNotNullProvidesMethod(methodCall)
: methodCall);
- } else if (binding.membersInjectionRequest().isPresent()) {
- getMethodBuilder.addStatement(
- "return $T.injectMembers($N, new $T($L))",
- MembersInjectors.class,
- fields.get(binding.membersInjectionRequest().get().bindingKey()),
- providedTypeName,
- parametersCodeBlock);
+ } else if (!binding.injectionSites().isEmpty()) {
+ CodeBlock instance = CodeBlock.of("instance");
+ getMethodBuilder
+ .addStatement("$1T $2L = new $1T($3L)", providedTypeName, instance, parametersCodeBlock)
+ .addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ generatedTypeName,
+ instance,
+ frameworkFieldUsages(binding.dependencies(), fields)::get))
+ .addStatement("return $L", instance);
} else {
getMethodBuilder.addStatement("return new $T($L)", providedTypeName, parametersCodeBlock);
}
@@ -267,7 +266,7 @@
factoryBuilder.addMethod(createMethod.get());
}
- proxyMethodFor(binding).ifPresent(factoryBuilder::addMethod);
+ ProvisionMethod.create(binding).ifPresent(factoryBuilder::addMethod);
gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
@@ -286,21 +285,6 @@
CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD);
}
- /**
- * Returns a method to proxy access to the binding's {@link Binding#bindingElement()}, which
- * behaves according to the description in {@link Proxies}. Use here is further restricted by
- * whether or not members injection is required, since that is not yet implemented for proxy
- * methods, but will be added.
- */
- // TODO(gak): support accessibility proxies for types with injected members as well
- private static Optional<MethodSpec> proxyMethodFor(ProvisionBinding binding) {
- ExecutableElement executableElement = MoreElements.asExecutable(binding.bindingElement().get());
- if (binding.membersInjectionRequest().isPresent() || !shouldGenerateProxy(executableElement)) {
- return Optional.empty();
- }
- return Optional.of(createProxy(executableElement));
- }
-
@CanIgnoreReturnValue
private FieldSpec addConstructorParameterAndTypeField(
TypeName typeName,
diff --git a/java/dagger/internal/codegen/HasBindingExpressions.java b/java/dagger/internal/codegen/HasBindingExpressions.java
index 30ee2cf..b9691a6 100644
--- a/java/dagger/internal/codegen/HasBindingExpressions.java
+++ b/java/dagger/internal/codegen/HasBindingExpressions.java
@@ -18,6 +18,7 @@
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
import javax.annotation.Nullable;
/** An object which associates a {@link BindingExpression} instance with a {@link BindingKey}. */
@@ -41,4 +42,9 @@
/** Adds the given code block to the initialize methods of the component. */
void addInitialization(CodeBlock codeBlock);
+
+ /**
+ * Returns the {@code private} members injection method that injects objects with the {@code key}.
+ */
+ MethodSpec getMembersInjectionMethod(Key key);
}
diff --git a/java/dagger/internal/codegen/InjectionMethods.java b/java/dagger/internal/codegen/InjectionMethods.java
new file mode 100644
index 0000000..d0ca9a3
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionMethods.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
+import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.TypeNames.rawTypeName;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.DependencyRequest.Kind;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Parameterizable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Injection methods are static methods that implement provision and/or injection in one step:
+ *
+ * <ul>
+ * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary
+ * <li>methods that call {@code @Provides} module methods
+ * <li>methods that perform members injection
+ * </ul>
+ */
+// TODO(ronshapiro): add examples for each class of injection method
+final class InjectionMethods {
+ /**
+ * A static method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
+ * constructor. Its parameters match the dependency requests for constructor and members
+ * injection.
+ *
+ * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
+ * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
+ * necessary and this method returns {@link Optional#empty()}.
+ *
+ * <p>TODO(ronshapiro): At the moment non-static {@code @Provides} methods are not supported.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * abstract class FooModule {
+ * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
+ * }
+ *
+ * public static proxyProvideFoo(Bar bar, Baz baz) { … }
+ * </code></pre>
+ *
+ * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
+ * raw parameter types are publicly accessible, no method is necessary and this method returns
+ * {@code Optional#empty()}.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Foo(Bar bar) {}
+ * }
+ *
+ * public static Foo newFoo(Bar bar) { … }
+ * </code></pre>
+ */
+ static final class ProvisionMethod {
+
+ /**
+ * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
+ * constructor} and injects the instance's members, if necessary. If {@link
+ * #shouldCreateInjectionMethod(ProvisionBinding) no method is necessary}, then {@link
+ * Optional#empty()} is returned.
+ */
+ static Optional<MethodSpec> create(ProvisionBinding binding) {
+ if (!shouldCreateInjectionMethod(binding)) {
+ return Optional.empty();
+ }
+ ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
+ switch (element.getKind()) {
+ case CONSTRUCTOR:
+ return Optional.of(constructorProxy(element));
+ case METHOD:
+ return Optional.of(
+ methodProxy(element, methodName(element), ReceiverAccessibility.IGNORE));
+ default:
+ throw new AssertionError(element);
+ }
+ }
+
+ /**
+ * Invokes the injection method for {@code binding}, with the dependencies transformed with the
+ * {@code dependencyUsage} function.
+ */
+ static CodeBlock invoke(
+ ProvisionBinding binding,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass) {
+ return callInjectionMethod(
+ create(binding).get().name,
+ // TODO(dpb): would this be simpler if injectionMethodArguments returned a List?
+ ImmutableList.of(
+ injectionMethodArguments(
+ binding.provisionDependencies(), dependencyUsage, requestingClass)),
+ generatedClassNameForBinding(binding),
+ requestingClass);
+ }
+
+ private static MethodSpec constructorProxy(ExecutableElement constructor) {
+ UniqueNameSet names = new UniqueNameSet();
+ TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
+ MethodSpec.Builder method =
+ methodBuilder(methodName(constructor))
+ .returns(TypeName.get(enclosingType.asType()))
+ .addModifiers(PUBLIC, STATIC);
+
+ copyTypeParameters(enclosingType, method);
+ copyThrows(constructor, method);
+
+ return method
+ .addStatement(
+ "return new $T($L)", enclosingType, copyParameters(constructor, method, names))
+ .build();
+ }
+
+ /**
+ * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
+ * requires the use of an injection method.
+ */
+ static boolean requiresInjectionMethod(ProvisionBinding binding, String callingPackage) {
+ ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
+ return !binding.injectionSites().isEmpty()
+ || !isElementAccessibleFrom(method, callingPackage)
+ || method
+ .getParameters()
+ .stream()
+ .map(VariableElement::asType)
+ .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
+ }
+
+ private static boolean shouldCreateInjectionMethod(ProvisionBinding binding) {
+ return requiresInjectionMethod(binding, "dagger.should.never.exist");
+ }
+
+ /**
+ * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
+ * associated with {@code @Inject} constructors, the method will also inject all {@link
+ * InjectionSite}s.
+ */
+ private static String methodName(ExecutableElement method) {
+ switch (method.getKind()) {
+ case CONSTRUCTOR:
+ return "new" + method.getEnclosingElement().getSimpleName();
+ case METHOD:
+ return "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
+ default:
+ throw new AssertionError(method);
+ }
+ }
+ }
+
+ /**
+ * A static method that injects one member of an instance of a type. Its first parameter is an
+ * instance of the type to be injected. The remaining parameters match the dependency requests for
+ * the injection site.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Bar bar;
+ * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
+ * }
+ *
+ * public static injectBar(Foo instance, Bar bar) { … }
+ * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
+ * </code></pre>
+ */
+ static final class InjectionSiteMethod {
+ /**
+ * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
+ * that's in a different package), a method in the supertype's package must be generated to give
+ * the subclass's members injector a way to inject it. Each potentially inaccessible member
+ * receives its own method, as the subclass may need to inject them in a different order from
+ * the parent class.
+ */
+ static MethodSpec create(InjectionSite injectionSite) {
+ String methodName = methodName(injectionSite);
+ switch (injectionSite.kind()) {
+ case METHOD:
+ return methodProxy(
+ MoreElements.asExecutable(injectionSite.element()),
+ methodName,
+ ReceiverAccessibility.CAST_IF_NOT_PUBLIC);
+ case FIELD:
+ return fieldProxy(MoreElements.asVariable(injectionSite.element()), methodName);
+ default:
+ throw new AssertionError(injectionSite);
+ }
+ }
+
+ /**
+ * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
+ * transformed using the {@code dependencyUsage} function.
+ */
+ static CodeBlock invokeAll(
+ ImmutableSet<InjectionSite> injectionSites,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ Function<DependencyRequest, CodeBlock> dependencyUsage) {
+ return injectionSites
+ .stream()
+ .map(
+ injectionSite ->
+ CodeBlock.of(
+ "$L;",
+ invoke(injectionSite, generatedTypeName, instanceCodeBlock, dependencyUsage)))
+ .collect(toConcatenatedCodeBlock());
+ }
+
+ /**
+ * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
+ * using the {@code dependencyUsage} function.
+ */
+ private static CodeBlock invoke(
+ InjectionSite injectionSite,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ Function<DependencyRequest, CodeBlock> dependencyUsage) {
+ List<CodeBlock> arguments = new ArrayList<>();
+ arguments.add(instanceCodeBlock);
+ if (!injectionSite.dependencies().isEmpty()) {
+ arguments.addAll(
+ injectionSite
+ .dependencies()
+ .stream()
+ .map(dependencyUsage)
+ .collect(toList()));
+ }
+ return callInjectionMethod(
+ create(injectionSite).name,
+ arguments,
+ membersInjectorNameForType(
+ MoreElements.asType(injectionSite.element().getEnclosingElement())),
+ generatedTypeName);
+ }
+
+ /*
+ * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
+ *
+ * - @Inject void members() {} will generate a method that conflicts with the instance
+ * method `injectMembers(T)`
+ * - Adding the index could conflict with another member:
+ * @Inject void a(Object o) {}
+ * @Inject void a(String s) {}
+ * @Inject void a1(String s) {}
+ *
+ * Here, Method a(String) will add the suffix "1", which will conflict with the method
+ * generated for a1(String)
+ * - Members named "members" or "methods" could also conflict with the {@code static} injection
+ * method.
+ */
+ private static String methodName(InjectionSite injectionSite) {
+ int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
+ String indexString = index == 0 ? "" : String.valueOf(index + 1);
+ return "inject"
+ + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
+ + indexString;
+ }
+ }
+
+ /**
+ * Returns an argument list suitable for calling an injection method. Down-casts any arguments
+ * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
+ *
+ * @param dependencies the dependencies used by the method
+ * @param dependencyUsage function to apply on each of {@code dependencies} before casting
+ * @param requestingClass the class calling the injection method
+ */
+ private static CodeBlock injectionMethodArguments(
+ ImmutableSet<DependencyRequest> dependencies,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass) {
+ return dependencies.stream()
+ .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
+ .collect(toParametersCodeBlock());
+ }
+
+ private static CodeBlock injectionMethodArgument(
+ DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
+ TypeMirror keyType = dependency.key().type();
+ CodeBlock.Builder codeBlock = CodeBlock.builder();
+ if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
+ && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
+ if (!dependency.kind().equals(Kind.INSTANCE)) {
+ TypeName usageTypeName = accessibleType(dependency);
+ codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
+ } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
+ codeBlock.add("($T)", keyType);
+ }
+ }
+ return codeBlock.add(argument).build();
+ }
+
+ /**
+ * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
+ * {@link Object}.
+ */
+ private static TypeName accessibleType(DependencyRequest dependency) {
+ TypeName typeName = dependency.kind().typeName(accessibleType(dependency.key().type()));
+ return dependency.requestsPrimitiveType() ? typeName.unbox() : typeName;
+ }
+
+ /**
+ * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
+ * Object}.
+ */
+ private static TypeName accessibleType(TypeMirror type) {
+ return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
+ }
+
+ private static CodeBlock callInjectionMethod(
+ String methodName,
+ List<CodeBlock> arguments,
+ ClassName enclosingClass,
+ ClassName requestingClass) {
+ CodeBlock.Builder invocation = CodeBlock.builder();
+ if (!enclosingClass.equals(requestingClass)) {
+ invocation.add("$T.", enclosingClass);
+ }
+ return invocation.add("$L($L)", methodName, makeParametersCodeBlock(arguments)).build();
+ }
+
+ private enum ReceiverAccessibility {
+ CAST_IF_NOT_PUBLIC {
+ @Override
+ TypeName parameterType(TypeMirror type) {
+ return accessibleType(type);
+ }
+
+ @Override
+ CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
+ return instanceWithPotentialCast(instance, instanceType);
+ }
+ },
+ IGNORE {
+ @Override
+ TypeName parameterType(TypeMirror type) {
+ return TypeName.get(type);
+ }
+
+ @Override
+ CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
+ return instance;
+ }
+ },
+ ;
+
+ abstract TypeName parameterType(TypeMirror type);
+ abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
+ }
+
+ private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
+ return isRawTypePubliclyAccessible(instanceType)
+ ? instance
+ : CodeBlock.of("(($T) $L)", instanceType, instance);
+ }
+
+ private static MethodSpec methodProxy(
+ ExecutableElement method, String methodName, ReceiverAccessibility receiverAccessibility) {
+ TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
+ MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
+
+ UniqueNameSet nameSet = new UniqueNameSet();
+ if (!method.getModifiers().contains(STATIC)) {
+ methodBuilder.addParameter(
+ receiverAccessibility.parameterType(enclosingType.asType()),
+ nameSet.getUniqueName("instance"));
+ }
+ CodeBlock arguments = copyParameters(method, methodBuilder, nameSet);
+ if (!method.getReturnType().getKind().equals(VOID)) {
+ methodBuilder.returns(TypeName.get(method.getReturnType()));
+ getNullableType(method)
+ .ifPresent(nullableType -> CodeBlocks.addAnnotation(methodBuilder, nullableType));
+ methodBuilder.addCode("return ");
+ }
+ if (method.getModifiers().contains(STATIC)) {
+ methodBuilder.addCode("$T", rawTypeName(TypeName.get(enclosingType.asType())));
+ } else {
+ copyTypeParameters(enclosingType, methodBuilder);
+ // "instance" is guaranteed b/c it was the first name into the UniqueNameSet
+ methodBuilder.addCode(
+ receiverAccessibility.potentiallyCast(CodeBlock.of("instance"), enclosingType.asType()));
+ }
+ copyTypeParameters(method, methodBuilder);
+ copyThrows(method, methodBuilder);
+
+ methodBuilder.addCode(".$N($L);", method.getSimpleName(), arguments);
+ return methodBuilder.build();
+ }
+
+ private static MethodSpec fieldProxy(VariableElement field, String methodName) {
+ TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
+ MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
+ copyTypeParameters(enclosingType, methodBuilder);
+
+ UniqueNameSet nameSet = new UniqueNameSet();
+ String instanceName = nameSet.getUniqueName("instance");
+ methodBuilder.addParameter(accessibleType(enclosingType.asType()), instanceName);
+ return methodBuilder
+ .addCode(
+ "$L.$L = $L;",
+ instanceWithPotentialCast(CodeBlock.of(instanceName), enclosingType.asType()),
+ field.getSimpleName(),
+ copyParameter(field, methodBuilder, nameSet))
+ .build();
+ }
+
+ private static void copyThrows(ExecutableElement method, MethodSpec.Builder methodBuilder) {
+ for (TypeMirror thrownType : method.getThrownTypes()) {
+ methodBuilder.addException(TypeName.get(thrownType));
+ }
+ }
+
+ private static CodeBlock copyParameters(
+ ExecutableElement method, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
+ ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
+ for (VariableElement parameter : method.getParameters()) {
+ argumentsBuilder.add(copyParameter(parameter, methodBuilder, nameSet));
+ }
+ methodBuilder.varargs(method.isVarArgs());
+ return makeParametersCodeBlock(argumentsBuilder.build());
+ }
+
+ private static CodeBlock copyParameter(
+ VariableElement element, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
+ TypeMirror elementType = element.asType();
+ boolean useObject = !isRawTypePubliclyAccessible(elementType);
+ TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(elementType);
+ String name = nameSet.getUniqueName(element.getSimpleName().toString());
+ ParameterSpec parameter =
+ ParameterSpec.builder(typeName, name).build();
+ methodBuilder.addParameter(parameter);
+ return useObject
+ ? CodeBlock.of("($T) $N", elementType, parameter)
+ : CodeBlock.of("$N", parameter);
+ }
+
+ private static void copyTypeParameters(
+ Parameterizable parameterizable, MethodSpec.Builder methodBuilder) {
+ for (TypeParameterElement typeParameterElement : parameterizable.getTypeParameters()) {
+ methodBuilder.addTypeVariable(TypeVariableName.get(typeParameterElement));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/MembersInjectionBinding.java b/java/dagger/internal/codegen/MembersInjectionBinding.java
index 0322841..384cd5c 100644
--- a/java/dagger/internal/codegen/MembersInjectionBinding.java
+++ b/java/dagger/internal/codegen/MembersInjectionBinding.java
@@ -124,10 +124,6 @@
* Returns the index of {@link #element()} in its parents {@code @Inject} members that have the
* same simple name. This method filters out private elements so that the results will be
* consistent independent of whether the build system uses header jars or not.
- *
- * <p>This allows {@link MembersInjectorGenerator} to generate unique {@linkplain
- * MembersInjectorGenerator#injectionSiteDelegateMethodName(InjectionSite)} delegate injection
- * methods}.
*/
@Memoized
int indexAmongAtInjectMembersWithSameSimpleName() {
diff --git a/java/dagger/internal/codegen/MembersInjectorGenerator.java b/java/dagger/internal/codegen/MembersInjectorGenerator.java
index 5e8381e..4d1a08a 100644
--- a/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ b/java/dagger/internal/codegen/MembersInjectorGenerator.java
@@ -16,24 +16,18 @@
package dagger.internal.codegen;
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeName.VOID;
import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
import static dagger.internal.codegen.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
@@ -43,30 +37,22 @@
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
-import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import dagger.MembersInjector;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.producers.Producer;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
-import java.util.Set;
import javax.annotation.processing.Filer;
-import javax.inject.Provider;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
@@ -121,16 +107,9 @@
MethodSpec.Builder injectMembersBuilder =
methodBuilder("injectMembers")
- .returns(VOID)
.addModifiers(PUBLIC)
.addAnnotation(Override.class)
- .addParameter(injectedTypeName, "instance")
- .addCode("if (instance == null) {")
- .addStatement(
- "throw new $T($S)",
- NullPointerException.class,
- "Cannot inject members into a null reference")
- .addCode("}");
+ .addParameter(injectedTypeName, "instance");
ImmutableMap<BindingKey, FrameworkField> fields = generateBindingFieldsForDependencies(binding);
@@ -193,174 +172,27 @@
injectorTypeBuilder.addMethod(createMethodBuilder.build());
ImmutableMap<BindingKey, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
- List<MethodSpec> injectMethodsForSubclasses = new ArrayList<>();
- for (InjectionSite injectionSite : binding.injectionSites()) {
- injectMembersBuilder.addCode(
- isElementAccessibleFrom(injectionSite.element(), generatedTypeName.packageName())
- ? directInjectMemberCodeBlock(binding, dependencyFields, injectionSite)
- : delegateInjectMemberCodeBlock(dependencyFields, injectionSite));
- if (!injectionSite.element().getModifiers().contains(PUBLIC)
- && injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
- injectMethodsForSubclasses.add(
- injectorMethodForSubclasses(
- dependencyFields, typeParameters, injectedTypeName, injectionSite));
- }
- }
+
+ injectMembersBuilder.addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ generatedTypeName,
+ CodeBlock.of("instance"),
+ frameworkFieldUsages(binding.dependencies(), dependencyFields)::get));
if (usesRawFrameworkTypes) {
injectMembersBuilder.addAnnotation(suppressWarnings(UNCHECKED));
}
-
injectorTypeBuilder.addMethod(injectMembersBuilder.build());
- injectMethodsForSubclasses.forEach(injectorTypeBuilder::addMethod);
+
+ for (InjectionSite injectionSite : binding.injectionSites()) {
+ if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
+ injectorTypeBuilder.addMethod(InjectionSiteMethod.create(injectionSite));
+ }
+ }
gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation);
return Optional.of(injectorTypeBuilder);
}
-
- /** Returns a code block that directly injects the instance's field or method. */
- private CodeBlock directInjectMemberCodeBlock(
- MembersInjectionBinding binding,
- ImmutableMap<BindingKey, FieldSpec> dependencyFields,
- InjectionSite injectionSite) {
- return CodeBlock.of(
- injectionSite.element().getKind().isField() ? "$L.$L = $L;" : "$L.$L($L);",
- getInstanceCodeBlockWithPotentialCast(
- injectionSite.element().getEnclosingElement(), binding.membersInjectedType()),
- injectionSite.element().getSimpleName(),
- makeParametersCodeBlock(
- parameterCodeBlocks(dependencyFields, injectionSite.dependencies(), true)));
- }
-
- /**
- * Returns a code block that injects the instance's field or method by calling a static method on
- * the parent MembersInjector class.
- */
- private CodeBlock delegateInjectMemberCodeBlock(
- ImmutableMap<BindingKey, FieldSpec> dependencyFields, InjectionSite injectionSite) {
- return CodeBlock.of(
- "$T.$L($L);",
- membersInjectorNameForType(
- MoreElements.asType(injectionSite.element().getEnclosingElement())),
- injectionSiteDelegateMethodName(injectionSite),
- makeParametersCodeBlock(
- new ImmutableList.Builder<CodeBlock>()
- .add(CodeBlock.of("instance"))
- .addAll(parameterCodeBlocks(dependencyFields, injectionSite.dependencies(), false))
- .build()));
- }
-
- /**
- * Returns the parameters for injecting a member.
- *
- * @param passValue if {@code true}, each parameter code block will be the result of converting
- * the field from the framework type ({@link Provider}, {@link Producer}, etc.) to the real
- * value; if {@code false}, each parameter code block will be just the field
- */
- private ImmutableList<CodeBlock> parameterCodeBlocks(
- ImmutableMap<BindingKey, FieldSpec> dependencyFields,
- ImmutableSet<DependencyRequest> dependencies,
- boolean passValue) {
- ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
- for (DependencyRequest dependency : dependencies) {
- CodeBlock fieldCodeBlock =
- CodeBlock.of("$L", dependencyFields.get(dependency.bindingKey()).name);
- parameters.add(
- passValue
- ? frameworkTypeUsageStatement(fieldCodeBlock, dependency.kind())
- : fieldCodeBlock);
- }
- return parameters.build();
- }
-
- private CodeBlock getInstanceCodeBlockWithPotentialCast(
- Element injectionSiteElement, Element bindingElement) {
- if (injectionSiteElement.equals(bindingElement)) {
- return CodeBlock.of("instance");
- }
- TypeName injectionSiteName = TypeName.get(injectionSiteElement.asType());
- if (injectionSiteName instanceof ParameterizedTypeName) {
- injectionSiteName = ((ParameterizedTypeName) injectionSiteName).rawType;
- }
- return CodeBlock.of("(($T) instance)", injectionSiteName);
- }
-
- private static String injectionSiteDelegateMethodName(InjectionSite injectionSite) {
- int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
- String indexString = index == 0 ? "" : String.valueOf(index + 1);
- return "inject"
- + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
- + indexString;
- }
-
- private MethodSpec injectorMethodForSubclasses(
- ImmutableMap<BindingKey, FieldSpec> dependencyFields,
- List<TypeVariableName> typeParameters,
- TypeName injectedTypeName,
- InjectionSite injectionSite) {
- Element injectionElement = injectionSite.element();
- MethodSpec.Builder methodBuilder =
- methodBuilder(injectionSiteDelegateMethodName(injectionSite))
- .addModifiers(PUBLIC, STATIC)
- .addParameter(injectedTypeName, "instance")
- .addTypeVariables(typeParameters);
- ImmutableList.Builder<CodeBlock> providedParameters = ImmutableList.builder();
- Set<String> parameterNames = new HashSet<>();
- for (DependencyRequest dependency : injectionSite.dependencies()) {
- FieldSpec field = dependencyFields.get(dependency.bindingKey());
- ParameterSpec parameter =
- ParameterSpec.builder(
- field.type,
- staticInjectMethodDependencyParameterName(parameterNames, dependency, field))
- .build();
- methodBuilder.addParameter(parameter);
- providedParameters.add(
- frameworkTypeUsageStatement(CodeBlock.of("$N", parameter), dependency.kind()));
- }
- if (injectionElement.getKind().isField()) {
- methodBuilder.addStatement(
- "instance.$L = $L",
- injectionElement.getSimpleName(),
- getOnlyElement(providedParameters.build()));
- } else {
- methodBuilder.addStatement(
- "instance.$L($L)",
- injectionElement.getSimpleName(),
- makeParametersCodeBlock(providedParameters.build()));
- }
- return methodBuilder.build();
- }
-
- /**
- * Returns the static inject method parameter name for a dependency.
- *
- * @param parameterNames the parameter names used so far
- * @param dependency the dependency
- * @param field the field used to hold the framework type for the dependency
- */
- private String staticInjectMethodDependencyParameterName(
- Set<String> parameterNames, DependencyRequest dependency, FieldSpec field) {
- StringBuilder parameterName =
- new StringBuilder(dependency.requestElement().get().getSimpleName().toString());
- switch (dependency.kind()) {
- case LAZY:
- case INSTANCE:
- case FUTURE:
- String suffix = ((ParameterizedTypeName) field.type).rawType.simpleName();
- if (parameterName.length() <= suffix.length()
- || !parameterName.substring(parameterName.length() - suffix.length()).equals(suffix)) {
- parameterName.append(suffix);
- }
- break;
-
- default:
- break;
- }
- int baseLength = parameterName.length();
- for (int i = 2; !parameterNames.add(parameterName.toString()); i++) {
- parameterName.replace(baseLength, parameterName.length(), String.valueOf(i));
- }
- return parameterName.toString();
- }
}
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
index 1c80263..4a71dea 100644
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -109,6 +109,10 @@
@AutoValue.Builder
@CanIgnoreReturnValue
abstract static class Builder extends ContributionBinding.Builder<Builder> {
+ abstract Builder explicitDependencies(Iterable<DependencyRequest> dependencies);
+
+ abstract Builder explicitDependencies(DependencyRequest... dependencies);
+
abstract Builder productionKind(ProductionKind productionKind);
abstract Builder thrownTypes(Iterable<? extends TypeMirror> thrownTypes);
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
index d5779e9..003520d 100644
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -24,31 +24,31 @@
import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.MapKeys.getMapKey;
import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
+import static dagger.internal.codegen.Util.toImmutableSet;
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
-import static javax.lang.model.element.ElementKind.FIELD;
import static javax.lang.model.element.ElementKind.METHOD;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import dagger.internal.codegen.ComponentDescriptor.BuilderRequirementMethod;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
import java.util.Optional;
import javax.annotation.CheckReturnValue;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
@@ -62,14 +62,32 @@
abstract class ProvisionBinding extends ContributionBinding {
@Override
- ImmutableSet<DependencyRequest> implicitDependencies() {
- return membersInjectionRequest().isPresent()
- ? ImmutableSet.of(membersInjectionRequest().get())
- : ImmutableSet.of();
+ @Memoized
+ ImmutableSet<DependencyRequest> explicitDependencies() {
+ return ImmutableSet.<DependencyRequest>builder()
+ .addAll(provisionDependencies())
+ .addAll(membersInjectionDependencies())
+ .build();
}
- /** If this provision requires members injection, this will be the corresponding request. */
- abstract Optional<DependencyRequest> membersInjectionRequest();
+ /**
+ * Dependencies necessary to invoke an {@code @Inject} constructor or {@code @Provides} method.
+ */
+ abstract ImmutableSet<DependencyRequest> provisionDependencies();
+
+ @Memoized
+ ImmutableSet<DependencyRequest> membersInjectionDependencies() {
+ return injectionSites()
+ .stream()
+ .flatMap(i -> i.dependencies().stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * {@link InjectionSite}s for all {@code @Inject} members if {@link #bindingKind()} is {@link
+ * ContributionBinding.Kind#INJECTION}, otherwise empty.
+ */
+ abstract ImmutableSortedSet<InjectionSite> injectionSites();
@Override
public BindingType bindingType() {
@@ -84,7 +102,8 @@
private static Builder builder() {
return new AutoValue_ProvisionBinding.Builder()
- .explicitDependencies(ImmutableSet.<DependencyRequest>of());
+ .provisionDependencies(ImmutableSet.of())
+ .injectionSites(ImmutableSortedSet.of());
}
abstract Builder toBuilder();
@@ -92,8 +111,10 @@
@AutoValue.Builder
@CanIgnoreReturnValue
abstract static class Builder extends ContributionBinding.Builder<Builder> {
+ abstract Builder provisionDependencies(DependencyRequest... provisionDependencies);
+ abstract Builder provisionDependencies(ImmutableSet<DependencyRequest> provisionDependencies);
- abstract Builder membersInjectionRequest(Optional<DependencyRequest> membersInjectionRequest);
+ abstract Builder injectionSites(ImmutableSortedSet<InjectionSite> injectionSites);
abstract Builder unresolved(ProvisionBinding unresolved);
@@ -104,17 +125,20 @@
}
static final class Factory {
- private final Elements elements;
private final Types types;
private final Key.Factory keyFactory;
private final DependencyRequest.Factory dependencyRequestFactory;
+ private final MembersInjectionBinding.Factory membersInjectionBindingFactory;
- Factory(Elements elements, Types types, Key.Factory keyFactory,
- DependencyRequest.Factory dependencyRequestFactory) {
- this.elements = elements;
+ Factory(
+ Types types,
+ Key.Factory keyFactory,
+ DependencyRequest.Factory dependencyRequestFactory,
+ MembersInjectionBinding.Factory membersInjectionBindingFactory) {
this.types = types;
this.keyFactory = keyFactory;
this.dependencyRequestFactory = dependencyRequestFactory;
+ this.membersInjectionBindingFactory = membersInjectionBindingFactory;
}
/**
@@ -145,19 +169,23 @@
Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType);
checkArgument(!key.qualifier().isPresent());
- ImmutableSet<DependencyRequest> dependencies =
+ ImmutableSet<DependencyRequest> provisionDependencies =
dependencyRequestFactory.forRequiredResolvedVariables(
constructorElement.getParameters(), cxtorType.getParameterTypes());
- Optional<DependencyRequest> membersInjectionRequest =
- membersInjectionRequest(enclosingCxtorType);
+ // TODO(ronshapiro): instead of creating a MembersInjectionBinding just to retrieve the
+ // injection sites, create an InjectionSite.Factory and pass that in here.
+ ImmutableSortedSet<InjectionSite> injectionSites =
+ membersInjectionBindingFactory
+ .forInjectedType(enclosingCxtorType, Optional.empty())
+ .injectionSites();
ProvisionBinding.Builder builder =
ProvisionBinding.builder()
.contributionType(ContributionType.UNIQUE)
.bindingElement(constructorElement)
.key(key)
- .explicitDependencies(dependencies)
- .membersInjectionRequest(membersInjectionRequest)
+ .provisionDependencies(provisionDependencies)
+ .injectionSites(injectionSites)
.bindingKind(Kind.INJECTION)
.scope(Scope.uniqueScopeOf(constructorElement.getEnclosingElement()));
@@ -169,24 +197,6 @@
return builder.build();
}
- private static final ImmutableSet<ElementKind> MEMBER_KINDS =
- Sets.immutableEnumSet(METHOD, FIELD);
-
- private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) {
- TypeElement typeElement = MoreElements.asType(type.asElement());
- if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(),
- typeElement.getSuperclass())) {
- return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
- }
- for (Element enclosedElement : typeElement.getEnclosedElements()) {
- if (MEMBER_KINDS.contains(enclosedElement.getKind())
- && (isAnnotationPresent(enclosedElement, Inject.class))) {
- return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
- }
- }
- return Optional.empty();
- }
-
ProvisionBinding forProvidesMethod(
ExecutableElement providesMethod, TypeElement contributedBy) {
checkArgument(providesMethod.getKind().equals(METHOD));
@@ -203,7 +213,7 @@
.bindingElement(providesMethod)
.contributingModule(contributedBy)
.key(key)
- .explicitDependencies(dependencies)
+ .provisionDependencies(dependencies)
.nullableType(ConfigurationAnnotations.getNullableType(providesMethod))
.wrappedMapKey(wrapOptionalInEquivalence(getMapKey(providesMethod)))
.bindingKind(Kind.PROVISION)
@@ -221,7 +231,7 @@
return ProvisionBinding.builder()
.contributionType(ContributionType.UNIQUE)
.key(mapOfValuesKey)
- .explicitDependencies(requestForMapOfProviders)
+ .provisionDependencies(requestForMapOfProviders)
.bindingKind(Kind.SYNTHETIC_MAP)
.build();
}
@@ -237,7 +247,7 @@
return ProvisionBinding.builder()
.contributionType(ContributionType.UNIQUE)
.key(key)
- .explicitDependencies(
+ .provisionDependencies(
dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
.bindingKind(Kind.forMultibindingKey(key))
.build();
@@ -327,7 +337,7 @@
.bindingElement(delegateDeclaration.bindingElement().get())
.contributingModule(delegateDeclaration.contributingModule().get())
.key(keyFactory.forDelegateBinding(delegateDeclaration, Provider.class))
- .explicitDependencies(delegateDeclaration.delegateRequest())
+ .provisionDependencies(delegateDeclaration.delegateRequest())
.wrappedMapKey(delegateDeclaration.wrappedMapKey())
.bindingKind(Kind.SYNTHETIC_DELEGATE_BINDING)
.scope(Scope.uniqueScopeOf(delegateDeclaration.bindingElement().get()));
@@ -396,7 +406,7 @@
ProvisionBinding syntheticPresentBinding(Key key, DependencyRequest.Kind kind) {
return syntheticAbsentBinding(key)
.toBuilder()
- .explicitDependencies(
+ .provisionDependencies(
dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, kind))
.build();
}
diff --git a/java/dagger/internal/codegen/Proxies.java b/java/dagger/internal/codegen/Proxies.java
deleted file mode 100644
index 61d8a40..0000000
--- a/java/dagger/internal/codegen/Proxies.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.toArray;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
-import static dagger.internal.codegen.Accessibility.isElementPubliclyAccessible;
-import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.CodeBlocks.javadocLinkTo;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
-import static dagger.internal.codegen.TypeNames.rawTypeName;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.type.TypeKind.VOID;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableList;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeVariableName;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.Parameterizable;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Proxy methods ("proxies") are generated methods used to give component implementations access to
- * {@link Element}s that are inaccessible as written in the source code. For example, a component
- * cannot directly invoke a package-private {@code @Inject} constructor in a different package.
- *
- * <p>Since proxies are generated separately from their usages, they cannot make any assumptions
- * about the types or packages from which methods will be invoked. Thus, any type or element that is
- * not public is considered to be "inaccessible".
- *
- * <p>This class generates proxies for any {@link ExecutableElement}, but the form of the methods
- * are somewhat tailored to how they are used within components.
- *
- * <p>Proxies have the following attributes:
- *
- * <ul>
- * <li>Proxies are always {@code public}, {@code static} methods.
- * <li>The return type of the proxy is always the return type of the method or the constructed
- * type regardless of its accessibility. For example, if a proxied method returns {@code
- * MyPackagePrivateClass}, the proxy method will also return {@code MyPackagePrivateClass}
- * because the accessibility of the return type does not impact callers.
- * <li>If the method being proxied is {@code @Nullable}, so will the proxy be.
- * <li>Proxies for constructors are named "{@code newTypeName}" (where "{@code TypeName}" is the
- * name of the type being constructed) and proxies for methods are named "{@code
- * proxyMethodName}" (where "{@code methodName}" is the name of the method being proxied).
- * <li>If the element being proxied is an instance method, the first parameter will be the
- * instance.
- * <li>The rest of the parameters of the proxy method are that of the proxied method unless the
- * raw type of a parameter is inaccessible, in which case it is {@link Object}. Passing an
- * object to this method that is not of the proxied parameter type will result in a {@link
- * ClassCastException}.
- * <p>While it is not required by the language that a method's parameter types be accessible
- * to invoke it, components often hold references to {@link javax.inject.Provider} as raw
- * types in order to dodge similar accessibility restrictions. This means that the {@code
- * {@link javax.inject.Provider#get()}} method will return {@link Object}. Since it cannot be
- * cast to the the more specific type on the calling side, we must accept {@link Object} in
- * the proxy method.
- * </ul>
- *
- * <p>Proxies are not generated under the following conditions:
- *
- * <ul>
- * <li>If an {@link ExecutableElement} is publicly accessible and all of its {@linkplain
- * ExecutableElement#getParameters() parameters} are publicly accessible types, no proxy is
- * necessary. If the type of a parameter has a type argument that is is inaccessible, but the
- * raw type that is accessible, the type is considered to be accessible because callers can
- * always hold references to the raw type.
- * <li>If an {@link ExecutableElement} or any of its enclosing types are {@code private}, no proxy
- * is generated because it is impossible to write Java (without reflection) that accesses the
- * element.
- * </ul>
- */
-final class Proxies {
-
- /**
- * Returns {@code true} if the given method has limited access, thus requiring a proxy for some
- * cases.
- */
- static boolean shouldGenerateProxy(ExecutableElement method) {
- return !isElementPubliclyAccessible(method)
- || method
- .getParameters()
- .stream()
- .map(VariableElement::asType)
- .anyMatch(type -> !isRawTypePubliclyAccessible(type));
- }
-
- /** Returns {@code true} if accessing the given method from the given package requires a proxy. */
- static boolean requiresProxyAccess(ExecutableElement method, String callingPackage) {
- return !isElementAccessibleFrom(method, callingPackage)
- || method
- .getParameters()
- .stream()
- .map(VariableElement::asType)
- .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
- }
-
- /** Returns the name of the method that proxies access to the given method. */
- static String proxyName(ExecutableElement method) {
- switch (method.getKind()) {
- case CONSTRUCTOR:
- return "new" + method.getEnclosingElement().getSimpleName();
- case METHOD:
- return "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
- case STATIC_INIT:
- case INSTANCE_INIT:
- throw new IllegalArgumentException(
- "cannot proxy initializers because they cannot be invoked directly: " + method);
- default:
- throw new AssertionError(method);
- }
- }
-
- /**
- * Returns a proxy method implementation for the method.
- *
- * @throws IllegalArgumentException if the method is publicly accessible
- */
- // TODO(gak): expand support to proxy fields
- static MethodSpec createProxy(ExecutableElement method) {
- checkArgument(
- shouldGenerateProxy(method),
- "method and all of its arguments are accessible; proxy isn't necessary: %s",
- method);
- final MethodSpec.Builder builder;
- switch (method.getKind()) {
- case CONSTRUCTOR:
- builder = forConstructor(method);
- break;
- case METHOD:
- builder = forMethod(method);
- break;
- default:
- throw new AssertionError();
- }
- builder.addJavadoc("Proxies $L.", javadocLinkTo(method));
- builder.addModifiers(PUBLIC, STATIC);
-
- copyTypeParameters(method, builder);
- copyThrows(method, builder);
-
- return builder.build();
- }
-
- private static MethodSpec.Builder forConstructor(ExecutableElement constructor) {
- TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
- MethodSpec.Builder methodBuilder = methodBuilder(proxyName(constructor));
-
- copyTypeParameters(enclosingType, methodBuilder);
-
- methodBuilder.returns(TypeName.get(enclosingType.asType()));
-
- CodeBlock arguments =
- copyParameters(
- constructor, methodBuilder, new UniqueNameSet(), new ImmutableList.Builder<>());
-
- methodBuilder.addCode("return new $T($L);", enclosingType, arguments);
-
- return methodBuilder;
- }
-
- private static MethodSpec.Builder forMethod(ExecutableElement method) {
- TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
- MethodSpec.Builder methodBuilder = methodBuilder(proxyName(method));
-
- UniqueNameSet nameSet = new UniqueNameSet();
- ImmutableList.Builder<CodeBlock> argumentsBuilder = new ImmutableList.Builder<>();
- if (!method.getModifiers().contains(STATIC)) {
- methodBuilder.addParameter(
- TypeName.get(enclosingType.asType()), nameSet.getUniqueName("instance"));
- }
- CodeBlock arguments = copyParameters(method, methodBuilder, nameSet, argumentsBuilder);
- if (!method.getReturnType().getKind().equals(VOID)) {
- methodBuilder.returns(TypeName.get(method.getReturnType()));
- getNullableType(method)
- .ifPresent(nullableType -> CodeBlocks.addAnnotation(methodBuilder, nullableType));
- methodBuilder.addCode("return ");
- }
- if (method.getModifiers().contains(STATIC)) {
- methodBuilder.addCode("$T", rawTypeName(TypeName.get(enclosingType.asType())));
- } else {
- copyTypeParameters(enclosingType, methodBuilder);
- // "instance" is guaranteed b/c it was the first name into the UniqueNameSet
- methodBuilder.addCode("instance", method.getSimpleName());
- }
- methodBuilder.addCode(".$N($L);", method.getSimpleName(), arguments);
- return methodBuilder;
- }
-
- private static void copyThrows(ExecutableElement method, MethodSpec.Builder methodBuilder) {
- for (TypeMirror thrownType : method.getThrownTypes()) {
- methodBuilder.addException(TypeName.get(thrownType));
- }
- }
-
- private static CodeBlock copyParameters(
- ExecutableElement method,
- MethodSpec.Builder methodBuilder,
- UniqueNameSet nameSet,
- ImmutableList.Builder<CodeBlock> argumentsBuilder) {
- for (VariableElement parameter : method.getParameters()) {
- TypeMirror parameterType = parameter.asType();
- boolean useObject = !isRawTypePubliclyAccessible(parameterType);
- TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(parameterType);
- String name = nameSet.getUniqueName(parameter.getSimpleName().toString());
- argumentsBuilder.add(
- useObject ? CodeBlock.of("($T) $L", parameterType, name) : CodeBlock.of(name));
- ParameterSpec.Builder parameterBuilder =
- ParameterSpec.builder(typeName, name)
- .addModifiers(toArray(parameter.getModifiers(), Modifier.class));
- methodBuilder.addParameter(parameterBuilder.build());
- }
- methodBuilder.varargs(method.isVarArgs());
- return makeParametersCodeBlock(argumentsBuilder.build());
- }
-
- private static void copyTypeParameters(
- Parameterizable parameterizable, MethodSpec.Builder methodBuilder) {
- for (TypeParameterElement typeParameterElement : parameterizable.getTypeParameters()) {
- methodBuilder.addTypeVariable(TypeVariableName.get(typeParameterElement));
- }
- }
-
- private Proxies() {}
-}
diff --git a/java/dagger/internal/codegen/SimpleMethodRequestFulfillment.java b/java/dagger/internal/codegen/SimpleMethodRequestFulfillment.java
index d5325ae..260f58d 100644
--- a/java/dagger/internal/codegen/SimpleMethodRequestFulfillment.java
+++ b/java/dagger/internal/codegen/SimpleMethodRequestFulfillment.java
@@ -21,21 +21,20 @@
import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
-import static dagger.internal.codegen.Proxies.proxyName;
-import static dagger.internal.codegen.Proxies.requiresProxyAccess;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
import static dagger.internal.codegen.TypeNames.rawTypeName;
-import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.STATIC;
+import com.google.auto.common.MoreTypes;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
/**
@@ -44,7 +43,6 @@
* delegates to one that uses a {@link javax.inject.Provider}.
*/
final class SimpleMethodRequestFulfillment extends SimpleInvocationRequestFulfillment {
-
private final CompilerOptions compilerOptions;
private final ProvisionBinding provisionBinding;
private final HasBindingExpressions hasBindingExpressions;
@@ -69,34 +67,23 @@
@Override
CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass) {
- ExecutableElement bindingElement = asExecutable(provisionBinding.bindingElement().get());
- return requiresProxyAccess(bindingElement, requestingClass.packageName())
- ? invokeProxyMethod(requestingClass)
+ return requiresInjectionMethod(provisionBinding, requestingClass.packageName())
+ ? invokeInjectionMethod(requestingClass)
: invokeMethod(requestingClass);
}
private CodeBlock invokeMethod(ClassName requestingClass) {
- CodeBlock parametersCodeBlock =
- makeParametersCodeBlock(
- provisionBinding
- .explicitDependencies()
- .stream()
- .map(
- request -> {
- CodeBlock snippet = getDependencySnippet(requestingClass, request);
- TypeMirror requestElementType = request.requestElement().get().asType();
- return isTypeAccessibleFrom(requestElementType, requestingClass.packageName())
- ? snippet
- : CodeBlock.of(
- "($T) $L", rawTypeName(TypeName.get(requestElementType)), snippet);
- })
- .collect(toList()));
- // we use the type from the key to ensure we get the right generics
- // TODO(gak): use <>?
+ // TODO(dpb): align this with the contents of InlineMethods.create
+ CodeBlock arguments =
+ provisionBinding
+ .dependencies()
+ .stream()
+ .map(request -> dependencyArgument(request, requestingClass))
+ .collect(toParametersCodeBlock());
ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
switch (method.getKind()) {
case CONSTRUCTOR:
- return CodeBlock.of("new $T($L)", provisionBinding.key().type(), parametersCodeBlock);
+ return CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
case METHOD:
checkState(method.getModifiers().contains(STATIC));
return maybeCheckForNulls(
@@ -104,33 +91,40 @@
"$T.$L($L)",
provisionBinding.bindingTypeElement().get(),
method.getSimpleName(),
- parametersCodeBlock));
+ arguments));
default:
throw new IllegalStateException();
}
}
- private CodeBlock invokeProxyMethod(ClassName requestingClass) {
- return maybeCheckForNulls(
- CodeBlock.of(
- "$T.$L($L)",
- generatedClassNameForBinding(provisionBinding),
- proxyName(asExecutable(provisionBinding.bindingElement().get())),
- provisionBinding
- .explicitDependencies()
- .stream()
- .map(request -> proxyMethodParameter(request, requestingClass))
- .collect(toParametersCodeBlock())));
+ private TypeName constructorTypeName(ClassName requestingClass) {
+ DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
+ TypeName typeName = TypeName.get(type);
+ if (type.getTypeArguments()
+ .stream()
+ .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
+ return typeName;
+ }
+ return rawTypeName(typeName);
}
- private CodeBlock proxyMethodParameter(DependencyRequest dependency, ClassName requestingClass) {
+ private CodeBlock invokeInjectionMethod(ClassName requestingClass) {
+ return injectMembers(
+ maybeCheckForNulls(
+ ProvisionMethod.invoke(
+ provisionBinding,
+ request -> dependencyArgument(request, requestingClass),
+ requestingClass)));
+ }
+
+ private CodeBlock dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
CodeBlock snippet = getDependencySnippet(requestingClass, dependency);
TypeMirror requestElementType = dependency.requestElement().get().asType();
/* If the type is accessible, use the snippet. If only the raw type is accessible, cast it to
* the raw type. If the type is completely inaccessible, the proxy will have an Object method
* parameter, so we can again, just use the snippet. */
return isTypeAccessibleFrom(requestElementType, requestingClass.packageName())
- || !isRawTypeAccessible(requestElementType, requestingClass.packageName())
+ || !isRawTypeAccessible(requestElementType, requestingClass.packageName())
? snippet
: CodeBlock.of("($T) $L", rawTypeName(TypeName.get(requestElementType)), snippet);
}
@@ -143,6 +137,24 @@
: methodCall;
}
+ private CodeBlock injectMembers(CodeBlock instance) {
+ if (provisionBinding.injectionSites().isEmpty()) {
+ return instance;
+ }
+ // Java 7 type inference can't figure out that instance in
+ // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
+ // Parameterized<Object>
+ if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
+ TypeName keyType = TypeName.get(provisionBinding.key().type());
+ instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
+ }
+
+ return CodeBlock.of(
+ "$N($L)",
+ hasBindingExpressions.getMembersInjectionMethod(provisionBinding.key()),
+ instance);
+ }
+
private CodeBlock getDependencySnippet(ClassName requestingClass, DependencyRequest request) {
return hasBindingExpressions
.getBindingExpression(request.bindingKey())
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index 42fd910..ded5f6f 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -44,8 +44,10 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeVariableName;
@@ -164,6 +166,19 @@
}
/**
+ * Returns a mapping of {@link DependencyRequest}s to {@link CodeBlock}s that {@linkplain
+ * #frameworkTypeUsageStatement(CodeBlock, DependencyRequest.Kind) use them}.
+ */
+ static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
+ ImmutableSet<DependencyRequest> dependencies, ImmutableMap<BindingKey, FieldSpec> fields) {
+ return Maps.toMap(
+ dependencies,
+ dep ->
+ frameworkTypeUsageStatement(
+ CodeBlock.of("$N", fields.get(dep.bindingKey())), dep.kind()));
+ }
+
+ /**
* Returns the generated factory or members injector name for a binding.
*/
static ClassName generatedClassNameForBinding(Binding binding) {
diff --git a/java/dagger/internal/codegen/UniqueNameSet.java b/java/dagger/internal/codegen/UniqueNameSet.java
index 27a275c..11c48b3 100644
--- a/java/dagger/internal/codegen/UniqueNameSet.java
+++ b/java/dagger/internal/codegen/UniqueNameSet.java
@@ -19,9 +19,7 @@
import java.util.HashSet;
import java.util.Set;
-/**
- * A collector for names to be used in the same namespace that should not conflict.
- */
+/** A collector for names to be used in the same namespace that should not conflict. */
final class UniqueNameSet {
private final Set<String> uniqueNames = new HashSet<>();
@@ -36,4 +34,12 @@
}
return name;
}
+
+ /**
+ * Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
+ * already present in the set.
+ */
+ void claim(CharSequence name) {
+ uniqueNames.add(name.toString());
+ }
}