1 /* 2 * Copyright (C) 2016 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.auto.common.MoreElements.asExecutable; 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod; 22 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 23 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName; 24 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 25 26 import com.google.auto.common.MoreTypes; 27 import com.google.common.collect.ImmutableMap; 28 import com.google.common.collect.Maps; 29 import com.squareup.javapoet.ClassName; 30 import com.squareup.javapoet.CodeBlock; 31 import com.squareup.javapoet.MethodSpec; 32 import com.squareup.javapoet.TypeName; 33 import dagger.internal.codegen.InjectionMethods.ProvisionMethod; 34 import dagger.internal.codegen.javapoet.Expression; 35 import dagger.internal.codegen.langmodel.DaggerElements; 36 import dagger.internal.codegen.langmodel.DaggerTypes; 37 import dagger.model.DependencyRequest; 38 import java.util.Optional; 39 import java.util.function.Function; 40 import javax.lang.model.SourceVersion; 41 import javax.lang.model.element.Element; 42 import javax.lang.model.element.ExecutableElement; 43 import javax.lang.model.type.DeclaredType; 44 import javax.lang.model.type.TypeMirror; 45 46 /** 47 * A binding expression that invokes methods or constructors directly (without attempting to scope) 48 * {@link dagger.model.RequestKind#INSTANCE} requests. 49 */ 50 final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression { 51 private final CompilerOptions compilerOptions; 52 private final ProvisionBinding provisionBinding; 53 private final ComponentBindingExpressions componentBindingExpressions; 54 private final MembersInjectionMethods membersInjectionMethods; 55 private final ComponentRequirementExpressions componentRequirementExpressions; 56 private final DaggerTypes types; 57 private final DaggerElements elements; 58 private final SourceVersion sourceVersion; 59 SimpleMethodBindingExpression( ResolvedBindings resolvedBindings, CompilerOptions compilerOptions, ComponentBindingExpressions componentBindingExpressions, MembersInjectionMethods membersInjectionMethods, ComponentRequirementExpressions componentRequirementExpressions, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion)60 SimpleMethodBindingExpression( 61 ResolvedBindings resolvedBindings, 62 CompilerOptions compilerOptions, 63 ComponentBindingExpressions componentBindingExpressions, 64 MembersInjectionMethods membersInjectionMethods, 65 ComponentRequirementExpressions componentRequirementExpressions, 66 DaggerTypes types, 67 DaggerElements elements, 68 SourceVersion sourceVersion) { 69 super(resolvedBindings); 70 this.compilerOptions = compilerOptions; 71 this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding(); 72 checkArgument( 73 provisionBinding.implicitDependencies().isEmpty(), 74 "framework deps are not currently supported"); 75 checkArgument(provisionBinding.bindingElement().isPresent()); 76 this.componentBindingExpressions = componentBindingExpressions; 77 this.membersInjectionMethods = membersInjectionMethods; 78 this.componentRequirementExpressions = componentRequirementExpressions; 79 this.types = types; 80 this.elements = elements; 81 this.sourceVersion = sourceVersion; 82 } 83 84 @Override getDependencyExpression(ClassName requestingClass)85 Expression getDependencyExpression(ClassName requestingClass) { 86 ImmutableMap<DependencyRequest, Expression> arguments = 87 ImmutableMap.copyOf( 88 Maps.asMap( 89 provisionBinding.dependencies(), 90 request -> dependencyArgument(request, requestingClass))); 91 Function<DependencyRequest, CodeBlock> argumentsFunction = 92 request -> arguments.get(request).codeBlock(); 93 return requiresInjectionMethod( 94 provisionBinding, 95 arguments.values().asList(), 96 compilerOptions, 97 requestingClass.packageName(), 98 types) 99 ? invokeInjectionMethod(argumentsFunction, requestingClass) 100 : invokeMethod(argumentsFunction, requestingClass); 101 } 102 invokeMethod( Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass)103 private Expression invokeMethod( 104 Function<DependencyRequest, CodeBlock> argumentsFunction, 105 ClassName requestingClass) { 106 // TODO(dpb): align this with the contents of InlineMethods.create 107 CodeBlock arguments = 108 provisionBinding.dependencies().stream() 109 .map(argumentsFunction) 110 .collect(toParametersCodeBlock()); 111 ExecutableElement method = asExecutable(provisionBinding.bindingElement().get()); 112 CodeBlock invocation; 113 switch (method.getKind()) { 114 case CONSTRUCTOR: 115 invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments); 116 break; 117 case METHOD: 118 CodeBlock module = 119 moduleReference(requestingClass) 120 .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get())); 121 invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments); 122 break; 123 default: 124 throw new IllegalStateException(); 125 } 126 127 return Expression.create(simpleMethodReturnType(), invocation); 128 } 129 constructorTypeName(ClassName requestingClass)130 private TypeName constructorTypeName(ClassName requestingClass) { 131 DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type()); 132 TypeName typeName = TypeName.get(type); 133 if (type.getTypeArguments() 134 .stream() 135 .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) { 136 return typeName; 137 } 138 return rawTypeName(typeName); 139 } 140 invokeInjectionMethod( Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass)141 private Expression invokeInjectionMethod( 142 Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) { 143 return injectMembers( 144 ProvisionMethod.invoke( 145 provisionBinding, 146 argumentsFunction, 147 requestingClass, 148 moduleReference(requestingClass), 149 compilerOptions, 150 elements)); 151 } 152 dependencyArgument(DependencyRequest dependency, ClassName requestingClass)153 private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) { 154 return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass); 155 } 156 injectMembers(CodeBlock instance)157 private Expression injectMembers(CodeBlock instance) { 158 if (provisionBinding.injectionSites().isEmpty()) { 159 return Expression.create(simpleMethodReturnType(), instance); 160 } 161 if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) { 162 // Java 7 type inference can't figure out that instance in 163 // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not 164 // Parameterized<Object> 165 if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) { 166 TypeName keyType = TypeName.get(provisionBinding.key().type()); 167 instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance); 168 } 169 } 170 MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key()); 171 TypeMirror returnType = 172 membersInjectionMethod.returnType.equals(TypeName.OBJECT) 173 ? elements.getTypeElement(Object.class).asType() 174 : provisionBinding.key().type(); 175 return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance)); 176 } 177 moduleReference(ClassName requestingClass)178 private Optional<CodeBlock> moduleReference(ClassName requestingClass) { 179 return provisionBinding.requiresModuleInstance() 180 ? provisionBinding 181 .contributingModule() 182 .map(Element::asType) 183 .map(ComponentRequirement::forModule) 184 .map(module -> componentRequirementExpressions.getExpression(module, requestingClass)) 185 : Optional.empty(); 186 } 187 simpleMethodReturnType()188 private TypeMirror simpleMethodReturnType() { 189 return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type()); 190 } 191 } 192