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