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