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