1 /* 2 * Copyright (C) 2017 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.squareup.javapoet.MethodSpec.methodBuilder; 20 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; 21 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 22 import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD; 23 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 24 import static javax.lang.model.element.Modifier.PRIVATE; 25 26 import androidx.room.compiler.processing.XProcessingEnv; 27 import androidx.room.compiler.processing.XType; 28 import androidx.room.compiler.processing.XTypeElement; 29 import com.google.common.collect.ImmutableSet; 30 import com.squareup.javapoet.ClassName; 31 import com.squareup.javapoet.CodeBlock; 32 import com.squareup.javapoet.MethodSpec; 33 import com.squareup.javapoet.ParameterSpec; 34 import com.squareup.javapoet.TypeName; 35 import dagger.internal.codegen.binding.Binding; 36 import dagger.internal.codegen.binding.BindingGraph; 37 import dagger.internal.codegen.binding.MembersInjectionBinding; 38 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; 39 import dagger.internal.codegen.binding.ProvisionBinding; 40 import dagger.internal.codegen.javapoet.Expression; 41 import dagger.internal.codegen.model.Key; 42 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; 43 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod; 44 import java.util.LinkedHashMap; 45 import java.util.Map; 46 import javax.inject.Inject; 47 48 /** Manages the member injection methods for a component. */ 49 @PerComponentImplementation 50 final class MembersInjectionMethods { 51 private final Map<Key, Expression> injectMethodExpressions = new LinkedHashMap<>(); 52 private final ComponentImplementation componentImplementation; 53 private final ComponentRequestRepresentations bindingExpressions; 54 private final BindingGraph graph; 55 private final XProcessingEnv processingEnv; 56 57 @Inject MembersInjectionMethods( ComponentImplementation componentImplementation, ComponentRequestRepresentations bindingExpressions, BindingGraph graph, XProcessingEnv processingEnv)58 MembersInjectionMethods( 59 ComponentImplementation componentImplementation, 60 ComponentRequestRepresentations bindingExpressions, 61 BindingGraph graph, 62 XProcessingEnv processingEnv) { 63 this.componentImplementation = componentImplementation; 64 this.bindingExpressions = bindingExpressions; 65 this.graph = graph; 66 this.processingEnv = processingEnv; 67 } 68 69 /** 70 * Returns the members injection {@link Expression} for the given {@link Key}, creating it if 71 * necessary. 72 */ getInjectExpression(Key key, CodeBlock instance, ClassName requestingClass)73 Expression getInjectExpression(Key key, CodeBlock instance, ClassName requestingClass) { 74 Binding binding = 75 graph.localMembersInjectionBinding(key).isPresent() 76 ? graph.localMembersInjectionBinding(key).get() 77 : graph.localContributionBinding(key).get(); 78 Expression expression = 79 reentrantComputeIfAbsent( 80 injectMethodExpressions, key, k -> injectMethodExpression(binding)); 81 ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding); 82 return Expression.create( 83 expression.type(), 84 shardImplementation.name().equals(requestingClass) 85 ? CodeBlock.of("$L($L)", expression.codeBlock(), instance) 86 : CodeBlock.of( 87 "$L.$L($L)", 88 shardImplementation.shardFieldReference(), 89 expression.codeBlock(), 90 instance)); 91 } 92 injectMethodExpression(Binding binding)93 private Expression injectMethodExpression(Binding binding) { 94 // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid 95 // exceeding component class constant pool limit. 96 // Add to Component Shard so that is can be accessible from Switching Providers. 97 ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding); 98 XType keyType = binding.key().type().xprocessing(); 99 XType membersInjectedType = 100 isTypeAccessibleFrom(keyType, shardImplementation.name().packageName()) 101 ? keyType 102 : processingEnv.requireType(TypeName.OBJECT); 103 String bindingTypeName = getSimpleName(binding.bindingTypeElement().get()); 104 // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class 105 // simple names Foo.Builder -> injectFooBuilder 106 String methodName = shardImplementation.getUniqueMethodName("inject" + bindingTypeName); 107 ParameterSpec parameter = 108 ParameterSpec.builder(membersInjectedType.getTypeName(), "instance").build(); 109 MethodSpec.Builder methodBuilder = 110 methodBuilder(methodName) 111 .addModifiers(PRIVATE) 112 .returns(membersInjectedType.getTypeName()) 113 .addParameter(parameter); 114 XTypeElement canIgnoreReturnValue = 115 processingEnv.findTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue"); 116 if (canIgnoreReturnValue != null) { 117 methodBuilder.addAnnotation(canIgnoreReturnValue.getClassName()); 118 } 119 CodeBlock instance = CodeBlock.of("$N", parameter); 120 methodBuilder.addCode( 121 InjectionSiteMethod.invokeAll( 122 injectionSites(binding), 123 shardImplementation.name(), 124 instance, 125 membersInjectedType, 126 request -> 127 bindingExpressions 128 .getDependencyArgumentExpression(request, shardImplementation.name()) 129 .codeBlock())); 130 methodBuilder.addStatement("return $L", instance); 131 132 MethodSpec method = methodBuilder.build(); 133 shardImplementation.addMethod(MEMBERS_INJECTION_METHOD, method); 134 return Expression.create(membersInjectedType, CodeBlock.of("$N", method)); 135 } 136 injectionSites(Binding binding)137 private static ImmutableSet<InjectionSite> injectionSites(Binding binding) { 138 if (binding instanceof ProvisionBinding) { 139 return ((ProvisionBinding) binding).injectionSites(); 140 } else if (binding instanceof MembersInjectionBinding) { 141 return ((MembersInjectionBinding) binding).injectionSites(); 142 } 143 throw new IllegalArgumentException(binding.key().toString()); 144 } 145 } 146