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; 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.DaggerStreams.toImmutableMap; 23 import static dagger.internal.codegen.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.langmodel.DaggerElements; 42 import dagger.internal.codegen.langmodel.DaggerTypes; 43 import dagger.model.DependencyRequest; 44 import dagger.model.Scope; 45 import dagger.producers.CancellationPolicy; 46 import dagger.producers.ProductionComponent; 47 import java.util.Objects; 48 import java.util.Optional; 49 import java.util.stream.Stream; 50 import javax.lang.model.element.Element; 51 import javax.lang.model.element.ExecutableElement; 52 import javax.lang.model.element.TypeElement; 53 import javax.lang.model.type.TypeMirror; 54 55 /** 56 * A component declaration. 57 * 58 * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent}, 59 * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}. 60 * 61 * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also 62 * represent a synthetic component for the module, where there is an entry point for each binding in 63 * the module. 64 */ 65 @AutoValue 66 abstract class ComponentDescriptor { 67 /** The annotation that specifies that {@link #typeElement()} is a component. */ annotation()68 abstract ComponentAnnotation annotation(); 69 70 /** Returns {@code true} if this is a subcomponent. */ isSubcomponent()71 final boolean isSubcomponent() { 72 return annotation().isSubcomponent(); 73 } 74 75 /** 76 * Returns {@code true} if this is a production component or subcomponent, or a 77 * {@code @ProducerModule} when doing module binding validation. 78 */ isProduction()79 final boolean isProduction() { 80 return annotation().isProduction(); 81 } 82 83 /** 84 * Returns {@code true} if this is a real component, and not a fictional one used to validate 85 * module bindings. 86 */ isRealComponent()87 final boolean isRealComponent() { 88 return annotation().isRealComponent(); 89 } 90 91 /** 92 * The element that defines the component. This is the element to which the {@link #annotation()} 93 * was applied. 94 */ typeElement()95 abstract TypeElement typeElement(); 96 97 /** 98 * The set of component dependencies listed in {@link Component#dependencies} or {@link 99 * ProductionComponent#dependencies()}. 100 */ dependencies()101 abstract ImmutableSet<ComponentRequirement> dependencies(); 102 103 /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */ dependenciesAndConcreteModules()104 final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() { 105 return Stream.concat( 106 moduleTypes().stream() 107 .filter(dep -> !dep.getModifiers().contains(ABSTRACT)) 108 .map(module -> ComponentRequirement.forModule(module.asType())), 109 dependencies().stream()) 110 .collect(toImmutableSet()); 111 } 112 113 /** 114 * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by 115 * traversing {@link Module#includes()}. 116 */ modules()117 abstract ImmutableSet<ModuleDescriptor> modules(); 118 119 /** The types of the {@link #modules()}. */ moduleTypes()120 final ImmutableSet<TypeElement> moduleTypes() { 121 return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet()); 122 } 123 124 /** 125 * The types for which the component will need instances if all of its bindings are used. For the 126 * types the component will need in a given binding graph, use {@link 127 * BindingGraph#componentRequirements()}. 128 * 129 * <ul> 130 * <li>{@linkplain #modules()} modules} with concrete instance bindings 131 * <li>Bound instances 132 * <li>{@linkplain #dependencies() dependencies} 133 * </ul> 134 */ 135 @Memoized requirements()136 ImmutableSet<ComponentRequirement> requirements() { 137 ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder(); 138 modules().stream() 139 .filter( 140 module -> 141 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance)) 142 .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())) 143 .forEach(requirements::add); 144 requirements.addAll(dependencies()); 145 requirements.addAll( 146 creatorDescriptor() 147 .map(ComponentCreatorDescriptor::boundInstanceRequirements) 148 .orElse(ImmutableSet.of())); 149 return requirements.build(); 150 } 151 152 /** 153 * This component's {@linkplain #dependencies() dependencies} keyed by each provision or 154 * production method defined by that dependency. Note that the dependencies' types are not simply 155 * the enclosing type of the method; a method may be declared by a supertype of the actual 156 * dependency. 157 */ dependenciesByDependencyMethod()158 abstract ImmutableMap<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod(); 159 160 /** The {@linkplain #dependencies() component dependency} that defines a method. */ getDependencyThatDefinesMethod(Element method)161 final ComponentRequirement getDependencyThatDefinesMethod(Element method) { 162 checkArgument( 163 method instanceof ExecutableElement, "method must be an executable element: %s", method); 164 return checkNotNull( 165 dependenciesByDependencyMethod().get(method), "no dependency implements %s", method); 166 } 167 168 /** 169 * The scopes of the component. 170 */ scopes()171 abstract ImmutableSet<Scope> scopes(); 172 173 /** 174 * All {@link Subcomponent}s which are direct children of this component. This includes 175 * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain 176 * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain 177 * #childComponentsDeclaredByBuilderEntryPoints() builder methods}. 178 */ childComponents()179 final ImmutableSet<ComponentDescriptor> childComponents() { 180 return ImmutableSet.<ComponentDescriptor>builder() 181 .addAll(childComponentsDeclaredByFactoryMethods().values()) 182 .addAll(childComponentsDeclaredByBuilderEntryPoints().values()) 183 .addAll(childComponentsDeclaredByModules()) 184 .build(); 185 } 186 187 /** 188 * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain 189 * Module#subcomponents() module's subcomponents}. 190 */ childComponentsDeclaredByModules()191 abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules(); 192 193 /** 194 * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent 195 * factory method. 196 */ 197 abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> childComponentsDeclaredByFactoryMethods()198 childComponentsDeclaredByFactoryMethods(); 199 200 /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */ 201 @Memoized childComponentsByElement()202 ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() { 203 return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement); 204 } 205 206 /** Returns the factory method that declares a child component. */ getFactoryMethodForChildComponent( ComponentDescriptor childComponent)207 final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent( 208 ComponentDescriptor childComponent) { 209 return Optional.ofNullable( 210 childComponentsDeclaredByFactoryMethods().inverse().get(childComponent)); 211 } 212 213 /** 214 * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent 215 * builder method. 216 */ 217 abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> childComponentsDeclaredByBuilderEntryPoints()218 childComponentsDeclaredByBuilderEntryPoints(); 219 220 private final Supplier<ImmutableMap<TypeElement, ComponentDescriptor>> 221 childComponentsByBuilderType = 222 Suppliers.memoize( 223 () -> 224 childComponents().stream() 225 .filter(child -> child.creatorDescriptor().isPresent()) 226 .collect( 227 toImmutableMap( 228 child -> child.creatorDescriptor().get().typeElement(), 229 child -> child))); 230 231 /** Returns the child component with the given builder type. */ getChildComponentWithBuilderType(TypeElement builderType)232 final ComponentDescriptor getChildComponentWithBuilderType(TypeElement builderType) { 233 return checkNotNull( 234 childComponentsByBuilderType.get().get(builderType), 235 "no child component found for builder type %s", 236 builderType.getQualifiedName()); 237 } 238 componentMethods()239 abstract ImmutableSet<ComponentMethodDescriptor> componentMethods(); 240 241 /** Returns the first component method associated with this binding request, if one exists. */ firstMatchingComponentMethod(BindingRequest request)242 Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) { 243 return componentMethods().stream() 244 .filter(method -> doesComponentMethodMatch(method, request)) 245 .findFirst(); 246 } 247 248 /** Returns true if the component method matches the binding request. */ doesComponentMethodMatch( ComponentMethodDescriptor componentMethod, BindingRequest request)249 private static boolean doesComponentMethodMatch( 250 ComponentMethodDescriptor componentMethod, BindingRequest request) { 251 return componentMethod 252 .dependencyRequest() 253 .map(BindingRequest::bindingRequest) 254 .filter(request::equals) 255 .isPresent(); 256 } 257 258 /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */ entryPointMethods()259 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 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 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 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 abstract static class ComponentMethodDescriptor { 304 /** The method itself. Note that this may be declared on a supertype of the component. */ methodElement()305 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 abstract Optional<DependencyRequest> dependencyRequest(); 312 313 /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */ subcomponent()314 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 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 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 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