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