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.isMethod; 20 import static androidx.room.compiler.processing.XTypeKt.isVoid; 21 import static androidx.room.compiler.processing.compat.XConverters.toJavac; 22 import static com.google.common.base.Preconditions.checkArgument; 23 import static com.google.common.base.Preconditions.checkNotNull; 24 import static com.google.common.base.Preconditions.checkState; 25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 27 import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType; 28 import static dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf; 29 import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive; 30 import static javax.lang.model.type.TypeKind.VOID; 31 32 import androidx.room.compiler.processing.XElement; 33 import androidx.room.compiler.processing.XMethodElement; 34 import androidx.room.compiler.processing.XType; 35 import androidx.room.compiler.processing.XTypeElement; 36 import com.google.auto.value.AutoValue; 37 import com.google.auto.value.extension.memoized.Memoized; 38 import com.google.common.base.Supplier; 39 import com.google.common.base.Suppliers; 40 import com.google.common.collect.ImmutableBiMap; 41 import com.google.common.collect.ImmutableMap; 42 import com.google.common.collect.ImmutableSet; 43 import com.google.common.collect.Maps; 44 import com.google.errorprone.annotations.CanIgnoreReturnValue; 45 import com.google.errorprone.annotations.CheckReturnValue; 46 import com.squareup.javapoet.TypeName; 47 import dagger.Component; 48 import dagger.Module; 49 import dagger.Subcomponent; 50 import dagger.internal.codegen.base.ComponentAnnotation; 51 import dagger.internal.codegen.langmodel.DaggerTypes; 52 import dagger.producers.CancellationPolicy; 53 import dagger.spi.model.DependencyRequest; 54 import dagger.spi.model.Scope; 55 import java.util.HashMap; 56 import java.util.Map; 57 import java.util.Objects; 58 import java.util.Optional; 59 import java.util.stream.Stream; 60 import javax.lang.model.element.ExecutableElement; 61 import javax.lang.model.type.TypeMirror; 62 63 /** 64 * A component declaration. 65 * 66 * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent}, 67 * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}. 68 * 69 * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also 70 * represent a synthetic component for the module, where there is an entry point for each binding in 71 * the module. 72 */ 73 @AutoValue 74 public abstract class ComponentDescriptor { 75 /** Creates a {@link ComponentDescriptor}. */ create( ComponentAnnotation componentAnnotation, XTypeElement component, ImmutableSet<ComponentRequirement> componentDependencies, ImmutableSet<ModuleDescriptor> transitiveModules, ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod, ImmutableSet<Scope> scopes, ImmutableSet<ComponentDescriptor> subcomponentsFromModules, ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod, ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod, ImmutableSet<ComponentMethodDescriptor> componentMethods, Optional<ComponentCreatorDescriptor> creator)76 static ComponentDescriptor create( 77 ComponentAnnotation componentAnnotation, 78 XTypeElement component, 79 ImmutableSet<ComponentRequirement> componentDependencies, 80 ImmutableSet<ModuleDescriptor> transitiveModules, 81 ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod, 82 ImmutableSet<Scope> scopes, 83 ImmutableSet<ComponentDescriptor> subcomponentsFromModules, 84 ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod, 85 ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod, 86 ImmutableSet<ComponentMethodDescriptor> componentMethods, 87 Optional<ComponentCreatorDescriptor> creator) { 88 ComponentDescriptor descriptor = 89 new AutoValue_ComponentDescriptor( 90 componentAnnotation, 91 component, 92 componentDependencies, 93 transitiveModules, 94 dependenciesByDependencyMethod, 95 scopes, 96 subcomponentsFromModules, 97 subcomponentsByFactoryMethod, 98 subcomponentsByBuilderMethod, 99 componentMethods, 100 creator); 101 return descriptor; 102 } 103 104 /** The annotation that specifies that {@link #typeElement()} is a component. */ annotation()105 public abstract ComponentAnnotation annotation(); 106 107 /** Returns {@code true} if this is a subcomponent. */ isSubcomponent()108 public final boolean isSubcomponent() { 109 return annotation().isSubcomponent(); 110 } 111 112 /** 113 * Returns {@code true} if this is a production component or subcomponent, or a 114 * {@code @ProducerModule} when doing module binding validation. 115 */ isProduction()116 public final boolean isProduction() { 117 return annotation().isProduction(); 118 } 119 120 /** 121 * Returns {@code true} if this is a real component, and not a fictional one used to validate 122 * module bindings. 123 */ isRealComponent()124 public final boolean isRealComponent() { 125 return annotation().isRealComponent(); 126 } 127 128 /** 129 * The element that defines the component. This is the element to which the {@link #annotation()} 130 * was applied. 131 */ typeElement()132 public abstract XTypeElement typeElement(); 133 134 /** 135 * The set of component dependencies listed in {@link Component#dependencies} or {@link 136 * dagger.producers.ProductionComponent#dependencies()}. 137 */ dependencies()138 public abstract ImmutableSet<ComponentRequirement> dependencies(); 139 140 /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */ dependenciesAndConcreteModules()141 public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() { 142 return Stream.concat( 143 moduleTypes().stream() 144 .filter(dep -> !dep.isAbstract()) 145 .map(module -> ComponentRequirement.forModule(module.getType())), 146 dependencies().stream()) 147 .collect(toImmutableSet()); 148 } 149 150 /** 151 * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by 152 * traversing {@link Module#includes()}. 153 */ modules()154 public abstract ImmutableSet<ModuleDescriptor> modules(); 155 156 /** The types of the {@link #modules()}. */ moduleTypes()157 public final ImmutableSet<XTypeElement> moduleTypes() { 158 return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet()); 159 } 160 161 /** 162 * The types for which the component will need instances if all of its bindings are used. For the 163 * types the component will need in a given binding graph, use {@link 164 * BindingGraph#componentRequirements()}. 165 * 166 * <ul> 167 * <li>{@linkplain #modules()} modules} with concrete instance bindings 168 * <li>Bound instances 169 * <li>{@linkplain #dependencies() dependencies} 170 * </ul> 171 */ 172 @Memoized requirements()173 ImmutableSet<ComponentRequirement> requirements() { 174 ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder(); 175 modules().stream() 176 .filter( 177 module -> 178 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance)) 179 .map(module -> ComponentRequirement.forModule(module.moduleElement().getType())) 180 .forEach(requirements::add); 181 requirements.addAll(dependencies()); 182 requirements.addAll( 183 creatorDescriptor() 184 .map(ComponentCreatorDescriptor::boundInstanceRequirements) 185 .orElse(ImmutableSet.of())); 186 return requirements.build(); 187 } 188 189 /** 190 * This component's {@linkplain #dependencies() dependencies} keyed by each provision or 191 * production method defined by that dependency. Note that the dependencies' types are not simply 192 * the enclosing type of the method; a method may be declared by a supertype of the actual 193 * dependency. 194 */ 195 public abstract ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod()196 dependenciesByDependencyMethod(); 197 198 /** The {@linkplain #dependencies() component dependency} that defines a method. */ getDependencyThatDefinesMethod(XElement method)199 public final ComponentRequirement getDependencyThatDefinesMethod(XElement method) { 200 checkArgument(isMethod(method), "method must be an executable element: %s", method); 201 checkState( 202 dependenciesByDependencyMethod().containsKey(method), 203 "no dependency implements %s", 204 method); 205 return dependenciesByDependencyMethod().get(method); 206 } 207 208 /** The scopes of the component. */ scopes()209 public abstract ImmutableSet<Scope> scopes(); 210 211 /** 212 * All {@link Subcomponent}s which are direct children of this component. This includes 213 * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain 214 * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain 215 * #childComponentsDeclaredByBuilderEntryPoints() builder methods}. 216 */ childComponents()217 public final ImmutableSet<ComponentDescriptor> childComponents() { 218 return ImmutableSet.<ComponentDescriptor>builder() 219 .addAll(childComponentsDeclaredByFactoryMethods().values()) 220 .addAll(childComponentsDeclaredByBuilderEntryPoints().values()) 221 .addAll(childComponentsDeclaredByModules()) 222 .build(); 223 } 224 225 /** 226 * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain 227 * Module#subcomponents() module's subcomponents}. 228 */ childComponentsDeclaredByModules()229 abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules(); 230 231 /** 232 * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent 233 * factory method. 234 */ 235 public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> childComponentsDeclaredByFactoryMethods()236 childComponentsDeclaredByFactoryMethods(); 237 238 /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */ 239 @Memoized childComponentsByElement()240 public ImmutableMap<XTypeElement, ComponentDescriptor> childComponentsByElement() { 241 return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement); 242 } 243 244 /** Returns the factory method that declares a child component. */ getFactoryMethodForChildComponent( ComponentDescriptor childComponent)245 final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent( 246 ComponentDescriptor childComponent) { 247 return Optional.ofNullable( 248 childComponentsDeclaredByFactoryMethods().inverse().get(childComponent)); 249 } 250 251 /** 252 * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent 253 * builder method. 254 */ 255 abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> childComponentsDeclaredByBuilderEntryPoints()256 childComponentsDeclaredByBuilderEntryPoints(); 257 258 private final Supplier<ImmutableMap<XTypeElement, ComponentDescriptor>> 259 childComponentsByBuilderType = 260 Suppliers.memoize( 261 () -> 262 childComponents().stream() 263 .filter(child -> child.creatorDescriptor().isPresent()) 264 .collect( 265 toImmutableMap( 266 child -> child.creatorDescriptor().get().typeElement(), 267 child -> child))); 268 269 /** Returns the child component with the given builder type. */ getChildComponentWithBuilderType(XTypeElement builderType)270 final ComponentDescriptor getChildComponentWithBuilderType(XTypeElement builderType) { 271 return checkNotNull( 272 childComponentsByBuilderType.get().get(builderType), 273 "no child component found for builder type %s", 274 builderType.getQualifiedName()); 275 } 276 componentMethods()277 public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods(); 278 279 /** Returns the first component method associated with this binding request, if one exists. */ firstMatchingComponentMethod(BindingRequest request)280 public Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) { 281 return Optional.ofNullable(firstMatchingComponentMethods().get(request)); 282 } 283 284 @Memoized 285 ImmutableMap<BindingRequest, ComponentMethodDescriptor> firstMatchingComponentMethods()286 firstMatchingComponentMethods() { 287 Map<BindingRequest, ComponentMethodDescriptor> methods = new HashMap<>(); 288 for (ComponentMethodDescriptor method : entryPointMethods()) { 289 methods.putIfAbsent(BindingRequest.bindingRequest(method.dependencyRequest().get()), method); 290 } 291 return ImmutableMap.copyOf(methods); 292 } 293 294 /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */ entryPointMethods()295 public final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() { 296 return componentMethods() 297 .stream() 298 .filter(method -> method.dependencyRequest().isPresent()) 299 .collect(toImmutableSet()); 300 } 301 302 // TODO(gak): Consider making this non-optional and revising the 303 // interaction between the spec & generation 304 /** Returns a descriptor for the creator type for this component type, if the user defined one. */ creatorDescriptor()305 public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor(); 306 307 /** 308 * Returns {@code true} for components that have a creator, either because the user {@linkplain 309 * #creatorDescriptor() specified one} or because it's a top-level component with an implicit 310 * builder. 311 */ hasCreator()312 public final boolean hasCreator() { 313 return !isSubcomponent() || creatorDescriptor().isPresent(); 314 } 315 316 /** 317 * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the 318 * component is not a production component or no {@code CancellationPolicy} annotation is present. 319 */ cancellationPolicy()320 public final Optional<CancellationPolicy> cancellationPolicy() { 321 return isProduction() 322 // TODO(bcorso): Get values from XAnnotation instead of using CancellationPolicy directly. 323 ? Optional.ofNullable(toJavac(typeElement()).getAnnotation(CancellationPolicy.class)) 324 : Optional.empty(); 325 } 326 327 @Memoized 328 @Override hashCode()329 public int hashCode() { 330 // TODO(b/122962745): Only use typeElement().hashCode() 331 return Objects.hash(typeElement(), annotation()); 332 } 333 334 // TODO(ronshapiro): simplify the equality semantics 335 @Override equals(Object obj)336 public abstract boolean equals(Object obj); 337 338 /** A component method. */ 339 @AutoValue 340 public abstract static class ComponentMethodDescriptor { 341 /** The method itself. Note that this may be declared on a supertype of the component. */ methodElement()342 public abstract XMethodElement methodElement(); 343 344 /** 345 * The dependency request for production, provision, and subcomponent creator methods. Absent 346 * for subcomponent factory methods. 347 */ dependencyRequest()348 public abstract Optional<DependencyRequest> dependencyRequest(); 349 350 /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */ subcomponent()351 public abstract Optional<ComponentDescriptor> subcomponent(); 352 353 /** 354 * Returns the return type of {@link #methodElement()} as resolved in the {@link 355 * ComponentDescriptor#typeElement() component type}. If there are no type variables in the 356 * return type, this is the equivalent of {@code methodElement().getReturnType()}. 357 */ resolvedReturnType(DaggerTypes types)358 public TypeMirror resolvedReturnType(DaggerTypes types) { 359 checkState(dependencyRequest().isPresent()); 360 361 XType returnType = methodElement().getReturnType(); 362 if (isPrimitive(returnType) || isVoid(returnType)) { 363 return toJavac(returnType); 364 } 365 return BindingRequest.bindingRequest(dependencyRequest().get()) 366 .requestedType(dependencyRequest().get().key().type().java(), types); 367 } 368 369 /** A {@link ComponentMethodDescriptor}builder for a method. */ builder(XMethodElement method)370 public static Builder builder(XMethodElement method) { 371 return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder() 372 .methodElement(method); 373 } 374 375 /** A builder of {@link ComponentMethodDescriptor}s. */ 376 @AutoValue.Builder 377 @CanIgnoreReturnValue 378 public interface Builder { 379 /** @see ComponentMethodDescriptor#methodElement() */ methodElement(XMethodElement methodElement)380 Builder methodElement(XMethodElement methodElement); 381 382 /** @see ComponentMethodDescriptor#dependencyRequest() */ dependencyRequest(DependencyRequest dependencyRequest)383 Builder dependencyRequest(DependencyRequest dependencyRequest); 384 385 /** @see ComponentMethodDescriptor#subcomponent() */ subcomponent(ComponentDescriptor subcomponent)386 Builder subcomponent(ComponentDescriptor subcomponent); 387 388 /** Builds the descriptor. */ 389 @CheckReturnValue build()390 ComponentMethodDescriptor build(); 391 } 392 } 393 394 /** No-argument methods defined on {@link Object} that are ignored for contribution. */ 395 private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES = 396 ImmutableSet.of("toString", "hashCode", "clone", "getClass"); 397 398 /** 399 * Returns {@code true} if a method could be a component entry point but not a members-injection 400 * method. 401 */ isComponentContributionMethod(XMethodElement method)402 static boolean isComponentContributionMethod(XMethodElement method) { 403 return isComponentContributionMethod(toJavac(method)); 404 } 405 406 /** 407 * Returns {@code true} if a method could be a component entry point but not a members-injection 408 * method. 409 */ isComponentContributionMethod(ExecutableElement method)410 static boolean isComponentContributionMethod(ExecutableElement method) { 411 return method.getParameters().isEmpty() 412 && !method.getReturnType().getKind().equals(VOID) 413 && !isTypeOf(TypeName.OBJECT, method.getEnclosingElement().asType()) 414 && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(method.getSimpleName().toString()); 415 } 416 417 /** Returns {@code true} if a method could be a component production entry point. */ isComponentProductionMethod(XMethodElement method)418 static boolean isComponentProductionMethod(XMethodElement method) { 419 return isComponentContributionMethod(method) && isFutureType(method.getReturnType()); 420 } 421 } 422