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; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.squareup.javapoet.MethodSpec.methodBuilder; 21 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD; 22 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent; 23 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 24 import static javax.lang.model.element.Modifier.PRIVATE; 25 26 import com.google.common.collect.ImmutableSet; 27 import com.squareup.javapoet.ClassName; 28 import com.squareup.javapoet.CodeBlock; 29 import com.squareup.javapoet.MethodSpec; 30 import com.squareup.javapoet.ParameterSpec; 31 import com.squareup.javapoet.TypeName; 32 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod; 33 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite; 34 import dagger.internal.codegen.langmodel.DaggerElements; 35 import dagger.internal.codegen.langmodel.DaggerTypes; 36 import dagger.model.Key; 37 import java.util.LinkedHashMap; 38 import java.util.Map; 39 import javax.lang.model.element.Name; 40 import javax.lang.model.element.TypeElement; 41 import javax.lang.model.type.TypeMirror; 42 43 /** Manages the member injection methods for a component. */ 44 final class MembersInjectionMethods { 45 private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>(); 46 private final ComponentImplementation componentImplementation; 47 private final ComponentBindingExpressions bindingExpressions; 48 private final BindingGraph graph; 49 private final DaggerElements elements; 50 private final DaggerTypes types; 51 MembersInjectionMethods( ComponentImplementation componentImplementation, ComponentBindingExpressions bindingExpressions, BindingGraph graph, DaggerElements elements, DaggerTypes types)52 MembersInjectionMethods( 53 ComponentImplementation componentImplementation, 54 ComponentBindingExpressions bindingExpressions, 55 BindingGraph graph, 56 DaggerElements elements, 57 DaggerTypes types) { 58 this.componentImplementation = checkNotNull(componentImplementation); 59 this.bindingExpressions = checkNotNull(bindingExpressions); 60 this.graph = checkNotNull(graph); 61 this.elements = checkNotNull(elements); 62 this.types = checkNotNull(types); 63 } 64 65 /** 66 * Returns the members injection {@link MethodSpec} for the given {@link Key}, creating it if 67 * necessary. 68 */ getOrCreate(Key key)69 MethodSpec getOrCreate(Key key) { 70 return reentrantComputeIfAbsent(membersInjectionMethods, key, this::membersInjectionMethod); 71 } 72 membersInjectionMethod(Key key)73 private MethodSpec membersInjectionMethod(Key key) { 74 ResolvedBindings resolvedBindings = 75 graph.membersInjectionBindings().getOrDefault(key, graph.contributionBindings().get(key)); 76 Binding binding = resolvedBindings.binding(); 77 TypeMirror keyType = binding.key().type(); 78 TypeMirror membersInjectedType = 79 isTypeAccessibleFrom(keyType, componentImplementation.name().packageName()) 80 ? keyType 81 : elements.getTypeElement(Object.class).asType(); 82 TypeName membersInjectedTypeName = TypeName.get(membersInjectedType); 83 Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName(); 84 // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class 85 // simple names Foo.Builder -> injectFooBuilder 86 String methodName = componentImplementation.getUniqueMethodName("inject" + bindingTypeName); 87 ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build(); 88 MethodSpec.Builder methodBuilder = 89 methodBuilder(methodName) 90 .addModifiers(PRIVATE) 91 .returns(membersInjectedTypeName) 92 .addParameter(parameter); 93 TypeElement canIgnoreReturnValue = 94 elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue"); 95 if (canIgnoreReturnValue != null) { 96 methodBuilder.addAnnotation(ClassName.get(canIgnoreReturnValue)); 97 } 98 CodeBlock instance = CodeBlock.of("$N", parameter); 99 methodBuilder.addCode( 100 InjectionSiteMethod.invokeAll( 101 injectionSites(binding), 102 componentImplementation.name(), 103 instance, 104 membersInjectedType, 105 types, 106 request -> 107 bindingExpressions 108 .getDependencyArgumentExpression(request, componentImplementation.name()) 109 .codeBlock(), 110 elements)); 111 methodBuilder.addStatement("return $L", instance); 112 113 MethodSpec method = methodBuilder.build(); 114 componentImplementation.addMethod(MEMBERS_INJECTION_METHOD, method); 115 return method; 116 } 117 injectionSites(Binding binding)118 private static ImmutableSet<InjectionSite> injectionSites(Binding binding) { 119 if (binding instanceof ProvisionBinding) { 120 return ((ProvisionBinding) binding).injectionSites(); 121 } else if (binding instanceof MembersInjectionBinding) { 122 return ((MembersInjectionBinding) binding).injectionSites(); 123 } 124 throw new IllegalArgumentException(binding.key().toString()); 125 } 126 } 127