• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameterSpecs;
21 import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
22 import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.PRIVATE_METHOD_SCOPED_FIELD;
23 import static javax.lang.model.element.Modifier.PRIVATE;
24 import static javax.lang.model.element.Modifier.VOLATILE;
25 
26 import com.google.common.base.Supplier;
27 import com.google.common.base.Suppliers;
28 import com.squareup.javapoet.ClassName;
29 import com.squareup.javapoet.CodeBlock;
30 import com.squareup.javapoet.FieldSpec;
31 import com.squareup.javapoet.TypeName;
32 import dagger.internal.DoubleCheck;
33 import dagger.internal.MemoizedSentinel;
34 import dagger.internal.codegen.binding.BindingRequest;
35 import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
36 import dagger.internal.codegen.binding.ContributionBinding;
37 import dagger.internal.codegen.binding.FrameworkField;
38 import dagger.internal.codegen.binding.KeyVariableNamer;
39 import dagger.internal.codegen.javapoet.Expression;
40 import dagger.internal.codegen.langmodel.DaggerTypes;
41 import dagger.model.BindingKind;
42 import dagger.model.RequestKind;
43 import java.util.Optional;
44 import javax.lang.model.type.TypeMirror;
45 
46 /** A binding expression that wraps another in a nullary method on the component. */
47 abstract class MethodBindingExpression extends BindingExpression {
48   private final BindingRequest request;
49   private final ContributionBinding binding;
50   private final BindingMethodImplementation bindingMethodImplementation;
51   private final ComponentImplementation componentImplementation;
52   private final ProducerEntryPointView producerEntryPointView;
53   private final BindingExpression wrappedBindingExpression;
54   private final DaggerTypes types;
55 
MethodBindingExpression( BindingRequest request, ContributionBinding binding, MethodImplementationStrategy methodImplementationStrategy, BindingExpression wrappedBindingExpression, ComponentImplementation componentImplementation, DaggerTypes types)56   protected MethodBindingExpression(
57       BindingRequest request,
58       ContributionBinding binding,
59       MethodImplementationStrategy methodImplementationStrategy,
60       BindingExpression wrappedBindingExpression,
61       ComponentImplementation componentImplementation,
62       DaggerTypes types) {
63     this.request = checkNotNull(request);
64     this.binding = checkNotNull(binding);
65     this.bindingMethodImplementation = bindingMethodImplementation(methodImplementationStrategy);
66     this.wrappedBindingExpression = checkNotNull(wrappedBindingExpression);
67     this.componentImplementation = checkNotNull(componentImplementation);
68     this.producerEntryPointView = new ProducerEntryPointView(types);
69     this.types = checkNotNull(types);
70   }
71 
72   @Override
getDependencyExpression(ClassName requestingClass)73   Expression getDependencyExpression(ClassName requestingClass) {
74     if (request.frameworkType().isPresent()) {
75       // Initializing a framework instance that participates in a cycle requires that the underlying
76       // FrameworkInstanceBindingExpression is invoked in order for a cycle to be detected properly.
77       // When a MethodBindingExpression wraps a FrameworkInstanceBindingExpression, the wrapped
78       // expression will only be invoked once to implement the method body. This is a hack to work
79       // around that weirdness - methodImplementation.body() will invoke the framework instance
80       // initialization again in case the field is not fully initialized.
81       // TODO(b/121196706): use a less hacky approach to fix this bug
82       Object unused = methodBody();
83     }
84 
85     addMethod();
86 
87     CodeBlock methodCall =
88          binding.kind() == BindingKind.ASSISTED_INJECTION
89               // Private methods for assisted injection take assisted parameters as input.
90               ? CodeBlock.of(
91                   "$N($L)", methodName(), parameterNames(assistedParameterSpecs(binding, types)))
92               : CodeBlock.of("$N()", methodName());
93 
94     return Expression.create(
95         returnType(),
96         requestingClass.equals(componentImplementation.name())
97             ? methodCall
98             : CodeBlock.of("$L.$L", componentImplementation.externalReferenceBlock(), methodCall));
99   }
100 
101   @Override
getDependencyExpressionForComponentMethod(ComponentMethodDescriptor componentMethod, ComponentImplementation component)102   Expression getDependencyExpressionForComponentMethod(ComponentMethodDescriptor componentMethod,
103       ComponentImplementation component) {
104     return producerEntryPointView
105         .getProducerEntryPointField(this, componentMethod, component)
106         .orElseGet(
107             () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
108   }
109 
110   /** Adds the method to the component (if necessary) the first time it's called. */
addMethod()111   protected abstract void addMethod();
112 
113   /** Returns the name of the method to call. */
methodName()114   protected abstract String methodName();
115 
116   /** The method's body. */
methodBody()117   protected final CodeBlock methodBody() {
118     return implementation(
119         wrappedBindingExpression.getDependencyExpression(componentImplementation.name())
120             ::codeBlock);
121   }
122 
123   /** The method's body if this method is a component method. */
methodBodyForComponentMethod( ComponentMethodDescriptor componentMethod)124   protected final CodeBlock methodBodyForComponentMethod(
125       ComponentMethodDescriptor componentMethod) {
126     return implementation(
127         wrappedBindingExpression.getDependencyExpressionForComponentMethod(
128                 componentMethod, componentImplementation)
129             ::codeBlock);
130   }
131 
implementation(Supplier<CodeBlock> simpleBindingExpression)132   private CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
133     return bindingMethodImplementation.implementation(simpleBindingExpression);
134   }
135 
bindingMethodImplementation( MethodImplementationStrategy methodImplementationStrategy)136   private BindingMethodImplementation bindingMethodImplementation(
137       MethodImplementationStrategy methodImplementationStrategy) {
138     switch (methodImplementationStrategy) {
139       case SIMPLE:
140         return new SimpleMethodImplementation();
141       case SINGLE_CHECK:
142         return new SingleCheckedMethodImplementation();
143       case DOUBLE_CHECK:
144         return new DoubleCheckedMethodImplementation();
145     }
146     throw new AssertionError(methodImplementationStrategy);
147   }
148 
149   /** Returns the return type for the dependency request. */
returnType()150   protected TypeMirror returnType() {
151     if (request.isRequestKind(RequestKind.INSTANCE)
152         && binding.contributedPrimitiveType().isPresent()) {
153       return binding.contributedPrimitiveType().get();
154     }
155 
156     if (matchingComponentMethod().isPresent()) {
157       // Component methods are part of the user-defined API, and thus we must use the user-defined
158       // type.
159       return matchingComponentMethod().get().resolvedReturnType(types);
160     }
161 
162     TypeMirror requestedType = request.requestedType(binding.contributedType(), types);
163     return types.accessibleType(requestedType, componentImplementation.name());
164   }
165 
matchingComponentMethod()166   private Optional<ComponentMethodDescriptor> matchingComponentMethod() {
167     return componentImplementation.componentDescriptor().firstMatchingComponentMethod(request);
168   }
169 
170   /** Strateg for implementing the body of this method. */
171   enum MethodImplementationStrategy {
172     SIMPLE,
173     SINGLE_CHECK,
174     DOUBLE_CHECK,
175     ;
176   }
177 
178   private abstract static class BindingMethodImplementation {
179     /**
180      * Returns the method body, which contains zero or more statements (including semicolons).
181      *
182      * <p>If the implementation has a non-void return type, the body will also include the {@code
183      * return} statement.
184      *
185      * @param simpleBindingExpression the expression to retrieve an instance of this binding without
186      *     the wrapping method.
187      */
implementation(Supplier<CodeBlock> simpleBindingExpression)188     abstract CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression);
189   }
190 
191   /** Returns the {@code wrappedBindingExpression} directly. */
192   private static final class SimpleMethodImplementation extends BindingMethodImplementation {
193     @Override
implementation(Supplier<CodeBlock> simpleBindingExpression)194     CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
195       return CodeBlock.of("return $L;", simpleBindingExpression.get());
196     }
197   }
198 
199   /**
200    * Defines a method body for single checked caching of the given {@code wrappedBindingExpression}.
201    */
202   private final class SingleCheckedMethodImplementation extends BindingMethodImplementation {
203     private final Supplier<FieldSpec> field = Suppliers.memoize(this::createField);
204 
205     @Override
implementation(Supplier<CodeBlock> simpleBindingExpression)206     CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
207       String fieldExpression = field.get().name.equals("local") ? "this.local" : field.get().name;
208 
209       CodeBlock.Builder builder = CodeBlock.builder()
210           .addStatement("Object local = $N", fieldExpression);
211 
212       if (isNullable()) {
213         builder.beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class);
214       } else {
215         builder.beginControlFlow("if (local == null)");
216       }
217 
218       return builder
219           .addStatement("local = $L", simpleBindingExpression.get())
220           .addStatement("$N = ($T) local", fieldExpression, returnType())
221           .endControlFlow()
222           .addStatement("return ($T) local", returnType())
223           .build();
224     }
225 
createField()226     FieldSpec createField() {
227       String name =
228           componentImplementation.getUniqueFieldName(
229               request.isRequestKind(RequestKind.INSTANCE)
230                   ? KeyVariableNamer.name(binding.key())
231                   : FrameworkField.forBinding(binding, Optional.empty()).name());
232 
233       FieldSpec.Builder builder = FieldSpec.builder(fieldType(), name, PRIVATE, VOLATILE);
234       if (isNullable()) {
235         builder.initializer("new $T()", MemoizedSentinel.class);
236       }
237 
238       FieldSpec field = builder.build();
239       componentImplementation.addField(PRIVATE_METHOD_SCOPED_FIELD, field);
240       return field;
241     }
242 
fieldType()243     TypeName fieldType() {
244       if (isNullable()) {
245         // Nullable instances use `MemoizedSentinel` instead of `null` as the initialization value,
246         // so the field type must accept that and the return type
247         return TypeName.OBJECT;
248       }
249       TypeName returnType = TypeName.get(returnType());
250       return returnType.isPrimitive() ? returnType.box() : returnType;
251     }
252 
isNullable()253     private boolean isNullable() {
254       return request.isRequestKind(RequestKind.INSTANCE) && binding.isNullable();
255     }
256   }
257 
258   /**
259    * Defines a method body for double checked caching of the given {@code wrappedBindingExpression}.
260    */
261   private final class DoubleCheckedMethodImplementation extends BindingMethodImplementation {
262     private final Supplier<String> fieldName = Suppliers.memoize(this::createField);
263 
264     @Override
implementation(Supplier<CodeBlock> simpleBindingExpression)265     CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
266       String fieldExpression = fieldName.get().equals("local") ? "this.local" : fieldName.get();
267       return CodeBlock.builder()
268           .addStatement("$T local = $L", TypeName.OBJECT, fieldExpression)
269           .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
270           .beginControlFlow("synchronized (local)")
271           .addStatement("local = $L", fieldExpression)
272           .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
273           .addStatement("local = $L", simpleBindingExpression.get())
274           .addStatement("$1L = $2T.reentrantCheck($1L, local)", fieldExpression, DoubleCheck.class)
275           .endControlFlow()
276           .endControlFlow()
277           .endControlFlow()
278           .addStatement("return ($T) local", returnType())
279           .build();
280     }
281 
createField()282     private String createField() {
283       String name =
284           componentImplementation.getUniqueFieldName(KeyVariableNamer.name(binding.key()));
285       componentImplementation.addField(
286           PRIVATE_METHOD_SCOPED_FIELD,
287           FieldSpec.builder(TypeName.OBJECT, name, PRIVATE, VOLATILE)
288               .initializer("new $T()", MemoizedSentinel.class)
289               .build());
290       return name;
291     }
292   }
293 
294 }
295