1 /* 2 * Copyright (C) 2014 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.binding; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkState; 21 import static com.google.common.collect.Iterables.getOnlyElement; 22 import static dagger.internal.codegen.base.ProducerAnnotations.productionImplementationQualifier; 23 import static dagger.internal.codegen.base.ProducerAnnotations.productionQualifier; 24 import static dagger.internal.codegen.base.RequestKinds.extractKeyType; 25 import static dagger.internal.codegen.binding.MapKeys.getMapKey; 26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 27 import static dagger.internal.codegen.javapoet.TypeNames.isFutureType; 28 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 29 import static dagger.internal.codegen.xprocessing.XTypes.unwrapType; 30 31 import androidx.room.compiler.processing.XAnnotation; 32 import androidx.room.compiler.processing.XMethodElement; 33 import androidx.room.compiler.processing.XMethodType; 34 import androidx.room.compiler.processing.XProcessingEnv; 35 import androidx.room.compiler.processing.XType; 36 import androidx.room.compiler.processing.XTypeElement; 37 import com.squareup.javapoet.ClassName; 38 import dagger.Binds; 39 import dagger.BindsOptionalOf; 40 import dagger.internal.codegen.base.ContributionType; 41 import dagger.internal.codegen.base.FrameworkTypes; 42 import dagger.internal.codegen.base.MapType; 43 import dagger.internal.codegen.base.OptionalType; 44 import dagger.internal.codegen.base.RequestKinds; 45 import dagger.internal.codegen.base.SetType; 46 import dagger.internal.codegen.compileroption.CompilerOptions; 47 import dagger.internal.codegen.javapoet.TypeNames; 48 import dagger.internal.codegen.model.DaggerAnnotation; 49 import dagger.internal.codegen.model.DaggerExecutableElement; 50 import dagger.internal.codegen.model.DaggerType; 51 import dagger.internal.codegen.model.DaggerTypeElement; 52 import dagger.internal.codegen.model.Key; 53 import dagger.internal.codegen.model.RequestKind; 54 import dagger.internal.codegen.xprocessing.XAnnotations; 55 import dagger.multibindings.Multibinds; 56 import java.util.Optional; 57 import javax.inject.Inject; 58 59 /** A factory for {@link Key}s. */ 60 public final class KeyFactory { 61 private final XProcessingEnv processingEnv; 62 private final CompilerOptions compilerOptions; 63 private final InjectionAnnotations injectionAnnotations; 64 65 @Inject KeyFactory( XProcessingEnv processingEnv, CompilerOptions compilerOptions, InjectionAnnotations injectionAnnotations)66 KeyFactory( 67 XProcessingEnv processingEnv, 68 CompilerOptions compilerOptions, 69 InjectionAnnotations injectionAnnotations) { 70 this.processingEnv = processingEnv; 71 this.compilerOptions = compilerOptions; 72 this.injectionAnnotations = injectionAnnotations; 73 } 74 setOf(XType elementType)75 private XType setOf(XType elementType) { 76 return processingEnv.getDeclaredType( 77 processingEnv.requireTypeElement(TypeNames.SET), elementType.boxed()); 78 } 79 mapOf(XType keyType, XType valueType)80 private XType mapOf(XType keyType, XType valueType) { 81 return processingEnv.getDeclaredType( 82 processingEnv.requireTypeElement(TypeNames.MAP), keyType.boxed(), valueType.boxed()); 83 } 84 85 /** 86 * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same 87 * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, XType)} 88 * extracted} from {@code T}. 89 */ optionalOf(Key key)90 Key optionalOf(Key key) { 91 return key.withType(DaggerType.from(optionalOf(key.type().xprocessing()))); 92 } 93 optionalOf(XType type)94 private XType optionalOf(XType type) { 95 return processingEnv.getDeclaredType( 96 processingEnv.requireTypeElement(TypeNames.JDK_OPTIONAL), type.boxed()); 97 } 98 99 /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */ mapOfFrameworkType(XType keyType, ClassName frameworkClassName, XType valueType)100 private XType mapOfFrameworkType(XType keyType, ClassName frameworkClassName, XType valueType) { 101 checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName)); 102 return mapOf( 103 keyType, 104 processingEnv.getDeclaredType( 105 processingEnv.requireTypeElement(frameworkClassName), valueType.boxed())); 106 } 107 forComponentMethod(XMethodElement componentMethod)108 Key forComponentMethod(XMethodElement componentMethod) { 109 return forMethod(componentMethod, componentMethod.getReturnType()); 110 } 111 forProductionComponentMethod(XMethodElement componentMethod)112 Key forProductionComponentMethod(XMethodElement componentMethod) { 113 XType returnType = componentMethod.getReturnType(); 114 XType keyType = 115 isFutureType(returnType) ? getOnlyElement(returnType.getTypeArguments()) : returnType; 116 return forMethod(componentMethod, keyType); 117 } 118 forSubcomponentCreatorMethod( XMethodElement subcomponentCreatorMethod, XType declaredContainer)119 Key forSubcomponentCreatorMethod( 120 XMethodElement subcomponentCreatorMethod, XType declaredContainer) { 121 checkArgument(isDeclared(declaredContainer)); 122 XMethodType resolvedMethod = subcomponentCreatorMethod.asMemberOf(declaredContainer); 123 return forType(resolvedMethod.getReturnType()); 124 } 125 forSubcomponentCreator(XType creatorType)126 public Key forSubcomponentCreator(XType creatorType) { 127 return forType(creatorType); 128 } 129 forProvidesMethod(XMethodElement method, XTypeElement contributingModule)130 public Key forProvidesMethod(XMethodElement method, XTypeElement contributingModule) { 131 checkArgument(method.hasAnnotation(TypeNames.PROVIDES)); 132 return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PROVIDER)); 133 } 134 forProducesMethod(XMethodElement method, XTypeElement contributingModule)135 public Key forProducesMethod(XMethodElement method, XTypeElement contributingModule) { 136 checkArgument(method.hasAnnotation(TypeNames.PRODUCES)); 137 return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PRODUCER)); 138 } 139 140 /** Returns the key bound by a {@link Binds} method. */ forBindsMethod(XMethodElement method, XTypeElement contributingModule)141 Key forBindsMethod(XMethodElement method, XTypeElement contributingModule) { 142 checkArgument(method.hasAnnotation(TypeNames.BINDS)); 143 return forBindingMethod(method, contributingModule, Optional.empty()); 144 } 145 146 /** Returns the base key bound by a {@link BindsOptionalOf} method. */ forBindsOptionalOfMethod(XMethodElement method, XTypeElement contributingModule)147 Key forBindsOptionalOfMethod(XMethodElement method, XTypeElement contributingModule) { 148 checkArgument(method.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF)); 149 return forBindingMethod(method, contributingModule, Optional.empty()); 150 } 151 forBindingMethod( XMethodElement method, XTypeElement contributingModule, Optional<ClassName> frameworkClassName)152 private Key forBindingMethod( 153 XMethodElement method, 154 XTypeElement contributingModule, 155 Optional<ClassName> frameworkClassName) { 156 XMethodType methodType = method.asMemberOf(contributingModule.getType()); 157 ContributionType contributionType = ContributionType.fromBindingElement(method); 158 XType returnType = methodType.getReturnType(); 159 if (frameworkClassName.isPresent() && frameworkClassName.get().equals(TypeNames.PRODUCER)) { 160 if (isFutureType(returnType)) { 161 returnType = getOnlyElement(returnType.getTypeArguments()); 162 } else if (contributionType.equals(ContributionType.SET_VALUES) 163 && SetType.isSet(returnType)) { 164 SetType setType = SetType.from(returnType); 165 if (isFutureType(setType.elementType())) { 166 returnType = setOf(unwrapType(setType.elementType())); 167 } 168 } 169 } 170 XType keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkClassName); 171 Key key = forMethod(method, keyType); 172 return contributionType.equals(ContributionType.UNIQUE) 173 ? key 174 : key.withMultibindingContributionIdentifier( 175 DaggerTypeElement.from(contributingModule), DaggerExecutableElement.from(method)); 176 } 177 178 /** 179 * Returns the key for a {@link Multibinds @Multibinds} method. 180 * 181 * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works 182 * even for maps used by {@code Producer}s. 183 */ forMultibindsMethod(XMethodElement method, XMethodType methodType)184 Key forMultibindsMethod(XMethodElement method, XMethodType methodType) { 185 XType returnType = method.getReturnType(); 186 XType keyType = 187 MapType.isMap(returnType) 188 ? mapOfFrameworkType( 189 MapType.from(returnType).keyType(), 190 TypeNames.PROVIDER, 191 MapType.from(returnType).valueType()) 192 : returnType; 193 return forMethod(method, keyType); 194 } 195 bindingMethodKeyType( XType returnType, XMethodElement method, ContributionType contributionType, Optional<ClassName> frameworkClassName)196 private XType bindingMethodKeyType( 197 XType returnType, 198 XMethodElement method, 199 ContributionType contributionType, 200 Optional<ClassName> frameworkClassName) { 201 switch (contributionType) { 202 case UNIQUE: 203 return returnType; 204 case SET: 205 return setOf(returnType); 206 case MAP: 207 Optional<XType> mapKeyType = getMapKey(method).map(MapKeys::mapKeyType); 208 // TODO(bcorso): We've added a special checkState here since a number of people have run 209 // into this particular case, but technically it shouldn't be necessary if we are properly 210 // doing superficial validation and deferring on unresolvable types. We should revisit 211 // whether this is necessary once we're able to properly defer this case. 212 checkState( 213 mapKeyType.isPresent(), 214 "Missing map key annotation for method: %s#%s. That method was annotated with: %s. If a" 215 + " map key annotation is included in that list, it means Dagger wasn't able to" 216 + " detect that it was a map key because the dependency is missing from the" 217 + " classpath of the current build. To fix, add a dependency for the map key to the" 218 + " current build. For more details, see" 219 + " https://github.com/google/dagger/issues/3133#issuecomment-1002790894.", 220 method.getEnclosingElement(), 221 method, 222 method.getAllAnnotations().stream() 223 .map(XAnnotations::toString) 224 .collect(toImmutableList())); 225 return (frameworkClassName.isPresent() 226 && compilerOptions.useFrameworkTypeInMapMultibindingContributionKey()) 227 ? mapOfFrameworkType(mapKeyType.get(), frameworkClassName.get(), returnType) 228 : mapOf(mapKeyType.get(), returnType); 229 case SET_VALUES: 230 // TODO(gak): do we want to allow people to use "covariant return" here? 231 checkArgument(SetType.isSet(returnType)); 232 return returnType; 233 } 234 throw new AssertionError(); 235 } 236 237 /** 238 * Returns the key for a binding associated with a {@link DelegateDeclaration}. 239 * 240 * <p>If {@code delegateDeclaration} is a multibinding map contribution and 241 * {@link CompilerOptions#useFrameworkTypeInMapMultibindingContributionKey()} is enabled, then 242 * transforms the {@code Map<K, V>} key into {@code Map<K, FrameworkType<V>>}, otherwise returns 243 * the unaltered key. 244 */ forDelegateBinding(DelegateDeclaration delegateDeclaration, ClassName frameworkType)245 Key forDelegateBinding(DelegateDeclaration delegateDeclaration, ClassName frameworkType) { 246 return delegateDeclaration.contributionType().equals(ContributionType.MAP) 247 && compilerOptions.useFrameworkTypeInMapMultibindingContributionKey() 248 ? wrapMapValue(delegateDeclaration.key(), frameworkType) 249 : delegateDeclaration.key(); 250 } 251 forMethod(XMethodElement method, XType keyType)252 private Key forMethod(XMethodElement method, XType keyType) { 253 return forQualifiedType(injectionAnnotations.getQualifier(method), keyType); 254 } 255 forInjectConstructorWithResolvedType(XType type)256 public Key forInjectConstructorWithResolvedType(XType type) { 257 return forType(type); 258 } 259 forType(XType type)260 Key forType(XType type) { 261 return Key.builder(DaggerType.from(type)).build(); 262 } 263 forMembersInjectedType(XType type)264 public Key forMembersInjectedType(XType type) { 265 return forType(type); 266 } 267 forQualifiedType(Optional<XAnnotation> qualifier, XType type)268 Key forQualifiedType(Optional<XAnnotation> qualifier, XType type) { 269 return Key.builder(DaggerType.from(type.boxed())) 270 .qualifier(qualifier.map(DaggerAnnotation::from)) 271 .build(); 272 } 273 forProductionExecutor()274 public Key forProductionExecutor() { 275 return Key.builder(DaggerType.from(processingEnv.requireType(TypeNames.EXECUTOR))) 276 .qualifier(DaggerAnnotation.from(productionQualifier(processingEnv))) 277 .build(); 278 } 279 forProductionImplementationExecutor()280 public Key forProductionImplementationExecutor() { 281 return Key.builder(DaggerType.from(processingEnv.requireType(TypeNames.EXECUTOR))) 282 .qualifier(DaggerAnnotation.from(productionImplementationQualifier(processingEnv))) 283 .build(); 284 } 285 forProductionComponentMonitor()286 public Key forProductionComponentMonitor() { 287 return forType(processingEnv.requireType(TypeNames.PRODUCTION_COMPONENT_MONITOR)); 288 } 289 290 /** 291 * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code 292 * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link 293 * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}. 294 * 295 * <p>Otherwise, returns {@code key}. 296 */ unwrapMapValueType(Key key)297 public Key unwrapMapValueType(Key key) { 298 if (MapType.isMap(key)) { 299 MapType mapType = MapType.from(key); 300 if (!mapType.isRawType() && mapType.valuesAreFrameworkType()) { 301 return key.withType( 302 DaggerType.from(mapOf(mapType.keyType(), mapType.unwrappedFrameworkValueType()))); 303 } 304 } 305 return key; 306 } 307 308 /** 309 * Returns a key with the type {@code Map<K, FrameworkType<V>>} if the given key has a type of 310 * {@code Map<K, V>}. Otherwise, returns the unaltered key. 311 * 312 * @throws IllegalArgumentException if the {@code frameworkClassName} is not a valid framework 313 * type for multibinding maps. 314 * @throws IllegalStateException if the {@code key} is already wrapped in a (different) framework 315 * type. 316 */ wrapMapValue(Key key, ClassName frameworkClassName)317 private Key wrapMapValue(Key key, ClassName frameworkClassName) { 318 checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName)); 319 if (MapType.isMap(key)) { 320 MapType mapType = MapType.from(key); 321 if (!mapType.isRawType() && !mapType.valuesAreTypeOf(frameworkClassName)) { 322 checkState(!mapType.valuesAreFrameworkType()); 323 XTypeElement frameworkTypeElement = processingEnv.findTypeElement(frameworkClassName); 324 if (frameworkTypeElement == null) { 325 // This target might not be compiled with Producers, so wrappingClass might not have an 326 // associated element. 327 return key; 328 } 329 XType wrappedValueType = 330 processingEnv.getDeclaredType(frameworkTypeElement, mapType.valueType()); 331 return key.withType(DaggerType.from(mapOf(mapType.keyType(), wrappedValueType))); 332 } 333 } 334 return key; 335 } 336 337 /** 338 * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set 339 * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}. 340 */ unwrapSetKey(Key key, ClassName wrappingClassName)341 Optional<Key> unwrapSetKey(Key key, ClassName wrappingClassName) { 342 if (SetType.isSet(key)) { 343 SetType setType = SetType.from(key); 344 if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClassName)) { 345 return Optional.of( 346 key.withType(DaggerType.from(setOf(setType.unwrappedElementType(wrappingClassName))))); 347 } 348 } 349 return Optional.empty(); 350 } 351 352 /** 353 * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same 354 * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, XType)} 355 * extracted} from {@code T}. 356 */ unwrapOptional(Key key)357 Optional<Key> unwrapOptional(Key key) { 358 if (!OptionalType.isOptional(key)) { 359 return Optional.empty(); 360 } 361 362 XType optionalValueType = OptionalType.from(key).valueType(); 363 return Optional.of(key.withType(DaggerType.from(extractKeyType(optionalValueType)))); 364 } 365 } 366