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.codegen.XClassName; 40 import androidx.room.compiler.processing.XAnnotation; 41 import androidx.room.compiler.processing.XConstructorElement; 42 import androidx.room.compiler.processing.XElement; 43 import androidx.room.compiler.processing.XExecutableElement; 44 import androidx.room.compiler.processing.XFieldElement; 45 import androidx.room.compiler.processing.XProcessingEnv; 46 import androidx.room.compiler.processing.XTypeElement; 47 import com.google.common.collect.ImmutableList; 48 import com.google.common.collect.ImmutableSet; 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 dagger.internal.codegen.xprocessing.XTypeNames; 58 import java.util.Optional; 59 import java.util.stream.Stream; 60 import javax.inject.Inject; 61 62 /** Utilities relating to annotations defined in the {@code javax.inject} package. */ 63 public final class InjectionAnnotations { 64 private final XProcessingEnv processingEnv; 65 private final KotlinMetadataUtil kotlinMetadataUtil; 66 private final DaggerSuperficialValidation superficialValidation; 67 private final CompilerOptions compilerOptions; 68 69 @Inject InjectionAnnotations( XProcessingEnv processingEnv, KotlinMetadataUtil kotlinMetadataUtil, DaggerSuperficialValidation superficialValidation, CompilerOptions compilerOptions)70 InjectionAnnotations( 71 XProcessingEnv processingEnv, 72 KotlinMetadataUtil kotlinMetadataUtil, 73 DaggerSuperficialValidation superficialValidation, 74 CompilerOptions compilerOptions) { 75 this.processingEnv = processingEnv; 76 this.kotlinMetadataUtil = kotlinMetadataUtil; 77 this.superficialValidation = superficialValidation; 78 this.compilerOptions = compilerOptions; 79 } 80 81 /** 82 * Returns the scope on the given element if it exists. 83 * 84 * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary 85 * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be 86 * superficially validated before we can determine if they are scopes or not. 87 * 88 * @throws IllegalArgumentException if the given element has more than one scope. 89 */ getScope(XElement element)90 public Optional<Scope> getScope(XElement element) { 91 return getScopes(element).stream().collect(toOptional()); 92 } 93 94 /** 95 * Returns the scopes on the given element, or an empty set if none exist. 96 * 97 * <p>Note: Use {@link #getScope(XElement)} if the usage of the scope on the given element has 98 * already been validated and known to be unique. This method should typically only be used in the 99 * process of such validation. 100 * 101 * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary 102 * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be 103 * superficially validated before we can determine if they are scopes or not. 104 */ getScopes(XElement element)105 public ImmutableSet<Scope> getScopes(XElement element) { 106 superficialValidation.validateTypeOf(element); 107 ImmutableSet<Scope> scopes = 108 getScopesWithMetadata(element).orElseGet(() -> getScopesWithoutMetadata(element)); 109 110 // Fully validate each scope to ensure its values are also valid. 111 scopes.stream() 112 .map(scope -> scope.scopeAnnotation().xprocessing()) 113 .forEach(scope -> superficialValidation.validateAnnotationOf(element, scope)); 114 return scopes; 115 } 116 getScopesWithoutMetadata(XElement element)117 private ImmutableSet<Scope> getScopesWithoutMetadata(XElement element) { 118 // Validate the annotation types before we check for @Scope, otherwise the @Scope 119 // annotation may appear to be missing (b/213880825). 120 superficialValidation.validateAnnotationTypesOf(element); 121 return element.getAllAnnotations().stream() 122 .filter(InjectionAnnotations::hasScopeAnnotation) 123 .map(DaggerAnnotation::from) 124 .map(Scope::scope) 125 .collect(toImmutableSet()); 126 } 127 getScopesWithMetadata(XElement element)128 private Optional<ImmutableSet<Scope>> getScopesWithMetadata(XElement element) { 129 Optional<XAnnotation> scopeMetadata = getScopeMetadata(element); 130 if (!scopeMetadata.isPresent()) { 131 return Optional.empty(); 132 } 133 String scopeName = scopeMetadata.get().getAsString("value"); 134 if (scopeName.isEmpty()) { 135 return Optional.of(ImmutableSet.of()); 136 } 137 ImmutableList<XAnnotation> scopeAnnotations = 138 element.getAllAnnotations().stream() 139 .filter( 140 annotation -> 141 scopeName.contentEquals( 142 annotation.getType().getTypeElement().getQualifiedName())) 143 .collect(toImmutableList()); 144 checkState( 145 scopeAnnotations.size() == 1, 146 "Expected %s to have a scope annotation for %s but found: %s", 147 ElementFormatter.elementToString(element), 148 scopeName, 149 scopeAnnotations.stream().map(XAnnotations::toStableString).collect(toImmutableList())); 150 XAnnotation scopeAnnotation = getOnlyElement(scopeAnnotations); 151 // Do superficial validation before we convert to a Scope, otherwise the @Scope annotation may 152 // appear to be missing from the annotation if it's no longer on the classpath. 153 superficialValidation.validateAnnotationTypeOf(element, scopeAnnotation); 154 155 // If strictSuperficialValidation is disabled, then we fall back to the old behavior where 156 // we may potentially miss a scope rather than report an exception. 157 if (compilerOptions.strictSuperficialValidation()) { 158 return Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation)))); 159 } else { 160 return Scope.isScope(DaggerAnnotation.from(scopeAnnotation)) 161 ? Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation)))) 162 : Optional.empty(); 163 } 164 } 165 getScopeMetadata(XElement element)166 private Optional<XAnnotation> getScopeMetadata(XElement element) { 167 return getGeneratedNameForScopeMetadata(element) 168 .flatMap(factoryName -> Optional.ofNullable(processingEnv.findTypeElement(factoryName))) 169 .flatMap(factory -> Optional.ofNullable(factory.getAnnotation(XTypeNames.SCOPE_METADATA))); 170 } 171 getGeneratedNameForScopeMetadata(XElement element)172 private Optional<XClassName> getGeneratedNameForScopeMetadata(XElement element) { 173 // Currently, we only support ScopeMetadata for inject-constructor types and provides methods. 174 if (isTypeElement(element)) { 175 return asTypeElement(element).getConstructors().stream() 176 .filter(InjectionAnnotations::hasInjectOrAssistedInjectAnnotation) 177 .findFirst() 178 .map(SourceFiles::factoryNameForElement); 179 } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) { 180 return Optional.of(factoryNameForElement(asMethod(element))); 181 } 182 return Optional.empty(); 183 } 184 185 /** 186 * Returns the qualifier on the given element if it exists. 187 * 188 * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary 189 * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be 190 * superficially validated before we can determine if they are qualifiers or not. 191 * 192 * @throws IllegalArgumentException if the given element has more than one qualifier. 193 */ getQualifier(XElement element)194 public Optional<XAnnotation> getQualifier(XElement element) { 195 checkNotNull(element); 196 ImmutableSet<XAnnotation> qualifierAnnotations = getQualifiers(element); 197 switch (qualifierAnnotations.size()) { 198 case 0: 199 return Optional.empty(); 200 case 1: 201 return Optional.of(getOnlyElement(qualifierAnnotations)); 202 default: 203 throw new IllegalArgumentException( 204 element + " was annotated with more than one @Qualifier annotation"); 205 } 206 } 207 208 /* 209 * Returns the qualifiers on the given element, or an empty set if none exist. 210 * 211 * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary 212 * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be 213 * superficially validated before we can determine if they are qualifiers or not. 214 */ getQualifiers(XElement element)215 public ImmutableSet<XAnnotation> getQualifiers(XElement element) { 216 superficialValidation.validateTypeOf(element); 217 ImmutableSet<XAnnotation> qualifiers = 218 getQualifiersWithMetadata(element) 219 .orElseGet(() -> getQualifiersWithoutMetadata(element)); 220 221 if (isField(element)) { 222 XFieldElement field = asField(element); 223 // static/top-level injected fields are not supported, 224 // no need to get qualifier from kotlin metadata 225 if (!field.isStatic() 226 && isTypeElement(field.getEnclosingElement()) 227 && hasInjectAnnotation(field) 228 && kotlinMetadataUtil.hasMetadata(field)) { 229 qualifiers = 230 Stream.concat(qualifiers.stream(), getQualifiersForKotlinProperty(field).stream()) 231 .map(DaggerAnnotation::from) // Wrap in DaggerAnnotation to deduplicate 232 .distinct() 233 .map(DaggerAnnotation::xprocessing) 234 .collect(toImmutableSet()); 235 } 236 } 237 238 // Fully validate each qualifier to ensure its values are also valid. 239 qualifiers.forEach(qualifier -> superficialValidation.validateAnnotationOf(element, qualifier)); 240 241 return qualifiers; 242 } 243 getQualifiersWithoutMetadata(XElement element)244 private ImmutableSet<XAnnotation> getQualifiersWithoutMetadata(XElement element) { 245 // Validate the annotation types before we check for @Qualifier, otherwise the 246 // @Qualifier annotation may appear to be missing (b/213880825). 247 superficialValidation.validateAnnotationTypesOf(element); 248 return element.getAllAnnotations().stream() 249 .filter(InjectionAnnotations::hasQualifierAnnotation) 250 .collect(toImmutableSet()); 251 } 252 getQualifiersWithMetadata(XElement element)253 private Optional<ImmutableSet<XAnnotation>> getQualifiersWithMetadata(XElement element) { 254 Optional<XAnnotation> qualifierMetadata = getQualifierMetadata(element); 255 if (!qualifierMetadata.isPresent()) { 256 return Optional.empty(); 257 } 258 ImmutableSet<String> qualifierNames = 259 ImmutableSet.copyOf(qualifierMetadata.get().getAsStringList("value")); 260 if (qualifierNames.isEmpty()) { 261 return Optional.of(ImmutableSet.of()); 262 } 263 ImmutableSet<XAnnotation> qualifierAnnotations = 264 element.getAllAnnotations().stream() 265 .filter( 266 annotation -> 267 qualifierNames.contains( 268 annotation.getType().getTypeElement().getQualifiedName())) 269 .collect(toImmutableSet()); 270 if (qualifierAnnotations.isEmpty()) { 271 return Optional.of(ImmutableSet.of()); 272 } 273 // We should be guaranteed that there's exactly one qualifier since the existance of 274 // @QualifierMetadata means that this element has already been processed and multiple 275 // qualifiers would have been caught already. 276 XAnnotation qualifierAnnotation = getOnlyElement(qualifierAnnotations); 277 278 // Ensure the annotation type is superficially valid before we check for @Qualifier, otherwise 279 // the @Qualifier marker may appear to be missing from the annotation (b/213880825). 280 superficialValidation.validateAnnotationTypeOf(element, qualifierAnnotation); 281 if (compilerOptions.strictSuperficialValidation()) { 282 return Optional.of(ImmutableSet.of(qualifierAnnotation)); 283 } else { 284 // If strictSuperficialValidation is disabled, then we fall back to the old behavior where 285 // we may potentially miss a qualifier rather than report an exception. 286 return hasQualifierAnnotation(qualifierAnnotation) 287 ? Optional.of(ImmutableSet.of(qualifierAnnotation)) 288 : Optional.empty(); 289 } 290 } 291 292 /** 293 * Returns {@code QualifierMetadata} annotation. 294 * 295 * <p>Currently, {@code QualifierMetadata} is only associated with inject constructor parameters, 296 * inject fields, inject method parameters, provide methods, and provide method parameters. 297 */ getQualifierMetadata(XElement element)298 private Optional<XAnnotation> getQualifierMetadata(XElement element) { 299 return getGeneratedNameForQualifierMetadata(element) 300 .flatMap(name -> Optional.ofNullable(processingEnv.findTypeElement(name))) 301 .flatMap(type -> Optional.ofNullable(type.getAnnotation(XTypeNames.QUALIFIER_METADATA))); 302 } 303 getGeneratedNameForQualifierMetadata(XElement element)304 private Optional<XClassName> getGeneratedNameForQualifierMetadata(XElement element) { 305 // Currently we only support @QualifierMetadata for @Inject fields, @Inject method parameters, 306 // @Inject constructor parameters, @Provides methods, and @Provides method parameters. 307 if (isField(element) && hasInjectAnnotation(element)) { 308 return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element))); 309 } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) { 310 return Optional.of(factoryNameForElement(asMethod(element))); 311 } else if (isMethodParameter(element)) { 312 XExecutableElement executableElement = asMethodParameter(element).getEnclosingElement(); 313 if (isConstructor(executableElement) 314 && hasInjectOrAssistedInjectAnnotation(executableElement)) { 315 return Optional.of(factoryNameForElement(executableElement)); 316 } 317 if (isMethod(executableElement) && hasInjectAnnotation(executableElement)) { 318 return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element))); 319 } 320 if (isMethod(executableElement) && executableElement.hasAnnotation(TypeNames.PROVIDES)) { 321 return Optional.of(factoryNameForElement(executableElement)); 322 } 323 } 324 return Optional.empty(); 325 } 326 327 /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */ injectedConstructors(XTypeElement type)328 public static ImmutableSet<XConstructorElement> injectedConstructors(XTypeElement type) { 329 return type.getConstructors().stream() 330 .filter(InjectionAnnotations::hasInjectAnnotation) 331 .collect(toImmutableSet()); 332 } 333 hasQualifierAnnotation(XAnnotation annotation)334 private static boolean hasQualifierAnnotation(XAnnotation annotation) { 335 return annotation 336 .getType() 337 .getTypeElement() 338 .hasAnyAnnotation(TypeNames.QUALIFIER, TypeNames.QUALIFIER_JAVAX); 339 } 340 hasScopeAnnotation(XAnnotation annotation)341 private static boolean hasScopeAnnotation(XAnnotation annotation) { 342 return annotation 343 .getType() 344 .getTypeElement() 345 .hasAnyAnnotation(TypeNames.SCOPE, TypeNames.SCOPE_JAVAX); 346 } 347 348 /** Returns true if the given element is annotated with {@link Inject}. */ hasInjectAnnotation(XElement element)349 public static boolean hasInjectAnnotation(XElement element) { 350 return element.hasAnyAnnotation(TypeNames.INJECT, TypeNames.INJECT_JAVAX); 351 } 352 hasInjectOrAssistedInjectAnnotation(XElement element)353 private 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