• 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.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