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.writing; 18 19 import static com.google.auto.common.MoreElements.asExecutable; 20 import static com.google.auto.common.MoreElements.asType; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock; 23 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName; 24 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 25 import static dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod.requiresInjectionMethod; 26 27 import com.google.auto.common.MoreTypes; 28 import com.squareup.javapoet.ClassName; 29 import com.squareup.javapoet.CodeBlock; 30 import com.squareup.javapoet.MethodSpec; 31 import com.squareup.javapoet.TypeName; 32 import dagger.internal.codegen.binding.ComponentRequirement; 33 import dagger.internal.codegen.binding.ProvisionBinding; 34 import dagger.internal.codegen.compileroption.CompilerOptions; 35 import dagger.internal.codegen.javapoet.Expression; 36 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 37 import dagger.internal.codegen.langmodel.DaggerElements; 38 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod; 39 import dagger.model.DependencyRequest; 40 import java.util.Optional; 41 import javax.lang.model.SourceVersion; 42 import javax.lang.model.element.Element; 43 import javax.lang.model.element.ExecutableElement; 44 import javax.lang.model.type.DeclaredType; 45 import javax.lang.model.type.TypeMirror; 46 47 /** 48 * A binding expression that invokes methods or constructors directly (without attempting to scope) 49 * {@link dagger.model.RequestKind#INSTANCE} requests. 50 */ 51 final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression { 52 private final CompilerOptions compilerOptions; 53 private final ProvisionBinding provisionBinding; 54 private final ComponentBindingExpressions componentBindingExpressions; 55 private final MembersInjectionMethods membersInjectionMethods; 56 private final ComponentRequirementExpressions componentRequirementExpressions; 57 private final DaggerElements elements; 58 private final SourceVersion sourceVersion; 59 private final KotlinMetadataUtil metadataUtil; 60 SimpleMethodBindingExpression( ProvisionBinding binding, CompilerOptions compilerOptions, ComponentBindingExpressions componentBindingExpressions, MembersInjectionMethods membersInjectionMethods, ComponentRequirementExpressions componentRequirementExpressions, DaggerElements elements, SourceVersion sourceVersion, KotlinMetadataUtil metadataUtil)61 SimpleMethodBindingExpression( 62 ProvisionBinding binding, 63 CompilerOptions compilerOptions, 64 ComponentBindingExpressions componentBindingExpressions, 65 MembersInjectionMethods membersInjectionMethods, 66 ComponentRequirementExpressions componentRequirementExpressions, 67 DaggerElements elements, 68 SourceVersion sourceVersion, 69 KotlinMetadataUtil metadataUtil) { 70 super(binding); 71 this.compilerOptions = compilerOptions; 72 this.provisionBinding = binding; 73 this.metadataUtil = metadataUtil; 74 checkArgument( 75 provisionBinding.implicitDependencies().isEmpty(), 76 "framework deps are not currently supported"); 77 checkArgument(provisionBinding.bindingElement().isPresent()); 78 this.componentBindingExpressions = componentBindingExpressions; 79 this.membersInjectionMethods = membersInjectionMethods; 80 this.componentRequirementExpressions = componentRequirementExpressions; 81 this.elements = elements; 82 this.sourceVersion = sourceVersion; 83 } 84 85 @Override getDependencyExpression(ClassName requestingClass)86 Expression getDependencyExpression(ClassName requestingClass) { 87 return requiresInjectionMethod(provisionBinding, compilerOptions, requestingClass) 88 ? invokeInjectionMethod(requestingClass) 89 : invokeMethod(requestingClass); 90 } 91 invokeMethod(ClassName requestingClass)92 private Expression invokeMethod(ClassName requestingClass) { 93 // TODO(dpb): align this with the contents of InlineMethods.create 94 CodeBlock arguments = 95 makeParametersCodeBlock( 96 ProvisionMethod.invokeArguments( 97 provisionBinding, 98 request -> dependencyArgument(request, requestingClass).codeBlock(), 99 requestingClass)); 100 ExecutableElement method = asExecutable(provisionBinding.bindingElement().get()); 101 CodeBlock invocation; 102 switch (method.getKind()) { 103 case CONSTRUCTOR: 104 invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments); 105 break; 106 case METHOD: 107 CodeBlock module; 108 Optional<CodeBlock> requiredModuleInstance = moduleReference(requestingClass); 109 if (requiredModuleInstance.isPresent()) { 110 module = requiredModuleInstance.get(); 111 } else if (metadataUtil.isObjectClass(asType(method.getEnclosingElement()))) { 112 // Call through the singleton instance. 113 // See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods 114 module = CodeBlock.of("$T.INSTANCE", provisionBinding.bindingTypeElement().get()); 115 } else { 116 module = CodeBlock.of("$T", provisionBinding.bindingTypeElement().get()); 117 } 118 invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments); 119 break; 120 default: 121 throw new IllegalStateException(); 122 } 123 124 return Expression.create(simpleMethodReturnType(), invocation); 125 } 126 constructorTypeName(ClassName requestingClass)127 private TypeName constructorTypeName(ClassName requestingClass) { 128 DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type()); 129 TypeName typeName = TypeName.get(type); 130 if (type.getTypeArguments().stream() 131 .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) { 132 return typeName; 133 } 134 return rawTypeName(typeName); 135 } 136 invokeInjectionMethod(ClassName requestingClass)137 private Expression invokeInjectionMethod(ClassName requestingClass) { 138 return injectMembers( 139 ProvisionMethod.invoke( 140 provisionBinding, 141 request -> dependencyArgument(request, requestingClass).codeBlock(), 142 requestingClass, 143 moduleReference(requestingClass), 144 compilerOptions, 145 metadataUtil)); 146 } 147 dependencyArgument(DependencyRequest dependency, ClassName requestingClass)148 private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) { 149 return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass); 150 } 151 injectMembers(CodeBlock instance)152 private Expression injectMembers(CodeBlock instance) { 153 if (provisionBinding.injectionSites().isEmpty()) { 154 return Expression.create(simpleMethodReturnType(), instance); 155 } 156 if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) { 157 // Java 7 type inference can't figure out that instance in 158 // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not 159 // Parameterized<Object> 160 if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) { 161 TypeName keyType = TypeName.get(provisionBinding.key().type()); 162 instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance); 163 } 164 } 165 MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key()); 166 TypeMirror returnType = 167 membersInjectionMethod.returnType.equals(TypeName.OBJECT) 168 ? elements.getTypeElement(Object.class).asType() 169 : provisionBinding.key().type(); 170 return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance)); 171 } 172 moduleReference(ClassName requestingClass)173 private Optional<CodeBlock> moduleReference(ClassName requestingClass) { 174 return provisionBinding.requiresModuleInstance() 175 ? provisionBinding 176 .contributingModule() 177 .map(Element::asType) 178 .map(ComponentRequirement::forModule) 179 .map(module -> componentRequirementExpressions.getExpression(module, requestingClass)) 180 : Optional.empty(); 181 } 182 simpleMethodReturnType()183 private TypeMirror simpleMethodReturnType() { 184 return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type()); 185 } 186 } 187