Factor out a type from InjectionMethods that stores TypeMirrors instead of MethodSpec/TypeNames
This will help fix a bug in AOT where we need to insert a cast conditionally if a type is assignable or not
RELNOTES=n/a
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=226522358
diff --git a/java/dagger/internal/codegen/InjectionMethod.java b/java/dagger/internal/codegen/InjectionMethod.java
new file mode 100644
index 0000000..b739088
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionMethod.java
@@ -0,0 +1,208 @@
+/*
+ * 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.checkArgument;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+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 java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Parameterizable;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A static method that implements 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>
+ *
+ * <p>Note that although this type uses {@code @AutoValue}, it uses instance equality. It uses
+ * {@code @AutoValue} to avoid the boilerplate of writing a correct builder, but is not intended to
+ * actually be a value type.
+ */
+@AutoValue
+abstract class InjectionMethod {
+ abstract String name();
+
+ abstract boolean varargs();
+
+ abstract ImmutableList<TypeVariableName> typeVariables();
+
+ abstract ImmutableMap<ParameterSpec, TypeMirror> parameters();
+
+ abstract Optional<TypeMirror> returnType();
+
+ abstract Optional<DeclaredType> nullableAnnotation();
+
+ abstract ImmutableList<TypeMirror> exceptions();
+
+ abstract CodeBlock methodBody();
+
+ abstract ClassName enclosingClass();
+
+ MethodSpec toMethodSpec() {
+ MethodSpec.Builder builder =
+ methodBuilder(name())
+ .addModifiers(PUBLIC, STATIC)
+ .varargs(varargs())
+ .addTypeVariables(typeVariables())
+ .addParameters(parameters().keySet())
+ .addCode(methodBody());
+ returnType().map(TypeName::get).ifPresent(builder::returns);
+ nullableAnnotation()
+ .ifPresent(nullableType -> CodeBlocks.addAnnotation(builder, nullableType));
+ exceptions().stream().map(TypeName::get).forEach(builder::addException);
+ return builder.build();
+ }
+
+ CodeBlock invoke(List<CodeBlock> arguments, ClassName requestingClass) {
+ checkArgument(arguments.size() == parameters().size());
+ CodeBlock.Builder invocation = CodeBlock.builder();
+ if (!enclosingClass().equals(requestingClass)) {
+ invocation.add("$T.", enclosingClass());
+ }
+ return invocation.add("$L($L)", name(), makeParametersCodeBlock(arguments)).build();
+ }
+
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ static Builder builder(DaggerElements elements) {
+ Builder builder = new AutoValue_InjectionMethod.Builder();
+ builder.elements = elements;
+ builder.varargs(false).exceptions(ImmutableList.of()).nullableAnnotation(Optional.empty());
+ return builder;
+ }
+
+ @CanIgnoreReturnValue
+ @AutoValue.Builder
+ abstract static class Builder {
+ private final UniqueNameSet parameterNames = new UniqueNameSet();
+ private final CodeBlock.Builder methodBody = CodeBlock.builder();
+ private DaggerElements elements;
+
+ abstract ImmutableMap.Builder<ParameterSpec, TypeMirror> parametersBuilder();
+ abstract ImmutableList.Builder<TypeVariableName> typeVariablesBuilder();
+ abstract Builder name(String name);
+ abstract Builder varargs(boolean varargs);
+ abstract Builder returnType(TypeMirror returnType);
+ abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
+ abstract Builder nullableAnnotation(Optional<DeclaredType> nullableAnnotation);
+ abstract Builder methodBody(CodeBlock methodBody);
+
+ final CodeBlock.Builder methodBodyBuilder() {
+ return methodBody;
+ }
+
+ abstract Builder enclosingClass(ClassName enclosingClass);
+
+ /**
+ * Adds a parameter for the given name and type. If another parameter has already been added
+ * with the same name, the name is disambiguated.
+ */
+ ParameterSpec addParameter(String name, TypeMirror type) {
+ ParameterSpec parameter =
+ ParameterSpec.builder(TypeName.get(type), parameterNames.getUniqueName(name)).build();
+ parametersBuilder().put(parameter, type);
+ return parameter;
+ }
+
+ /**
+ * Calls {@link #copyParameter(VariableElement)} for each parameter of of {@code method}, and
+ * concatenates the results of each call, {@link CodeBlocks#makeParametersCodeBlock(Iterable)
+ * separated with commas}.
+ */
+ CodeBlock copyParameters(ExecutableElement method) {
+ ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
+ for (VariableElement parameter : method.getParameters()) {
+ argumentsBuilder.add(copyParameter(parameter));
+ }
+ varargs(method.isVarArgs());
+ return makeParametersCodeBlock(argumentsBuilder.build());
+ }
+
+ /**
+ * Adds {@code parameter} as a parameter of this method, using a publicly accessible version of
+ * the parameter's type. Returns a {@link CodeBlock} of the usage of this parameter within the
+ * injection method's {@link #methodBody()}.
+ */
+ CodeBlock copyParameter(VariableElement parameter) {
+ TypeMirror elementType = parameter.asType();
+ boolean useObject = !isRawTypePubliclyAccessible(elementType);
+ TypeMirror publicType = useObject ? objectType() : elementType;
+ ParameterSpec parameterSpec = addParameter(parameter.getSimpleName().toString(), publicType);
+ return useObject
+ ? CodeBlock.of("($T) $N", elementType, parameterSpec)
+ : CodeBlock.of("$N", parameterSpec);
+ }
+
+ private TypeMirror objectType() {
+ return elements.getTypeElement(Object.class).asType();
+ }
+
+ /**
+ * Adds each type parameter of {@code parameterizable} as a type parameter of this injection
+ * method.
+ */
+ Builder copyTypeParameters(Parameterizable parameterizable) {
+ parameterizable.getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEach(typeVariablesBuilder()::add);
+ return this;
+ }
+
+ Builder copyThrows(ExecutableElement element) {
+ exceptions(element.getThrownTypes());
+ return this;
+ }
+
+ @CheckReturnValue
+ final InjectionMethod build() {
+ return methodBody(methodBody.build()).buildInternal();
+ }
+
+ abstract InjectionMethod buildInternal();
+ }
+}