dagger.internal.codegen.writing.InjectionMethods Maven / Gradle / Ivy
                 Go to download
                
        
                    Show more of this group  Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
                Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
                
            /*
 * 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.writing;
import static androidx.room.compiler.codegen.compat.XConverters.toXPoet;
import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedParameter;
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.binding.SourceFiles.generatedProxyMethodName;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorMethodName;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
import static dagger.internal.codegen.xprocessing.XCodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.xprocessing.XCodeBlocks.toConcatenatedCodeBlock;
import static dagger.internal.codegen.xprocessing.XCodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.xprocessing.XElements.asExecutable;
import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static dagger.internal.codegen.xprocessing.XTypes.erasedTypeName;
import androidx.room.compiler.codegen.XClassName;
import androidx.room.compiler.codegen.XCodeBlock;
import androidx.room.compiler.codegen.XTypeName;
import androidx.room.compiler.processing.XExecutableElement;
import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.TypeName;
import dagger.internal.codegen.base.UniqueNameSet;
import dagger.internal.codegen.binding.AssistedInjectionBinding;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.binding.InjectionBinding;
import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.xprocessing.Nullability;
import dagger.internal.codegen.xprocessing.XFunSpecs;
import dagger.internal.codegen.xprocessing.XParameterSpecs;
import dagger.internal.codegen.xprocessing.XTypeNames;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
final class InjectionMethods {
  /**
   * A 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.
   *
   * For {@code @Provides} methods named "foo", the method name is "proxyFoo". For example:
   *
   * 
   * abstract class FooModule {
   *   {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
   * }
   *
   * public static proxyProvideFoo(Bar bar, Baz baz) { … }
   * 
   *
   * For {@code @Inject}ed constructors, the method name is "newFoo". For example:
   *
   * 
   * class Foo {
   *   {@literal @Inject} Foo(Bar bar) {}
   * }
   *
   * public static Foo newFoo(Bar bar) { … }
   * 
   */
  static final class ProvisionMethod {
    /**
     * Invokes the injection method for {@code binding}, with the dependencies transformed with the
     * {@code dependencyUsage} function.
     */
    static XCodeBlock invoke(
        ContributionBinding binding,
        Function dependencyUsage,
        Function uniqueAssistedParameterName,
        XClassName requestingClass,
        Optional moduleReference,
        CompilerOptions compilerOptions) {
      ImmutableList.Builder arguments = ImmutableList.builder();
      moduleReference.ifPresent(arguments::add);
      invokeArguments(binding, dependencyUsage, uniqueAssistedParameterName)
          .forEach(arguments::add);
      XClassName enclosingClass = generatedClassNameForBinding(binding);
      String methodName = generatedProxyMethodName(binding);
      return invokeMethod(methodName, arguments.build(), enclosingClass, requestingClass);
    }
    static ImmutableList invokeArguments(
        ContributionBinding binding,
        Function dependencyUsage,
        Function uniqueAssistedParameterName) {
      ImmutableMap dependencyRequestMap =
          provisionDependencies(binding).stream()
              .collect(
                  toImmutableMap(
                      request -> asMethodParameter(request.requestElement().get().xprocessing()),
                      request -> request));
      ImmutableList.Builder arguments = ImmutableList.builder();
      XExecutableElement method = asExecutable(binding.bindingElement().get());
      for (XExecutableParameterElement parameter : method.getParameters()) {
        if (isAssistedParameter(parameter)) {
          arguments.add(XCodeBlock.of("%L", uniqueAssistedParameterName.apply(parameter)));
        } else if (dependencyRequestMap.containsKey(parameter)) {
          DependencyRequest request = dependencyRequestMap.get(parameter);
          arguments.add(dependencyUsage.apply(request));
        } else {
          throw new AssertionError("Unexpected parameter: " + parameter);
        }
      }
      return arguments.build();
    }
    private static ImmutableSet provisionDependencies(
        ContributionBinding binding) {
      switch (binding.kind()) {
        case INJECTION:
          return ((InjectionBinding) binding).constructorDependencies();
        case ASSISTED_INJECTION:
          return ((AssistedInjectionBinding) binding).constructorDependencies();
        case PROVISION:
          return ((ProvisionBinding) binding).dependencies();
        default:
          throw new AssertionError("Unexpected binding kind: " + binding.kind());
      }
    }
  }
  /**
   * 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.
   *
   * Example:
   *
   * 
   * 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) { … }
   * 
   */
  static final class InjectionSiteMethod {
    /**
     * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
     * transformed using the {@code dependencyUsage} function.
     *
     * @param instanceType the type of the {@code instance} parameter
     */
    static XCodeBlock invokeAll(
        ImmutableSet injectionSites,
        XClassName generatedTypeName,
        XCodeBlock instanceCodeBlock,
        XType instanceType,
        Function dependencyUsage) {
      return injectionSites.stream()
          .map(
              injectionSite -> {
                XType injectSiteType = injectionSite.enclosingTypeElement().getType();
                // If instance has been declared as Object because it is not accessible from the
                // component, but the injectionSite is in a supertype of instanceType that is
                // publicly accessible, the InjectionSiteMethod will request the actual type and not
                // Object as the first parameter. If so, cast to the supertype which is accessible
                // from within generatedTypeName
                XCodeBlock maybeCastedInstance =
                    instanceType.getTypeName().equals(TypeName.OBJECT)
                            && isRawTypeAccessible(
                                injectSiteType, generatedTypeName.getPackageName())
                        ? XCodeBlock.ofCast(
                            toXPoet(erasedTypeName(injectSiteType)), instanceCodeBlock)
                        : instanceCodeBlock;
                return XCodeBlock.of(
                    "%L;",
                    invoke(injectionSite, generatedTypeName, maybeCastedInstance, dependencyUsage));
              })
          .collect(toConcatenatedCodeBlock());
    }
    /**
     * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
     * using the {@code dependencyUsage} function.
     */
    private static XCodeBlock invoke(
        InjectionSite injectionSite,
        XClassName generatedTypeName,
        XCodeBlock instanceCodeBlock,
        Function dependencyUsage) {
      ImmutableList arguments =
          ImmutableList.builder()
              .add(instanceCodeBlock)
              .addAll(
                  injectionSite.dependencies().stream()
                      .map(dependencyUsage)
                      .collect(toImmutableList()))
              .build();
      XClassName enclosingClass = membersInjectorNameForType(injectionSite.enclosingTypeElement());
      String methodName = membersInjectorMethodName(injectionSite);
      return invokeMethod(methodName, arguments, enclosingClass, generatedTypeName);
    }
  }
  private static XCodeBlock invokeMethod(
      String methodName,
      ImmutableList parameters,
      XClassName enclosingClass,
      XClassName requestingClass) {
    XCodeBlock parameterBlock = makeParametersCodeBlock(parameters);
    return enclosingClass.equals(requestingClass)
        ? XCodeBlock.of("%L(%L)", methodName, parameterBlock)
        : XCodeBlock.of("%T.%L(%L)", enclosingClass, methodName, parameterBlock);
  }
  static XCodeBlock copyParameters(
      XFunSpecs.Builder methodBuilder,
      UniqueNameSet parameterNameSet,
      List extends XVariableElement> parameters) {
    return parameters.stream()
        .map(
            parameter -> {
              String name =
                  parameterNameSet.getUniqueName(
                      isMethodParameter(parameter)
                          ? asMethodParameter(parameter).getJvmName()
                          : getSimpleName(parameter));
              boolean useObject = !isRawTypePubliclyAccessible(parameter.getType());
              return copyParameter(
                  methodBuilder, parameter.getType(), name, useObject, Nullability.of(parameter));
            })
        .collect(toParametersCodeBlock());
  }
  static XCodeBlock copyParameter(
      XFunSpecs.Builder methodBuilder,
      XType type,
      String name,
      boolean useObject,
      Nullability nullability) {
    XTypeName typeName =
        XTypeNames.withTypeNullability(
            useObject ? XTypeName.ANY_OBJECT : type.asTypeName(), nullability);
    methodBuilder.addParameter(XParameterSpecs.of(name, typeName, nullability));
    return useObject
        ? XCodeBlock.ofCast(type.asTypeName(), XCodeBlock.of("%L", name))
        : XCodeBlock.of("%L", name);
  }
}
                    © 2015 - 2025 Weber Informatics LLC | Privacy Policy