[Ahead-of-time Subcomponents] Introduce ModifiableBindingType to classify
modifiable bindings across subcomponent implementations. Recast missing
bindings and generated instance bindings as "modifiable bindings". Add some
additional support for generated instance bindings.
RELNOTES=n/a
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=209645096
diff --git a/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java b/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
new file mode 100644
index 0000000..8d1efdb
--- /dev/null
+++ b/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 dagger.internal.codegen.RequestKinds.requestTypeName;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+
+/**
+ * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be
+ * satisfied when generating the abstract base class implementation of a subcomponent. The
+ * (unimplemented) method is added to the {@link GeneratedComponentModel} when the dependency
+ * expression is requested. The method is overridden when generating the implementation of an
+ * ancestor component.
+ */
+abstract class AbstractMethodModifiableBindingExpression extends BindingExpression {
+ private final GeneratedComponentModel generatedComponentModel;
+ private final ModifiableBindingType modifiableBindingType;
+ private final Key key;
+ private final RequestKind kind;
+ private String methodName;
+
+ AbstractMethodModifiableBindingExpression(
+ GeneratedComponentModel generatedComponentModel,
+ ModifiableBindingType modifiableBindingType,
+ Key key,
+ RequestKind kind) {
+ this.generatedComponentModel = generatedComponentModel;
+ this.modifiableBindingType = modifiableBindingType;
+ this.key = key;
+ this.kind = kind;
+ }
+
+ @Override
+ final Expression getDependencyExpression(ClassName requestingClass) {
+ addUnimplementedMethod();
+ return Expression.create(key.type(), CodeBlock.of("$L()", methodName));
+ }
+
+ private void addUnimplementedMethod() {
+ if (methodName == null) {
+ // Only add the method once in case of repeated references to the missing binding.
+ methodName = chooseMethodName();
+ generatedComponentModel.addModifiableBindingMethod(
+ modifiableBindingType,
+ key,
+ kind,
+ MethodSpec.methodBuilder(methodName)
+ .addModifiers(PUBLIC, ABSTRACT)
+ .returns(requestTypeName(kind, TypeName.get(key.type())))
+ .build());
+ }
+ }
+
+ /** Returns a unique 'getter' method name for the current component. */
+ abstract String chooseMethodName();
+}
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index f2e4139..ee0887e 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -227,6 +227,7 @@
java_library(
name = "writing",
srcs = [
+ "AbstractMethodModifiableBindingExpression.java",
"AnnotationCreatorGenerator.java",
"BindingExpression.java",
"BindingMethodImplementation.java",
@@ -266,7 +267,8 @@
"MembersInjectorProviderCreationExpression.java",
"MethodBindingExpression.java",
"MissingBindingExpression.java",
- "MissingBindingMethods.java",
+ "ModifiableBindingMethods.java",
+ "ModifiableBindingType.java",
"MonitoringModuleGenerator.java",
"MonitoringModuleProcessingStep.java",
"OptionalBindingExpression.java",
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index 50a11f8..f9c081f 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -41,7 +41,7 @@
import com.squareup.javapoet.MethodSpec;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
import dagger.model.DependencyRequest;
import dagger.model.Key;
import dagger.model.RequestKind;
@@ -251,25 +251,23 @@
}
/**
- * Returns the implementation of a method encapsulating a missing binding in a supertype
+ * Returns the implementation of a method encapsulating a modifiable binding in a supertype
* implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot
- * be satisfied by the current binding graph. This is only relevant for ahead-of-time
+ * or should not be modified by the current binding graph. This is only relevant for ahead-of-time
* subcomponents.
*/
- Optional<MethodSpec> getMissingBindingMethodImplementation(MissingBindingMethod missingBinding) {
- // TODO(b/72748365): investigate beder@'s comment about having intermediate component ancestors
- // satisfy missing bindings of their children with their own missing binding methods so that
- // we can minimize the cases where we need to reach into doubly-nested descendant component
- // implementations
- if (resolvableBinding(missingBinding.key(), missingBinding.kind())) {
+ Optional<MethodSpec> getModifiableBindingMethod(ModifiableBindingMethod modifiableBindingMethod) {
+ if (shouldOverrideModifiableBindingMethod(modifiableBindingMethod)) {
Expression bindingExpression =
getDependencyExpression(
- missingBinding.key(), missingBinding.kind(), generatedComponentModel.name());
- MethodSpec unimplementedMethod = missingBinding.unimplementedMethod();
+ modifiableBindingMethod.key(),
+ modifiableBindingMethod.kind(),
+ generatedComponentModel.name());
+ MethodSpec baseMethod = modifiableBindingMethod.baseMethod();
return Optional.of(
- MethodSpec.methodBuilder(unimplementedMethod.name)
+ MethodSpec.methodBuilder(baseMethod.name)
.addModifiers(PUBLIC)
- .returns(unimplementedMethod.returnType)
+ .returns(baseMethod.returnType)
.addAnnotation(Override.class)
.addStatement("return $L", bindingExpression.codeBlock())
.build());
@@ -282,12 +280,13 @@
return expressions.get(key, requestKind);
}
Optional<BindingExpression> expression = Optional.empty();
- if (resolvedInThisComponent(key, requestKind)) {
+ ModifiableBindingType modifiableBindingType = getModifiableBindingType(key, requestKind);
+ if (modifiableBindingType.isModifiable()) {
+ expression =
+ Optional.of(createModifiableBindingExpression(modifiableBindingType, key, requestKind));
+ } else if (resolvedInThisComponent(key, requestKind)) {
ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
expression = Optional.of(createBindingExpression(resolvedBindings, requestKind));
- } else if (!resolvableBinding(key, requestKind) && generatedComponentModel.isAbstract()) {
- expression =
- Optional.of(new MissingBindingExpression(generatedComponentModel, key, requestKind));
}
if (expression.isPresent()) {
expressions.put(key, requestKind, expression.get());
@@ -300,9 +299,6 @@
/** Creates a binding expression. */
private BindingExpression createBindingExpression(
ResolvedBindings resolvedBindings, RequestKind requestKind) {
- if (generatedInstanceForAbstractSubcomponent(resolvedBindings)) {
- return new GeneratedInstanceBindingExpression(resolvedBindings);
- }
switch (resolvedBindings.bindingType()) {
case MEMBERS_INJECTION:
checkArgument(requestKind.equals(RequestKind.MEMBERS_INJECTION));
@@ -325,13 +321,81 @@
}
/**
- * Returns true if the binding exposes an instance of a generated type, but no concrete
- * implementation of that type is available.
+ * Creates a binding expression for a binding that may be modified across implementations of a
+ * subcomponent. This is only relevant for ahead-of-time subcomponents.
*/
- private boolean generatedInstanceForAbstractSubcomponent(ResolvedBindings resolvedBindings) {
- return !resolvedBindings.contributionBindings().isEmpty()
- && resolvedBindings.contributionBinding().requiresGeneratedInstance()
- && generatedComponentModel.isAbstract();
+ private BindingExpression createModifiableBindingExpression(
+ ModifiableBindingType type, Key key, RequestKind requestKind) {
+ switch (type) {
+ case GENERATED_INSTANCE:
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+ return new GeneratedInstanceBindingExpression(
+ generatedComponentModel, resolvedBindings, requestKind);
+ case MISSING:
+ return new MissingBindingExpression(generatedComponentModel, key, requestKind);
+ default:
+ throw new IllegalStateException(
+ String.format(
+ "Building binding expression for unsupported ModifiableBindingType [%s].", type));
+ }
+ }
+
+ /**
+ * The reason why a binding may need to be modified across implementations of a subcomponent, if
+ * at all. This is only relevant for ahead-of-time subcomponents.
+ */
+ private ModifiableBindingType getModifiableBindingType(Key key, RequestKind requestKind) {
+ if (!compilerOptions.aheadOfTimeSubcomponents()) {
+ return ModifiableBindingType.NONE;
+ }
+
+ // When generating a final (concrete) implementation of a (sub)component the binding is no
+ // longer considered modifiable. It cannot be further modified by a subclass implementation.
+ if (!generatedComponentModel.isAbstract()) {
+ return ModifiableBindingType.NONE;
+ }
+
+ if (resolvedInThisComponent(key, requestKind)) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+ if (resolvedBindings.contributionBindings().isEmpty()) {
+ // TODO(ronshapiro): Confirm whether a resolved binding must have a single contribution
+ // binding.
+ return ModifiableBindingType.NONE;
+ }
+
+ ContributionBinding binding = resolvedBindings.contributionBinding();
+ if (binding.requiresGeneratedInstance()) {
+ return ModifiableBindingType.GENERATED_INSTANCE;
+ }
+ } else if (!resolvableBinding(key, requestKind)) {
+ return ModifiableBindingType.MISSING;
+ }
+
+ // TODO(b/72748365): Add support for remaining types.
+ return ModifiableBindingType.NONE;
+ }
+
+ /**
+ * Returns true if the current binding graph can, and should, modify a binding by overriding a
+ * modfiable binding method. This is only relevant for ahead-of-time subcomponents.
+ */
+ private boolean shouldOverrideModifiableBindingMethod(
+ ModifiableBindingMethod modifiableBindingMethod) {
+ switch (modifiableBindingMethod.type()) {
+ case GENERATED_INSTANCE:
+ return !generatedComponentModel.isAbstract();
+ case MISSING:
+ // TODO(b/72748365): investigate beder@'s comment about having intermediate component
+ // ancestors satisfy missing bindings of their children with their own missing binding
+ // methods so that we can minimize the cases where we need to reach into doubly-nested
+ // descendant component implementations
+ return resolvableBinding(modifiableBindingMethod.key(), modifiableBindingMethod.kind());
+ default:
+ throw new IllegalStateException(
+ String.format(
+ "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].",
+ modifiableBindingMethod.type()));
+ }
}
/**
diff --git a/java/dagger/internal/codegen/ComponentModelBuilder.java b/java/dagger/internal/codegen/ComponentModelBuilder.java
index 4716b3d..b38df3e 100644
--- a/java/dagger/internal/codegen/ComponentModelBuilder.java
+++ b/java/dagger/internal/codegen/ComponentModelBuilder.java
@@ -48,7 +48,7 @@
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.ExecutableElement;
@@ -589,15 +589,15 @@
// Since we're overriding a subcomponent implementation we add to its implementation given
// an expanded binding graph.
- // Implement missing binding methods.
- for (MissingBindingMethod missingBindingMethod :
- generatedComponentModel.getMissingBindingMethods()) {
+ // Override modifiable binding methods.
+ for (ModifiableBindingMethod modifiableBindingMethod :
+ generatedComponentModel.getModifiableBindingMethods()) {
bindingExpressions
- .getMissingBindingMethodImplementation(missingBindingMethod)
+ .getModifiableBindingMethod(modifiableBindingMethod)
.ifPresent(
method ->
- generatedComponentModel.addImplementedMissingBindingMethod(
- missingBindingMethod, method));
+ generatedComponentModel.addImplementedModifiableBindingMethod(
+ modifiableBindingMethod, method));
}
} else {
super.addInterfaceMethods();
diff --git a/java/dagger/internal/codegen/GeneratedComponentModel.java b/java/dagger/internal/codegen/GeneratedComponentModel.java
index d0dac71..c50ad9c 100644
--- a/java/dagger/internal/codegen/GeneratedComponentModel.java
+++ b/java/dagger/internal/codegen/GeneratedComponentModel.java
@@ -16,6 +16,9 @@
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.CaseFormat.UPPER_UNDERSCORE;
import static com.google.common.base.Preconditions.checkState;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
@@ -34,7 +37,7 @@
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.ReferenceReleasingProviderManager;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
import dagger.model.Key;
import dagger.model.RequestKind;
import java.util.ArrayList;
@@ -101,10 +104,12 @@
ABSENT_OPTIONAL_METHOD,
/**
- * A method encapsulating a missing binding to be overridden by a subclass when generating a
- * component ancestor. Only relevant for ahead-of-time subcomponents.
+ * A method that encapsulates a modifiable binding. A binding is modifiable if it can change
+ * across implementations of a subcomponent. This is only relevant for ahead-of-time
+ * subcomponents.
*/
- MISSING_BINDING_METHOD
+ MODIFIABLE_BINDING_METHOD,
+ ;
}
/** A type of nested class that this component model can generate. */
@@ -138,7 +143,7 @@
private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
private final List<Supplier<TypeSpec>> switchingProviderSupplier = new ArrayList<>();
- private final MissingBindingMethods missingBindingMethods = new MissingBindingMethods();
+ private final ModifiableBindingMethods modifiableBindingMethods = new ModifiableBindingMethods();
private GeneratedComponentModel(
ClassName name,
@@ -252,16 +257,22 @@
methodSpecsMap.putAll(methodKind, methodSpecs);
}
- /** Adds the given (abstract) method representing an encapsulated missing binding. */
- void addUnimplementedMissingBindingMethod(Key key, RequestKind kind, MethodSpec methodSpec) {
- missingBindingMethods.addUnimplementedMethod(key, kind, methodSpec);
- methodSpecsMap.put(MethodSpecKind.MISSING_BINDING_METHOD, methodSpec);
+ /**
+ * Adds the given method to the component. In this case, the method represents an encapsulation of
+ * a modifiable binding between implementations of a subcomponent. This is only relevant for
+ * ahead-of-time subcomponents.
+ */
+ void addModifiableBindingMethod(
+ ModifiableBindingType type, Key key, RequestKind kind, MethodSpec methodSpec) {
+ modifiableBindingMethods.addMethod(type, key, kind, methodSpec);
+ methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
}
- /** Adds the implementation for the given {@link MissingBindingMethod}. */
- void addImplementedMissingBindingMethod(MissingBindingMethod method, MethodSpec methodSpec) {
- missingBindingMethods.methodImplemented(method);
- methodSpecsMap.put(MethodSpecKind.MISSING_BINDING_METHOD, methodSpec);
+ /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
+ void addImplementedModifiableBindingMethod(
+ ModifiableBindingMethod method, MethodSpec methodSpec) {
+ modifiableBindingMethods.methodImplemented(method);
+ methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
}
/** Adds the given type to the component. */
@@ -301,6 +312,21 @@
return componentMethodNames.getUniqueName(name);
}
+ /**
+ * Returns a new, unique method name for a "getter" method exposing this binding and binding kind
+ * for this component.
+ */
+ String getUniqueGetterMethodName(ContributionBinding binding, RequestKind requestKind) {
+ // TODO(user): Use a better name for @MapKey binding instances.
+ // TODO(user): Include the binding method as part of the method name.
+ String bindingName = LOWER_CAMEL.to(UPPER_CAMEL, BindingVariableNamer.name(binding));
+ String kindName =
+ requestKind.equals(RequestKind.INSTANCE)
+ ? ""
+ : UPPER_UNDERSCORE.to(UPPER_CAMEL, requestKind.name());
+ return getUniqueMethodName("get" + bindingName + kindName);
+ }
+
/** Claims a new method name for the component. Does nothing if method name already exists. */
void claimMethodName(Name name) {
componentMethodNames.claim(name);
@@ -312,22 +338,21 @@
}
/**
- * Returns the unimplemented {@link MissingBindingMethod}s for this subcomponent implementation
- * and its superclasses.
+ * Returns the {@link ModifiableBindingMethod}s for this subcomponent implementation and its
+ * superclasses.
*/
- ImmutableList<MissingBindingMethod> getMissingBindingMethods() {
- ImmutableList.Builder<MissingBindingMethod> missingBindingMethodsBuilder =
+ ImmutableList<ModifiableBindingMethod> getModifiableBindingMethods() {
+ ImmutableList.Builder<ModifiableBindingMethod> modifiableBindingMethodsBuilder =
ImmutableList.builder();
if (supermodel.isPresent()) {
- ImmutableList<MissingBindingMethod> bindingsUnsatisfiedBySuperclasses =
- supermodel.get().getMissingBindingMethods();
- bindingsUnsatisfiedBySuperclasses
- .stream()
- .filter(method -> !missingBindingMethods.isMethodImplemented(method))
- .forEach(missingBindingMethodsBuilder::add);
+ ImmutableList<ModifiableBindingMethod> superclassModifiableBindingMethods =
+ supermodel.get().getModifiableBindingMethods();
+ superclassModifiableBindingMethods.stream()
+ .filter(method -> !modifiableBindingMethods.isFinalized(method))
+ .forEach(modifiableBindingMethodsBuilder::add);
}
- missingBindingMethodsBuilder.addAll(missingBindingMethods.getUnimplementedMethods());
- return missingBindingMethodsBuilder.build();
+ modifiableBindingMethodsBuilder.addAll(modifiableBindingMethods.getMethods());
+ return modifiableBindingMethodsBuilder.build();
}
/** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
diff --git a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
index 1121544..dffa052 100644
--- a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
@@ -16,28 +16,36 @@
package dagger.internal.codegen;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.model.Key;
+import dagger.model.RequestKind;
/**
- * A {@link BindingExpression} that invokes a method that encapsulates a binding that requires an
- * instance of a generated type. This expression is used in abstract implementations of a
- * subcomponent when there are no concrete definitions of generated types available. The
- * (unimplemented) method is added to the {@code GeneratedComponentModel} when this dependency
- * expression is requested. The method is overridden when generating the concrete implementation of
- * an ancestor component.
+ * An {@link AbstractMethodModifiableBindingExpression} for a binding that requires an instance of a
+ * generated type. This expression is used in abstract implementations of a subcomponent when there
+ * are no concrete definitions of generated types available. The (unimplemented) method is added to
+ * the {@code GeneratedComponentModel} when this dependency expression is requested. The method is
+ * overridden when generating the concrete implementation of an ancestor component.
*/
-final class GeneratedInstanceBindingExpression extends BindingExpression {
- private final Key key;
+final class GeneratedInstanceBindingExpression extends AbstractMethodModifiableBindingExpression {
+ private final GeneratedComponentModel generatedComponentModel;
+ private final ContributionBinding binding;
+ private final RequestKind requestKind;
- GeneratedInstanceBindingExpression(ResolvedBindings resolvedBindings) {
- this.key = resolvedBindings.key();
+ GeneratedInstanceBindingExpression(
+ GeneratedComponentModel generatedComponentModel,
+ ResolvedBindings resolvedBindings,
+ RequestKind requestKind) {
+ super(
+ generatedComponentModel,
+ ModifiableBindingType.GENERATED_INSTANCE,
+ resolvedBindings.key(),
+ requestKind);
+ this.generatedComponentModel = generatedComponentModel;
+ this.binding = resolvedBindings.contributionBinding();
+ this.requestKind = requestKind;
}
@Override
- final Expression getDependencyExpression(ClassName requestingClass) {
- // TODO(b/72748365): Implement method encapsulating binding to invoke in this expression.
- return Expression.create(key.type(), CodeBlock.of("null"));
+ String chooseMethodName() {
+ return generatedComponentModel.getUniqueGetterMethodName(binding, requestKind);
}
}
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
index 65eb5d9..dbc95e9 100644
--- a/java/dagger/internal/codegen/MissingBindingExpression.java
+++ b/java/dagger/internal/codegen/MissingBindingExpression.java
@@ -19,59 +19,33 @@
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static dagger.internal.codegen.RequestKinds.requestTypeName;
import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PUBLIC;
import com.google.auto.common.MoreTypes;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
import dagger.model.Key;
import dagger.model.RequestKind;
/**
- * A {@link BindingExpression} that invokes a method that encapsulates a binding that is missing
- * when generating the abstract base class implementation of a subcomponent. The (unimplemented)
- * method is added to the {@link GeneratedComponentModel} when the dependency expression is
- * requested. The method is overridden when generating the implementation of an ancestor component.
+ * A {@link AbstractMethodModifiableBindingExpression} for a binding that is missing when generating
+ * the abstract base class implementation of a subcomponent. The (unimplemented) method is added to
+ * the {@link GeneratedComponentModel} when the dependency expression is requested. The method is
+ * overridden when generating the implementation of an ancestor component.
*/
-final class MissingBindingExpression extends BindingExpression {
+final class MissingBindingExpression extends AbstractMethodModifiableBindingExpression {
private final GeneratedComponentModel generatedComponentModel;
private final Key key;
private final RequestKind kind;
- private String methodName;
MissingBindingExpression(
GeneratedComponentModel generatedComponentModel, Key key, RequestKind kind) {
+ super(generatedComponentModel, ModifiableBindingType.MISSING, key, kind);
this.generatedComponentModel = generatedComponentModel;
this.key = key;
this.kind = kind;
}
@Override
- final Expression getDependencyExpression(ClassName requestingClass) {
- addUnimplementedMethod();
- return Expression.create(key.type(), CodeBlock.of("$L()", methodName));
- }
-
- private void addUnimplementedMethod() {
- if (methodName == null) {
- // Only add the method once in case of repeated references to the missing binding.
- methodName = chooseMethodName();
- generatedComponentModel.addUnimplementedMissingBindingMethod(
- key,
- kind,
- MethodSpec.methodBuilder(methodName)
- .addModifiers(PUBLIC, ABSTRACT)
- .returns(requestTypeName(kind, TypeName.get(key.type())))
- .build());
- }
- }
-
- private String chooseMethodName() {
+ String chooseMethodName() {
return generatedComponentModel.getUniqueMethodName(
"get"
+ LOWER_CAMEL.to(UPPER_CAMEL, simpleVariableName(MoreTypes.asTypeElement(key.type())))
diff --git a/java/dagger/internal/codegen/MissingBindingMethods.java b/java/dagger/internal/codegen/MissingBindingMethods.java
deleted file mode 100644
index 473166a..0000000
--- a/java/dagger/internal/codegen/MissingBindingMethods.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 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.Preconditions.checkState;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.MethodSpec;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A registry for those methods which each wrap a binding that is unsatisfiable by a subcomponent in
- * isolation, but can be satisfied by an ancestor component. This is useful when generating
- * ahead-of-time subcomponents: An instance of this class is associated with a single subcomponent
- * implementation. We generate an implementation of a given subcomponent once for each of it's
- * ancestor components and for any one implementation the {@link MissingBindingMethod}s of it's
- * superclasses tell us what missing bindings have yet to be satisfied so we can attempt to satisfy
- * them.
- */
-final class MissingBindingMethods {
- private final Map<KeyAndKind, MissingBindingMethod> missingBindingMethods = Maps.newHashMap();
- private final Set<KeyAndKind> implementedMissingBindingMethods = Sets.newHashSet();
-
- /** Record an unimplemented method encapsulating a missing binding. */
- void addUnimplementedMethod(Key key, RequestKind kind, MethodSpec unimplementedMethod) {
- KeyAndKind keyAndKind = KeyAndKind.create(key, kind);
- checkState(
- !implementedMissingBindingMethods.contains(keyAndKind),
- "Adding an missing binding method for a method marked as implemented for the current "
- + "subcomponent implementation. The binding is for a %s-%s.",
- key,
- kind);
- missingBindingMethods.put(
- keyAndKind, MissingBindingMethod.create(key, kind, unimplementedMethod));
- }
-
- /** Returns all unimplemented {@link MissingBindingMethod}s */
- ImmutableList<MissingBindingMethod> getUnimplementedMethods() {
- // We will never register a binding as missing and also as implemented for the same instance of
- // MissingBindingMethods, so there's no need to filter missingBindingMethods.
- return ImmutableList.copyOf(missingBindingMethods.values());
- }
-
- /** Mark the {@link MissingBindingMethod} as having been implemented. */
- void methodImplemented(MissingBindingMethod method) {
- KeyAndKind keyAndKind = KeyAndKind.create(method.key(), method.kind());
- checkState(
- !missingBindingMethods.containsKey(keyAndKind),
- "Indicating a missing binding method as implemented when it was registered as missing for "
- + "the current subcomponent implementation. The binding is for a %s-%s.",
- method.key(),
- method.kind());
- implementedMissingBindingMethods.add(keyAndKind);
- }
-
- /** Whether a given binding has been marked as implemented. */
- boolean isMethodImplemented(MissingBindingMethod method) {
- return implementedMissingBindingMethods.contains(
- KeyAndKind.create(method.key(), method.kind()));
- }
-
- @AutoValue
- abstract static class MissingBindingMethod {
- private static MissingBindingMethod create(
- Key key, RequestKind kind, MethodSpec unimplementedMethod) {
- return new AutoValue_MissingBindingMethods_MissingBindingMethod(
- key, kind, unimplementedMethod);
- }
-
- abstract Key key();
-
- abstract RequestKind kind();
-
- abstract MethodSpec unimplementedMethod();
- }
-
- @AutoValue
- abstract static class KeyAndKind {
- private static KeyAndKind create(Key key, RequestKind kind) {
- return new AutoValue_MissingBindingMethods_KeyAndKind(key, kind);
- }
-
- abstract Key key();
-
- abstract RequestKind kind();
- }
-}
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
new file mode 100644
index 0000000..766031b
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingMethods.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 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.Preconditions.checkState;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.MethodSpec;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A registry for those methods which each wrap a binding whose definition may be modified across
+ * each class in the class hierarchy implementing a subcomponent. Subcomponent implementations are
+ * spread across a class hierarchy when generating ahead-of-time subcomponents. There is one
+ * subcomponent implementation class for each of the subcomponent's ancestor components. An instance
+ * of {@link ModifiableBindingMethod} is associated with a single class in this hierarchy. For a
+ * given subcomponent implementation class we can use the {@link ModifiableBindingMethod}s of its
+ * superclasses to know what binding methods to attempt to modify.
+ */
+final class ModifiableBindingMethods {
+ private final Map<KeyAndKind, ModifiableBindingMethod> methods = Maps.newHashMap();
+ private final Set<KeyAndKind> finalizedMethods = Sets.newHashSet();
+
+ /** Register a method encapsulating a modifiable binding. */
+ void addMethod(
+ ModifiableBindingType type, Key key, RequestKind kind, MethodSpec unimplementedMethod) {
+ KeyAndKind keyAndKind = KeyAndKind.create(key, kind);
+ checkState(
+ !finalizedMethods.contains(keyAndKind),
+ "Adding a modifiable binding method for a binding that has been marked as finalized for "
+ + "the current subcomponent implementation. The binding is for a %s-%s of type %s.",
+ key,
+ kind,
+ type);
+ methods.put(keyAndKind, ModifiableBindingMethod.create(type, key, kind, unimplementedMethod));
+ }
+
+ /** Returns all {@link ModifiableBindingMethod}s. */
+ ImmutableList<ModifiableBindingMethod> getMethods() {
+ // We will never add a modifiable binding method and mark it as having been finalized in the
+ // same instance of ModifiableBindingMethods, so there's no need to filter `methods` by
+ // `finalizedMethods`.
+ return ImmutableList.copyOf(methods.values());
+ }
+
+ /**
+ * Mark the {@link ModifiableBindingMethod} as having been implemented, thus modifying the
+ * binding. For those bindings that are finalized when modified, mark the binding as finalized,
+ * meaning it should no longer be modified.
+ */
+ void methodImplemented(ModifiableBindingMethod method) {
+ if (method.type().finalizedOnModification()) {
+ KeyAndKind keyAndKind = KeyAndKind.create(method.key(), method.kind());
+ checkState(
+ !methods.containsKey(keyAndKind),
+ "Indicating a modifiable binding method is finalized when it was registered as "
+ + "modifiable for the current subcomponent implementation. The binding is for a "
+ + "%s-%s of type %s.",
+ method.key(),
+ method.kind(),
+ method.type());
+ finalizedMethods.add(keyAndKind);
+ }
+ }
+
+ /** Whether a given binding has been marked as finalized. */
+ boolean isFinalized(ModifiableBindingMethod method) {
+ return finalizedMethods.contains(KeyAndKind.create(method.key(), method.kind()));
+ }
+
+ @AutoValue
+ abstract static class ModifiableBindingMethod {
+ private static ModifiableBindingMethod create(
+ ModifiableBindingType type, Key key, RequestKind kind, MethodSpec unimplementedMethod) {
+ return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
+ type, key, kind, unimplementedMethod);
+ }
+
+ abstract ModifiableBindingType type();
+
+ abstract Key key();
+
+ abstract RequestKind kind();
+
+ abstract MethodSpec baseMethod();
+ }
+
+ @AutoValue
+ abstract static class KeyAndKind {
+ private static KeyAndKind create(Key key, RequestKind kind) {
+ return new AutoValue_ModifiableBindingMethods_KeyAndKind(key, kind);
+ }
+
+ abstract Key key();
+
+ abstract RequestKind kind();
+ }
+}
diff --git a/java/dagger/internal/codegen/ModifiableBindingType.java b/java/dagger/internal/codegen/ModifiableBindingType.java
new file mode 100644
index 0000000..d2c5f6f
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingType.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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 com.google.common.collect.ImmutableSet;
+
+/**
+ * A label for a binding indicating whether, and how, it may be redefined across implementations of
+ * a subcomponent.
+ *
+ * <p>A subcomponent has multiple implementations only when generating ahead-of-time subcomponents.
+ * Specifically, each subcomponent type in a component hierarchy is implemented as an abstract
+ * class, and descendent components are implemented as abstract inner classes. A consequence of this
+ * is that a given subcomponent has an implementation for each ancestor component. Each
+ * implementation represents a different sub-binding-graph of the full subcomponent. A binding is
+ * modifiable if it's definition may change depending on the characteristics of its ancestor
+ * components.
+ */
+enum ModifiableBindingType {
+ /** A binding that is not modifiable */
+ NONE,
+
+ /**
+ * A binding that is missing when generating the abstract base class implementation of a
+ * subcomponent.
+ */
+ MISSING,
+
+ /**
+ * A binding that requires an instance of a generated type. These binding are modifiable in the
+ * sense that they are encapsulated in a method when they are first required, possibly in an
+ * abstract implementation of a subcomponent, where, in general, no concrete instances of
+ * generated types are available, and the method is satisfied in a final concrete implementation.
+ */
+ GENERATED_INSTANCE,
+
+ /**
+ * Multibindings may have contributions come from any ancestor component. Therefore, each
+ * implementation of a subcomponent may have newly available contributions, and so the binding
+ * method is reimplemented with each subcomponent implementation.
+ */
+ MULTIBINDING,
+
+ /**
+ * A Optional binding that may be empty when looking at a partial binding graph, but bound to a
+ * value when considering the complete binding graph, thus modifiable across subcomponent
+ * implementations.
+ */
+ OPTIONAL,
+
+ /**
+ * If a binding is defined according to an {@code @Inject} annotated constructor on the object it
+ * is valid for that binding to be redefined a single time by an {@code @Provides} annotated
+ * module method. It is possible that the {@code @Provides} binding isn't available in a partial
+ * binding graph, but becomes available when considering a more complete binding graph, therefore
+ * such bindings are modifiable across subcomponent implementations.
+ */
+ INJECTION,
+ ;
+
+ private static final ImmutableSet<ModifiableBindingType> TYPES_FINALIZED_ON_MODIFICATION =
+ ImmutableSet.of(MISSING, GENERATED_INSTANCE, OPTIONAL, INJECTION);
+
+ boolean isModifiable() {
+ return !equals(NONE);
+ }
+
+ /**
+ * Returns true if the modifiable binding should not be further modified once it's base
+ * implementation has been overridden. For example, a missing or optional binding may only be
+ * satisfied once in a subcomponent implementation class hierarchy, but a multibinding may be
+ * modified with every implementation of a subcomponent.
+ */
+ boolean finalizedOnModification() {
+ return TYPES_FINALIZED_ON_MODIFICATION.contains(this);
+ }
+}
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
index e19f9b8..bd92437 100644
--- a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
@@ -16,9 +16,6 @@
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.CaseFormat.UPPER_UNDERSCORE;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
@@ -56,7 +53,7 @@
protected void addMethod() {
if (methodName == null) {
// Have to set methodName field before implementing the method in order to handle recursion.
- methodName = chooseMethodName();
+ methodName = generatedComponentModel.getUniqueGetterMethodName(binding, requestKind);
// TODO(user): Fix the order that these generated methods are written to the component.
generatedComponentModel.addMethod(
PRIVATE_METHOD,
@@ -73,23 +70,4 @@
checkState(methodName != null, "addMethod() must be called before methodName()");
return methodName;
}
-
- private String chooseMethodName() {
- // TODO(user): Use a better name for @MapKey binding instances.
- // TODO(user): Include the binding method as part of the method name.
- return generatedComponentModel.getUniqueMethodName(
- "get" + bindingName() + dependencyKindName());
- }
-
- /** Returns the canonical method name suffix for the binding. */
- private String bindingName() {
- return LOWER_CAMEL.to(UPPER_CAMEL, BindingVariableNamer.name(binding));
- }
-
- /** Returns a canonical method name suffix for the request kind. */
- private String dependencyKindName() {
- return requestKind.equals(RequestKind.INSTANCE)
- ? ""
- : UPPER_UNDERSCORE.to(UPPER_CAMEL, requestKind.name());
- }
}