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 androidx.room.compiler.processing.XElementKt.isConstructor; 20 import static androidx.room.compiler.processing.XElementKt.isMethod; 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 import static dagger.internal.codegen.xprocessing.XElements.asMethod; 27 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.isPreJava8SourceVersion; 28 29 import androidx.room.compiler.processing.XElement; 30 import androidx.room.compiler.processing.XProcessingEnv; 31 import androidx.room.compiler.processing.XType; 32 import androidx.room.compiler.processing.XTypeElement; 33 import com.squareup.javapoet.ClassName; 34 import com.squareup.javapoet.CodeBlock; 35 import com.squareup.javapoet.TypeName; 36 import dagger.assisted.Assisted; 37 import dagger.assisted.AssistedFactory; 38 import dagger.assisted.AssistedInject; 39 import dagger.internal.codegen.binding.ComponentRequirement; 40 import dagger.internal.codegen.binding.ProvisionBinding; 41 import dagger.internal.codegen.compileroption.CompilerOptions; 42 import dagger.internal.codegen.javapoet.Expression; 43 import dagger.internal.codegen.model.DependencyRequest; 44 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; 45 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod; 46 import java.util.Optional; 47 48 /** 49 * A binding expression that invokes methods or constructors directly (without attempting to scope) 50 * {@link dagger.internal.codegen.model.RequestKind#INSTANCE} requests. 51 */ 52 final class SimpleMethodRequestRepresentation extends RequestRepresentation { 53 private final CompilerOptions compilerOptions; 54 private final XProcessingEnv processingEnv; 55 private final ProvisionBinding provisionBinding; 56 private final ComponentRequestRepresentations componentRequestRepresentations; 57 private final MembersInjectionMethods membersInjectionMethods; 58 private final ComponentRequirementExpressions componentRequirementExpressions; 59 private final ShardImplementation shardImplementation; 60 61 @AssistedInject SimpleMethodRequestRepresentation( @ssisted ProvisionBinding binding, MembersInjectionMethods membersInjectionMethods, CompilerOptions compilerOptions, XProcessingEnv processingEnv, ComponentRequestRepresentations componentRequestRepresentations, ComponentRequirementExpressions componentRequirementExpressions, ComponentImplementation componentImplementation)62 SimpleMethodRequestRepresentation( 63 @Assisted ProvisionBinding binding, 64 MembersInjectionMethods membersInjectionMethods, 65 CompilerOptions compilerOptions, 66 XProcessingEnv processingEnv, 67 ComponentRequestRepresentations componentRequestRepresentations, 68 ComponentRequirementExpressions componentRequirementExpressions, 69 ComponentImplementation componentImplementation) { 70 this.compilerOptions = compilerOptions; 71 this.processingEnv = processingEnv; 72 this.provisionBinding = binding; 73 checkArgument( 74 provisionBinding.implicitDependencies().isEmpty(), 75 "framework deps are not currently supported"); 76 checkArgument(provisionBinding.bindingElement().isPresent()); 77 this.componentRequestRepresentations = componentRequestRepresentations; 78 this.membersInjectionMethods = membersInjectionMethods; 79 this.componentRequirementExpressions = componentRequirementExpressions; 80 this.shardImplementation = componentImplementation.shardImplementation(binding); 81 } 82 83 @Override getDependencyExpression(ClassName requestingClass)84 Expression getDependencyExpression(ClassName requestingClass) { 85 return requiresInjectionMethod(provisionBinding, compilerOptions, requestingClass) 86 ? invokeInjectionMethod(requestingClass) 87 : invokeMethod(requestingClass); 88 } 89 invokeMethod(ClassName requestingClass)90 private Expression invokeMethod(ClassName requestingClass) { 91 // TODO(dpb): align this with the contents of InlineMethods.create 92 CodeBlock arguments = 93 makeParametersCodeBlock( 94 ProvisionMethod.invokeArguments( 95 provisionBinding, 96 request -> dependencyArgument(request, requestingClass).codeBlock(), 97 shardImplementation::getUniqueFieldNameForAssistedParam)); 98 XElement bindingElement = provisionBinding.bindingElement().get(); 99 XTypeElement bindingTypeElement = provisionBinding.bindingTypeElement().get(); 100 CodeBlock invocation; 101 if (isConstructor(bindingElement)) { 102 invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments); 103 } else if (isMethod(bindingElement)) { 104 CodeBlock module; 105 Optional<CodeBlock> requiredModuleInstance = moduleReference(requestingClass); 106 if (requiredModuleInstance.isPresent()) { 107 module = requiredModuleInstance.get(); 108 } else if (bindingTypeElement.isKotlinObject() && !bindingTypeElement.isCompanionObject()) { 109 // Call through the singleton instance. 110 // See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods 111 module = CodeBlock.of("$T.INSTANCE", bindingTypeElement.getClassName()); 112 } else { 113 module = CodeBlock.of("$T", bindingTypeElement.getClassName()); 114 } 115 invocation = 116 CodeBlock.of("$L.$L($L)", module, asMethod(bindingElement).getJvmName(), arguments); 117 } else { 118 throw new AssertionError("Unexpected binding element: " + bindingElement); 119 } 120 121 return Expression.create(simpleMethodReturnType(), invocation); 122 } 123 constructorTypeName(ClassName requestingClass)124 private TypeName constructorTypeName(ClassName requestingClass) { 125 XType type = provisionBinding.key().type().xprocessing(); 126 return type.getTypeArguments().stream() 127 .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName())) 128 ? type.getTypeName() 129 : rawTypeName(type.getTypeName()); 130 } 131 invokeInjectionMethod(ClassName requestingClass)132 private Expression invokeInjectionMethod(ClassName requestingClass) { 133 return injectMembers( 134 ProvisionMethod.invoke( 135 provisionBinding, 136 request -> dependencyArgument(request, requestingClass).codeBlock(), 137 shardImplementation::getUniqueFieldNameForAssistedParam, 138 requestingClass, 139 moduleReference(requestingClass), 140 compilerOptions), 141 requestingClass); 142 } 143 dependencyArgument(DependencyRequest dependency, ClassName requestingClass)144 private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) { 145 return componentRequestRepresentations.getDependencyArgumentExpression( 146 dependency, requestingClass); 147 } 148 injectMembers(CodeBlock instance, ClassName requestingClass)149 private Expression injectMembers(CodeBlock instance, ClassName requestingClass) { 150 if (provisionBinding.injectionSites().isEmpty()) { 151 return Expression.create(simpleMethodReturnType(), instance); 152 } 153 if (isPreJava8SourceVersion(processingEnv)) { 154 // Java 7 type inference can't figure out that instance in 155 // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not 156 // Parameterized<Object> 157 if (!provisionBinding.key().type().xprocessing().getTypeArguments().isEmpty()) { 158 TypeName keyType = provisionBinding.key().type().xprocessing().getTypeName(); 159 instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance); 160 } 161 } 162 return membersInjectionMethods.getInjectExpression( 163 provisionBinding.key(), instance, requestingClass); 164 } 165 moduleReference(ClassName requestingClass)166 private Optional<CodeBlock> moduleReference(ClassName requestingClass) { 167 return provisionBinding.requiresModuleInstance() 168 ? provisionBinding 169 .contributingModule() 170 .map(XTypeElement::getType) 171 .map(ComponentRequirement::forModule) 172 .map(module -> componentRequirementExpressions.getExpression(module, requestingClass)) 173 : Optional.empty(); 174 } 175 simpleMethodReturnType()176 private XType simpleMethodReturnType() { 177 return provisionBinding 178 .contributedPrimitiveType() 179 .orElse(provisionBinding.key().type().xprocessing()); 180 } 181 182 @AssistedFactory 183 static interface Factory { create(ProvisionBinding binding)184 SimpleMethodRequestRepresentation create(ProvisionBinding binding); 185 } 186 } 187