Generate binding expressions on demand.

Also moves BindingExpression.Factory into ComponentBindingExpressions and makes it private.

RELNOTES=n/a.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=177836336
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index f54bbe6..0bb4bf7 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -16,18 +16,28 @@
 
 package dagger.internal.codegen;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_SET;
+import static dagger.internal.codegen.MemberSelect.staticMemberSelect;
+import static javax.lang.model.element.Modifier.PRIVATE;
 
-import com.google.common.collect.FluentIterable;
-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.FieldSpec;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Types;
+import javax.lang.model.util.Elements;
 
 /** A central repository of code expressions used to access any binding available to a component. */
 final class ComponentBindingExpressions {
@@ -36,21 +46,80 @@
   // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
   // parents? If so, maybe make BindingExpression.Factory create it.
 
-  /**
-   * A list of binding expression maps. The first element contains the bindings owned by this
-   * component; the second contains the bindings owned by its parent; and so on.
-   */
-  private final ImmutableList<Map<BindingKey, BindingExpression>> bindingExpressionsMaps;
-  private final Types types;
+  private final Optional<ComponentBindingExpressions> parent;
+  private final BindingGraph graph;
+  private final DaggerTypes types;
+  private final BindingExpressionFactory bindingExpressionFactory;
+  private final Map<BindingKey, BindingExpression> bindingExpressionsMap = new HashMap<>();
 
-  private ComponentBindingExpressions(
-      ImmutableList<Map<BindingKey, BindingExpression>> bindingExpressionsMaps, Types types) {
-    this.bindingExpressionsMaps = bindingExpressionsMaps;
-    this.types = types;
+  ComponentBindingExpressions(
+      BindingGraph graph,
+      GeneratedComponentModel generatedComponentModel,
+      SubcomponentNames subcomponentNames,
+      ComponentRequirementFields componentRequirementFields,
+      OptionalFactories optionalFactories,
+      DaggerTypes types,
+      Elements elements,
+      CompilerOptions compilerOptions) {
+    this(
+        Optional.empty(),
+        graph,
+        generatedComponentModel,
+        subcomponentNames,
+        componentRequirementFields,
+        new ReferenceReleasingManagerFields(graph, generatedComponentModel),
+        optionalFactories,
+        types,
+        elements,
+        compilerOptions);
   }
 
-  ComponentBindingExpressions(Types types) {
-    this(ImmutableList.of(newBindingExpressionMap()), types);
+  private ComponentBindingExpressions(
+      Optional<ComponentBindingExpressions> parent,
+      BindingGraph graph,
+      GeneratedComponentModel generatedComponentModel,
+      SubcomponentNames subcomponentNames,
+      ComponentRequirementFields componentRequirementFields,
+      ReferenceReleasingManagerFields referenceReleasingManagerFields,
+      OptionalFactories optionalFactories,
+      DaggerTypes types,
+      Elements elements,
+      CompilerOptions compilerOptions) {
+    this.parent = parent;
+    this.graph = graph;
+    this.types = types;
+    this.bindingExpressionFactory =
+        new BindingExpressionFactory(
+            graph,
+            generatedComponentModel,
+            subcomponentNames,
+            this,
+            componentRequirementFields,
+            referenceReleasingManagerFields,
+            optionalFactories,
+            types,
+            elements,
+            compilerOptions);
+  }
+
+  /**
+   * Returns a new object representing the bindings available from a child component of this one.
+   */
+  ComponentBindingExpressions forChildComponent(
+      BindingGraph childGraph,
+      GeneratedComponentModel childComponentModel,
+      ComponentRequirementFields childComponentRequirementFields) {
+    return new ComponentBindingExpressions(
+        Optional.of(this),
+        childGraph,
+        childComponentModel,
+        bindingExpressionFactory.subcomponentNames,
+        childComponentRequirementFields,
+        bindingExpressionFactory.referenceReleasingManagerFields,
+        bindingExpressionFactory.optionalFactories,
+        bindingExpressionFactory.types,
+        bindingExpressionFactory.elements,
+        bindingExpressionFactory.compilerOptions);
   }
 
   /**
@@ -132,32 +201,286 @@
   }
 
   private BindingExpression getBindingExpression(BindingKey bindingKey) {
-    for (Map<BindingKey, BindingExpression> bindingExpressionsMap : bindingExpressionsMaps) {
-      BindingExpression expression = bindingExpressionsMap.get(bindingKey);
-      if (expression != null) {
-        return expression;
+    if (graph.resolvedBindings().containsKey(bindingKey)
+        && !graph.resolvedBindings().get(bindingKey).ownedBindings().isEmpty()) {
+      return bindingExpressionsMap.computeIfAbsent(bindingKey, this::createBindingExpression);
+    }
+    return parent
+        .map(p -> p.getBindingExpression(bindingKey))
+        .orElseThrow(
+            () -> new IllegalStateException("no binding expression found for " + bindingKey));
+  }
+
+  private BindingExpression createBindingExpression(BindingKey bindingKey) {
+    return bindingExpressionFactory.create(graph.resolvedBindings().get(bindingKey));
+  }
+
+  // This is only made visibile to keep the correct order of methods in the generated component.
+  // TODO(user): remove this method after removing the dependency from AbstractComponentWriter.
+  MembersInjectionMethods membersInjectionMethods() {
+    return bindingExpressionFactory.membersInjectionMethods;
+  }
+
+  /** Factory for building a {@link BindingExpression}. */
+  private static final class BindingExpressionFactory {
+    // TODO(user): Consider using PrivateMethodBindingExpression for other/all BEs?
+    private static final ImmutableSet<ContributionBinding.Kind> PRIVATE_METHOD_KINDS =
+        ImmutableSet.copyOf(
+            EnumSet.of(SYNTHETIC_MULTIBOUND_SET, SYNTHETIC_MULTIBOUND_MAP, INJECTION, PROVISION));
+
+    private final BindingGraph graph;
+    private final GeneratedComponentModel generatedComponentModel;
+    private final ComponentBindingExpressions componentBindingExpressions;
+    private final ComponentRequirementFields componentRequirementFields;
+    private final ReferenceReleasingManagerFields referenceReleasingManagerFields;
+    private final SubcomponentNames subcomponentNames;
+    private final OptionalFactories optionalFactories;
+    private final CompilerOptions compilerOptions;
+    private final DaggerTypes types;
+    private final Elements elements;
+    private final MembersInjectionMethods membersInjectionMethods;
+
+    BindingExpressionFactory(
+        BindingGraph graph,
+        GeneratedComponentModel generatedComponentModel,
+        SubcomponentNames subcomponentNames,
+        ComponentBindingExpressions componentBindingExpressions,
+        ComponentRequirementFields componentRequirementFields,
+        ReferenceReleasingManagerFields referenceReleasingManagerFields,
+        OptionalFactories optionalFactories,
+        DaggerTypes types,
+        Elements elements,
+        CompilerOptions compilerOptions) {
+      this.graph = graph;
+      this.generatedComponentModel = checkNotNull(generatedComponentModel);
+      this.subcomponentNames = checkNotNull(subcomponentNames);
+      this.componentBindingExpressions = componentBindingExpressions;
+      this.componentRequirementFields = checkNotNull(componentRequirementFields);
+      this.referenceReleasingManagerFields = checkNotNull(referenceReleasingManagerFields);
+      this.optionalFactories = checkNotNull(optionalFactories);
+      this.types = types;
+      this.elements = checkNotNull(elements);
+      this.compilerOptions = checkNotNull(compilerOptions);
+      this.membersInjectionMethods =
+          new MembersInjectionMethods(
+              generatedComponentModel, componentBindingExpressions, graph, elements, types);
+    }
+
+    /** Creates a binding expression */
+    BindingExpression create(ResolvedBindings resolvedBindings) {
+      return forStaticMethod(resolvedBindings).orElseGet(() -> forField(resolvedBindings));
+    }
+
+    /** Creates a binding expression for a field. */
+    private BindingExpression forField(ResolvedBindings resolvedBindings) {
+      FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.empty());
+      return internalCreate(
+          resolvedBindings,
+          MemberSelect.localField(generatedComponentModel.name(), fieldSpec.name),
+          Optional.of(newFrameworkFieldInitializer(fieldSpec, resolvedBindings)));
+    }
+
+    /** Creates a binding expression for a static method call. */
+    private Optional<BindingExpression> forStaticMethod(ResolvedBindings resolvedBindings) {
+      return staticMemberSelect(resolvedBindings)
+          .map(memberSelect -> internalCreate(resolvedBindings, memberSelect, Optional.empty()));
+    }
+
+    /**
+     * Adds a field representing the resolved bindings, optionally forcing it to use a particular
+     * binding type (instead of the type the resolved bindings would typically use).
+     */
+    private FieldSpec generateFrameworkField(
+        ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) {
+      boolean useRawType =
+          !isTypeAccessibleFrom(
+              resolvedBindings.key().type(), generatedComponentModel.name().packageName());
+
+      FrameworkField contributionBindingField =
+          FrameworkField.forResolvedBindings(resolvedBindings, frameworkClass);
+      FieldSpec.Builder contributionField =
+          FieldSpec.builder(
+              useRawType
+                  ? contributionBindingField.type().rawType
+                  : contributionBindingField.type(),
+              generatedComponentModel.getUniqueFieldName(contributionBindingField.name()));
+      contributionField.addModifiers(PRIVATE);
+      if (useRawType) {
+        contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
+      }
+
+      return contributionField.build();
+    }
+
+    private FrameworkFieldInitializer newFrameworkFieldInitializer(
+        FieldSpec fieldSpec, ResolvedBindings resolvedBindings) {
+      return new FrameworkFieldInitializer(
+          fieldSpec,
+          resolvedBindings,
+          subcomponentNames,
+          generatedComponentModel,
+          componentBindingExpressions,
+          componentRequirementFields,
+          referenceReleasingManagerFields,
+          compilerOptions,
+          graph,
+          optionalFactories);
+    }
+
+    private BindingExpression internalCreate(
+        ResolvedBindings resolvedBindings,
+        MemberSelect memberSelect,
+        Optional<FrameworkFieldInitializer> frameworkFieldInitializer) {
+      FrameworkInstanceBindingExpression frameworkInstanceBindingExpression =
+          FrameworkInstanceBindingExpression.create(
+              resolvedBindings, memberSelect, frameworkFieldInitializer, types, elements);
+
+       switch (resolvedBindings.bindingType()) {
+        case MEMBERS_INJECTION:
+          return new MembersInjectionBindingExpression(
+              frameworkInstanceBindingExpression,
+              generatedComponentModel,
+              membersInjectionMethods);
+        case PROVISION:
+          return provisionBindingExpression(frameworkInstanceBindingExpression);
+        default:
+          return frameworkInstanceBindingExpression;
       }
     }
-    throw new IllegalStateException("no binding expression found for " + bindingKey);
-  }
 
-  /** Adds a binding expression for a single binding owned by this component. */
-  void addBindingExpression(BindingExpression bindingExpression) {
-    bindingExpressionsMaps
-        .get(0)
-        .put(bindingExpression.resolvedBindings().bindingKey(), bindingExpression);
-  }
+    private BindingExpression provisionBindingExpression(
+        FrameworkInstanceBindingExpression frameworkInstanceBindingExpression) {
+      BindingExpression bindingExpression =
+          new ProviderOrProducerBindingExpression(
+              frameworkInstanceBindingExpression,
+              producerFromProviderBindingExpression(frameworkInstanceBindingExpression));
 
-  /**
-   * Returns a new object representing the bindings available from a child component of this one.
-   */
-  ComponentBindingExpressions forChildComponent() {
-    return new ComponentBindingExpressions(
-        FluentIterable.of(newBindingExpressionMap()).append(bindingExpressionsMaps).toList(),
-        types);
-  }
+      BindingExpression inlineBindingExpression =
+          inlineProvisionBindingExpression(bindingExpression);
 
-  private static Map<BindingKey, BindingExpression> newBindingExpressionMap() {
-    return new HashMap<>();
+      ResolvedBindings resolvedBindings = frameworkInstanceBindingExpression.resolvedBindings();
+      if (usePrivateMethod(resolvedBindings.contributionBinding())) {
+        return new PrivateMethodBindingExpression(
+            resolvedBindings,
+            generatedComponentModel,
+            inlineBindingExpression,
+            referenceReleasingManagerFields,
+            compilerOptions,
+            types,
+            elements);
+      }
+
+      return inlineBindingExpression;
+    }
+
+    private FrameworkInstanceBindingExpression producerFromProviderBindingExpression(
+        FrameworkInstanceBindingExpression providerBindingExpression) {
+      ResolvedBindings resolvedBindings = providerBindingExpression.resolvedBindings();
+      FieldSpec producerField =
+          generateFrameworkField(resolvedBindings, Optional.of(TypeNames.PRODUCER));
+      return providerBindingExpression.producerFromProvider(
+          MemberSelect.localField(generatedComponentModel.name(), producerField.name),
+          newFrameworkFieldInitializer(producerField, resolvedBindings).forProducerFromProvider());
+    }
+
+    private BindingExpression inlineProvisionBindingExpression(
+        BindingExpression bindingExpression) {
+      ProvisionBinding provisionBinding =
+          (ProvisionBinding) bindingExpression.resolvedBindings().contributionBinding();
+      switch (provisionBinding.bindingKind()) {
+        case COMPONENT:
+          return new ComponentInstanceBindingExpression(
+              bindingExpression, provisionBinding, generatedComponentModel.name(), types);
+
+        case COMPONENT_DEPENDENCY:
+          return new BoundInstanceBindingExpression(
+              bindingExpression,
+              ComponentRequirement.forDependency(provisionBinding.key().type()),
+              componentRequirementFields,
+              types);
+
+        case COMPONENT_PROVISION:
+          return new ComponentProvisionBindingExpression(
+              bindingExpression,
+              provisionBinding,
+              graph,
+              componentRequirementFields,
+              compilerOptions,
+              types);
+
+        case SUBCOMPONENT_BUILDER:
+          return new SubcomponentBuilderBindingExpression(
+              bindingExpression,
+              provisionBinding,
+              subcomponentNames.get(bindingExpression.resolvedBindings().bindingKey()),
+              types);
+
+        case SYNTHETIC_MULTIBOUND_SET:
+          return new SetBindingExpression(
+              provisionBinding,
+              graph,
+              componentBindingExpressions,
+              bindingExpression,
+              types,
+              elements);
+
+        case SYNTHETIC_MULTIBOUND_MAP:
+          return new MapBindingExpression(
+              provisionBinding,
+              graph,
+              componentBindingExpressions,
+              bindingExpression,
+              types,
+              elements);
+
+        case SYNTHETIC_OPTIONAL_BINDING:
+          return new OptionalBindingExpression(
+              provisionBinding, bindingExpression, componentBindingExpressions, types);
+
+        case SYNTHETIC_DELEGATE_BINDING:
+          return DelegateBindingExpression.create(
+              graph, bindingExpression, componentBindingExpressions, types, elements);
+
+        case BUILDER_BINDING:
+          return new BoundInstanceBindingExpression(
+              bindingExpression,
+              ComponentRequirement.forBinding(provisionBinding),
+              componentRequirementFields,
+              types);
+
+        case INJECTION:
+        case PROVISION:
+          if (canUseSimpleMethod(provisionBinding)) {
+            return new SimpleMethodBindingExpression(
+                compilerOptions,
+                provisionBinding,
+                bindingExpression,
+                componentBindingExpressions,
+                membersInjectionMethods,
+                componentRequirementFields,
+                types,
+                elements);
+          }
+          // fall through
+
+        default:
+          return bindingExpression;
+      }
+    }
+
+    private boolean usePrivateMethod(ContributionBinding binding) {
+      return (!binding.scope().isPresent() || compilerOptions.experimentalAndroidMode())
+          && PRIVATE_METHOD_KINDS.contains(binding.bindingKind());
+    }
+
+    private boolean canUseSimpleMethod(ContributionBinding binding) {
+      // Use the inlined form when in experimentalAndroidMode, as PrivateMethodBindingExpression
+      // implements scoping directly
+      // TODO(user): Also inline releasable references in experimentalAndroidMode
+      return !binding.scope().isPresent()
+          || (compilerOptions.experimentalAndroidMode()
+              && !referenceReleasingManagerFields.requiresReleasableReferences(
+                  binding.scope().get()));
+    }
   }
 }