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 androidx.room.compiler.processing.XElementKt.isConstructor; 20 import static androidx.room.compiler.processing.XElementKt.isField; 21 import static androidx.room.compiler.processing.XElementKt.isMethod; 22 import static androidx.room.compiler.processing.XElementKt.isMethodParameter; 23 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 24 import static com.google.common.base.Preconditions.checkNotNull; 25 import static com.google.common.base.Preconditions.checkState; 26 import static com.google.common.collect.Iterables.getOnlyElement; 27 import static dagger.internal.codegen.binding.SourceFiles.factoryNameForElement; 28 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable; 29 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 30 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional; 31 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 32 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 33 import static dagger.internal.codegen.xprocessing.XElements.asField; 34 import static dagger.internal.codegen.xprocessing.XElements.asMethod; 35 import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter; 36 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 37 import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement; 38 39 import androidx.room.compiler.processing.XAnnotation; 40 import androidx.room.compiler.processing.XConstructorElement; 41 import androidx.room.compiler.processing.XElement; 42 import androidx.room.compiler.processing.XExecutableElement; 43 import androidx.room.compiler.processing.XFieldElement; 44 import androidx.room.compiler.processing.XProcessingEnv; 45 import androidx.room.compiler.processing.XTypeElement; 46 import com.google.common.collect.ImmutableList; 47 import com.google.common.collect.ImmutableSet; 48 import com.squareup.javapoet.ClassName; 49 import dagger.internal.codegen.base.DaggerSuperficialValidation; 50 import dagger.internal.codegen.base.ElementFormatter; 51 import dagger.internal.codegen.compileroption.CompilerOptions; 52 import dagger.internal.codegen.javapoet.TypeNames; 53 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 54 import dagger.internal.codegen.model.DaggerAnnotation; 55 import dagger.internal.codegen.model.Scope; 56 import dagger.internal.codegen.xprocessing.XAnnotations; 57 import java.util.Optional; 58 import java.util.stream.Stream; 59 import javax.inject.Inject; 60 61 /** Utilities relating to annotations defined in the {@code javax.inject} package. */ 62 public final class InjectionAnnotations { 63 private final XProcessingEnv processingEnv; 64 private final KotlinMetadataUtil kotlinMetadataUtil; 65 private final DaggerSuperficialValidation superficialValidation; 66 private final CompilerOptions compilerOptions; 67 68 @Inject InjectionAnnotations( XProcessingEnv processingEnv, KotlinMetadataUtil kotlinMetadataUtil, DaggerSuperficialValidation superficialValidation, CompilerOptions compilerOptions)69 InjectionAnnotations( 70 XProcessingEnv processingEnv, 71 KotlinMetadataUtil kotlinMetadataUtil, 72 DaggerSuperficialValidation superficialValidation, 73 CompilerOptions compilerOptions) { 74 this.processingEnv = processingEnv; 75 this.kotlinMetadataUtil = kotlinMetadataUtil; 76 this.superficialValidation = superficialValidation; 77 this.compilerOptions = compilerOptions; 78 } 79 80 /** 81 * Returns the scope on the given element if it exists. 82 * 83 * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary 84 * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be 85 * superficially validated before we can determine if they are scopes or not. 86 * 87 * @throws IllegalArgumentException if the given element has more than one scope. 88 */ getScope(XElement element)89 public Optional<Scope> getScope(XElement element) { 90 return getScopes(element).stream().collect(toOptional()); 91 } 92 93 /** 94 * Returns the scopes on the given element, or an empty set if none exist. 95 * 96 * <p>Note: Use {@link #getScope(XElement)} if the usage of the scope on the given element has 97 * already been validated and known to be unique. This method should typically only be used in the 98 * process of such validation. 99 * 100 * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary 101 * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be 102 * superficially validated before we can determine if they are scopes or not. 103 */ getScopes(XElement element)104 public ImmutableSet<Scope> getScopes(XElement element) { 105 superficialValidation.validateTypeOf(element); 106 ImmutableSet<Scope> scopes = 107 getScopesWithMetadata(element).orElseGet(() -> getScopesWithoutMetadata(element)); 108 109 // Fully validate each scope to ensure its values are also valid. 110 scopes.stream() 111 .map(scope -> scope.scopeAnnotation().xprocessing()) 112 .forEach(scope -> superficialValidation.validateAnnotationOf(element, scope)); 113 return scopes; 114 } 115 getScopesWithoutMetadata(XElement element)116 private ImmutableSet<Scope> getScopesWithoutMetadata(XElement element) { 117 // Validate the annotation types before we check for @Scope, otherwise the @Scope 118 // annotation may appear to be missing (b/213880825). 119 superficialValidation.validateAnnotationTypesOf(element); 120 return element.getAllAnnotations().stream() 121 .filter(InjectionAnnotations::hasScopeAnnotation) 122 .map(DaggerAnnotation::from) 123 .map(Scope::scope) 124 .collect(toImmutableSet()); 125 } 126 getScopesWithMetadata(XElement element)127 private Optional<ImmutableSet<Scope>> getScopesWithMetadata(XElement element) { 128 Optional<XAnnotation> scopeMetadata = getScopeMetadata(element); 129 if (!scopeMetadata.isPresent()) { 130 return Optional.empty(); 131 } 132 String scopeName = scopeMetadata.get().getAsString("value"); 133 if (scopeName.isEmpty()) { 134 return Optional.of(ImmutableSet.of()); 135 } 136 ImmutableList<XAnnotation> scopeAnnotations = 137 element.getAllAnnotations().stream() 138 .filter( 139 annotation -> 140 scopeName.contentEquals( 141 annotation.getType().getTypeElement().getQualifiedName())) 142 .collect(toImmutableList()); 143 checkState( 144 scopeAnnotations.size() == 1, 145 "Expected %s to have a scope annotation for %s but found: %s", 146 ElementFormatter.elementToString(element), 147 scopeName, 148 scopeAnnotations.stream().map(XAnnotations::toStableString).collect(toImmutableList())); 149 XAnnotation scopeAnnotation = getOnlyElement(scopeAnnotations); 150 // Do superficial validation before we convert to a Scope, otherwise the @Scope annotation may 151 // appear to be missing from the annotation if it's no longer on the classpath. 152 superficialValidation.validateAnnotationTypeOf(element, scopeAnnotation); 153 154 // If strictSuperficialValidation is disabled, then we fall back to the old behavior where 155 // we may potentially miss a scope rather than report an exception. 156 if (compilerOptions.strictSuperficialValidation()) { 157 return Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation)))); 158 } else { 159 return Scope.isScope(DaggerAnnotation.from(scopeAnnotation)) 160 ? Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation)))) 161 : Optional.empty(); 162 } 163 } 164 getScopeMetadata(XElement element)165 private Optional<XAnnotation> getScopeMetadata(XElement element) { 166 return getGeneratedNameForScopeMetadata(element) 167 .flatMap(factoryName -> Optional.ofNullable(processingEnv.findTypeElement(factoryName))) 168 .flatMap(factory -> Optional.ofNullable(factory.getAnnotation(TypeNames.SCOPE_METADATA))); 169 } 170 getGeneratedNameForScopeMetadata(XElement element)171 private Optional<ClassName> getGeneratedNameForScopeMetadata(XElement element) { 172 // Currently, we only support ScopeMetadata for inject-constructor types and provides methods. 173 if (isTypeElement(element)) { 174 return asTypeElement(element).getConstructors().stream() 175 .filter(InjectionAnnotations::hasInjectOrAssistedInjectAnnotation) 176 .findFirst() 177 .map(SourceFiles::factoryNameForElement); 178 } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) { 179 return Optional.of(factoryNameForElement(asMethod(element))); 180 } 181 return Optional.empty(); 182 } 183 184 /** 185 * Returns the qualifier on the given element if it exists. 186 * 187 * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary 188 * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be 189 * superficially validated before we can determine if they are qualifiers or not. 190 * 191 * @throws IllegalArgumentException if the given element has more than one qualifier. 192 */ getQualifier(XElement element)193 public Optional<XAnnotation> getQualifier(XElement element) { 194 checkNotNull(element); 195 ImmutableSet<XAnnotation> qualifierAnnotations = getQualifiers(element); 196 switch (qualifierAnnotations.size()) { 197 case 0: 198 return Optional.empty(); 199 case 1: 200 return Optional.of(getOnlyElement(qualifierAnnotations)); 201 default: 202 throw new IllegalArgumentException( 203 element + " was annotated with more than one @Qualifier annotation"); 204 } 205 } 206 207 /* 208 * Returns the qualifiers on the given element, or an empty set if none exist. 209 * 210 * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary 211 * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be 212 * superficially validated before we can determine if they are qualifiers or not. 213 */ getQualifiers(XElement element)214 public ImmutableSet<XAnnotation> getQualifiers(XElement element) { 215 superficialValidation.validateTypeOf(element); 216 ImmutableSet<XAnnotation> qualifiers = 217 getQualifiersWithMetadata(element) 218 .orElseGet(() -> getQualifiersWithoutMetadata(element)); 219 220 if (isField(element)) { 221 XFieldElement field = asField(element); 222 // static/top-level injected fields are not supported, 223 // no need to get qualifier from kotlin metadata 224 if (!field.isStatic() 225 && isTypeElement(field.getEnclosingElement()) 226 && hasInjectAnnotation(field) 227 && kotlinMetadataUtil.hasMetadata(field)) { 228 qualifiers = 229 Stream.concat(qualifiers.stream(), getQualifiersForKotlinProperty(field).stream()) 230 .map(DaggerAnnotation::from) // Wrap in DaggerAnnotation to deduplicate 231 .distinct() 232 .map(DaggerAnnotation::xprocessing) 233 .collect(toImmutableSet()); 234 } 235 } 236 237 // Fully validate each qualifier to ensure its values are also valid. 238 qualifiers.forEach(qualifier -> superficialValidation.validateAnnotationOf(element, qualifier)); 239 240 return qualifiers; 241 } 242 getQualifiersWithoutMetadata(XElement element)243 private ImmutableSet<XAnnotation> getQualifiersWithoutMetadata(XElement element) { 244 // Validate the annotation types before we check for @Qualifier, otherwise the 245 // @Qualifier annotation may appear to be missing (b/213880825). 246 superficialValidation.validateAnnotationTypesOf(element); 247 return element.getAllAnnotations().stream() 248 .filter(InjectionAnnotations::hasQualifierAnnotation) 249 .collect(toImmutableSet()); 250 } 251 getQualifiersWithMetadata(XElement element)252 private Optional<ImmutableSet<XAnnotation>> getQualifiersWithMetadata(XElement element) { 253 Optional<XAnnotation> qualifierMetadata = getQualifierMetadata(element); 254 if (!qualifierMetadata.isPresent()) { 255 return Optional.empty(); 256 } 257 ImmutableSet<String> qualifierNames = 258 ImmutableSet.copyOf(qualifierMetadata.get().getAsStringList("value")); 259 if (qualifierNames.isEmpty()) { 260 return Optional.of(ImmutableSet.of()); 261 } 262 ImmutableSet<XAnnotation> qualifierAnnotations = 263 element.getAllAnnotations().stream() 264 .filter( 265 annotation -> 266 qualifierNames.contains( 267 annotation.getType().getTypeElement().getQualifiedName())) 268 .collect(toImmutableSet()); 269 if (qualifierAnnotations.isEmpty()) { 270 return Optional.of(ImmutableSet.of()); 271 } 272 // We should be guaranteed that there's exactly one qualifier since the existance of 273 // @QualifierMetadata means that this element has already been processed and multiple 274 // qualifiers would have been caught already. 275 XAnnotation qualifierAnnotation = getOnlyElement(qualifierAnnotations); 276 277 // Ensure the annotation type is superficially valid before we check for @Qualifier, otherwise 278 // the @Qualifier marker may appear to be missing from the annotation (b/213880825). 279 superficialValidation.validateAnnotationTypeOf(element, qualifierAnnotation); 280 if (compilerOptions.strictSuperficialValidation()) { 281 return Optional.of(ImmutableSet.of(qualifierAnnotation)); 282 } else { 283 // If strictSuperficialValidation is disabled, then we fall back to the old behavior where 284 // we may potentially miss a qualifier rather than report an exception. 285 return hasQualifierAnnotation(qualifierAnnotation) 286 ? Optional.of(ImmutableSet.of(qualifierAnnotation)) 287 : Optional.empty(); 288 } 289 } 290 291 /** 292 * Returns {@code QualifierMetadata} annotation. 293 * 294 * <p>Currently, {@code QualifierMetadata} is only associated with inject constructor parameters, 295 * inject fields, inject method parameters, provide methods, and provide method parameters. 296 */ getQualifierMetadata(XElement element)297 private Optional<XAnnotation> getQualifierMetadata(XElement element) { 298 return getGeneratedNameForQualifierMetadata(element) 299 .flatMap(name -> Optional.ofNullable(processingEnv.findTypeElement(name))) 300 .flatMap(type -> Optional.ofNullable(type.getAnnotation(TypeNames.QUALIFIER_METADATA))); 301 } 302 getGeneratedNameForQualifierMetadata(XElement element)303 private Optional<ClassName> getGeneratedNameForQualifierMetadata(XElement element) { 304 // Currently we only support @QualifierMetadata for @Inject fields, @Inject method parameters, 305 // @Inject constructor parameters, @Provides methods, and @Provides method parameters. 306 if (isField(element) && hasInjectAnnotation(element)) { 307 return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element))); 308 } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) { 309 return Optional.of(factoryNameForElement(asMethod(element))); 310 } else if (isMethodParameter(element)) { 311 XExecutableElement executableElement = asMethodParameter(element).getEnclosingElement(); 312 if (isConstructor(executableElement) 313 && hasInjectOrAssistedInjectAnnotation(executableElement)) { 314 return Optional.of(factoryNameForElement(executableElement)); 315 } 316 if (isMethod(executableElement) && hasInjectAnnotation(executableElement)) { 317 return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element))); 318 } 319 if (isMethod(executableElement) && executableElement.hasAnnotation(TypeNames.PROVIDES)) { 320 return Optional.of(factoryNameForElement(executableElement)); 321 } 322 } 323 return Optional.empty(); 324 } 325 326 /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */ injectedConstructors(XTypeElement type)327 public static ImmutableSet<XConstructorElement> injectedConstructors(XTypeElement type) { 328 return type.getConstructors().stream() 329 .filter(InjectionAnnotations::hasInjectAnnotation) 330 .collect(toImmutableSet()); 331 } 332 hasQualifierAnnotation(XAnnotation annotation)333 private static boolean hasQualifierAnnotation(XAnnotation annotation) { 334 return annotation 335 .getType() 336 .getTypeElement() 337 .hasAnyAnnotation(TypeNames.QUALIFIER, TypeNames.QUALIFIER_JAVAX); 338 } 339 hasScopeAnnotation(XAnnotation annotation)340 private static boolean hasScopeAnnotation(XAnnotation annotation) { 341 return annotation 342 .getType() 343 .getTypeElement() 344 .hasAnyAnnotation(TypeNames.SCOPE, TypeNames.SCOPE_JAVAX); 345 } 346 347 /** Returns true if the given element is annotated with {@link Inject}. */ hasInjectAnnotation(XElement element)348 public static boolean hasInjectAnnotation(XElement element) { 349 return element.hasAnyAnnotation(TypeNames.INJECT, TypeNames.INJECT_JAVAX); 350 } 351 352 /** Returns true if the given element is annotated with {@link Inject}. */ hasInjectOrAssistedInjectAnnotation(XElement element)353 public static boolean hasInjectOrAssistedInjectAnnotation(XElement element) { 354 return element.hasAnyAnnotation( 355 TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT); 356 } 357 358 /** 359 * Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding 360 * the synthetic method for annotations as described by the Kotlin metadata or finding the 361 * corresponding MembersInjector method for the field, which also contains the qualifier 362 * annotation. 363 */ getQualifiersForKotlinProperty(XFieldElement field)364 private ImmutableSet<XAnnotation> getQualifiersForKotlinProperty(XFieldElement field) { 365 // TODO(bcorso): Consider moving this to KotlinMetadataUtil 366 if (kotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(field)) { 367 // If we detect that the synthetic method for annotations is missing, possibly due to the 368 // element being from a compiled class, then find the MembersInjector that was generated 369 // for the enclosing class and extract the qualifier information from it. 370 XTypeElement membersInjector = 371 processingEnv.findTypeElement( 372 membersInjectorNameForType(asTypeElement(field.getEnclosingElement()))); 373 if (membersInjector != null) { 374 String memberInjectedFieldSignature = memberInjectedFieldSignatureForVariable(field); 375 // TODO(danysantiago): We have to iterate over all the injection methods for every qualifier 376 // look up. Making this N^2 when looking through all the injected fields. :( 377 return membersInjector.getDeclaredMethods().stream() 378 .filter( 379 method -> 380 Optional.ofNullable(method.getAnnotation(TypeNames.INJECTED_FIELD_SIGNATURE)) 381 .map(annotation -> annotation.getAsString("value")) 382 .map(memberInjectedFieldSignature::equals) 383 // If a method is not an @InjectedFieldSignature method then filter it out 384 .orElse(false)) 385 .collect(toOptional()) 386 .map(this::getQualifiers) 387 .orElseThrow( 388 () -> 389 new IllegalStateException( 390 String.format( 391 "No matching InjectedFieldSignature for %1$s. This likely means that " 392 + "%1$s was compiled with an older, incompatible version of " 393 + "Dagger. Please update all Dagger dependencies to the same " 394 + "version.", 395 memberInjectedFieldSignature))); 396 } else { 397 throw new IllegalStateException( 398 "No MembersInjector found for " + field.getEnclosingElement()); 399 } 400 } else { 401 return Stream.concat( 402 kotlinMetadataUtil 403 .getSyntheticPropertyAnnotations(field, TypeNames.QUALIFIER) 404 .stream(), 405 kotlinMetadataUtil 406 .getSyntheticPropertyAnnotations(field, TypeNames.QUALIFIER_JAVAX) 407 .stream()) 408 .collect(toImmutableSet()); 409 } 410 } 411 } 412