• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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