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.Objects; 49 import java.util.Optional; 50 import java.util.stream.Stream; 51 import javax.lang.model.element.Element; 52 import javax.lang.model.element.ExecutableElement; 53 import javax.lang.model.element.TypeElement; 54 import javax.lang.model.type.TypeMirror; 55 56 /** 57 * A component declaration. 58 * 59 * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent}, 60 * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}. 61 * 62 * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also 63 * represent a synthetic component for the module, where there is an entry point for each binding in 64 * the module. 65 */ 66 @AutoValue 67 public abstract class ComponentDescriptor { 68 /** The annotation that specifies that {@link #typeElement()} is a component. */ annotation()69 public abstract ComponentAnnotation annotation(); 70 71 /** Returns {@code true} if this is a subcomponent. */ isSubcomponent()72 public final boolean isSubcomponent() { 73 return annotation().isSubcomponent(); 74 } 75 76 /** 77 * Returns {@code true} if this is a production component or subcomponent, or a 78 * {@code @ProducerModule} when doing module binding validation. 79 */ isProduction()80 public final boolean isProduction() { 81 return annotation().isProduction(); 82 } 83 84 /** 85 * Returns {@code true} if this is a real component, and not a fictional one used to validate 86 * module bindings. 87 */ isRealComponent()88 public final boolean isRealComponent() { 89 return annotation().isRealComponent(); 90 } 91 92 /** 93 * The element that defines the component. This is the element to which the {@link #annotation()} 94 * was applied. 95 */ typeElement()96 public abstract TypeElement typeElement(); 97 98 /** 99 * The set of component dependencies listed in {@link Component#dependencies} or {@link 100 * ProductionComponent#dependencies()}. 101 */ dependencies()102 public abstract ImmutableSet<ComponentRequirement> dependencies(); 103 104 /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */ dependenciesAndConcreteModules()105 public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() { 106 return Stream.concat( 107 moduleTypes().stream() 108 .filter(dep -> !dep.getModifiers().contains(ABSTRACT)) 109 .map(module -> ComponentRequirement.forModule(module.asType())), 110 dependencies().stream()) 111 .collect(toImmutableSet()); 112 } 113 114 /** 115 * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by 116 * traversing {@link Module#includes()}. 117 */ modules()118 public abstract ImmutableSet<ModuleDescriptor> modules(); 119 120 /** The types of the {@link #modules()}. */ moduleTypes()121 public final ImmutableSet<TypeElement> moduleTypes() { 122 return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet()); 123 } 124 125 /** 126 * The types for which the component will need instances if all of its bindings are used. For the 127 * types the component will need in a given binding graph, use {@link 128 * BindingGraph#componentRequirements()}. 129 * 130 * <ul> 131 * <li>{@linkplain #modules()} modules} with concrete instance bindings 132 * <li>Bound instances 133 * <li>{@linkplain #dependencies() dependencies} 134 * </ul> 135 */ 136 @Memoized requirements()137 ImmutableSet<ComponentRequirement> requirements() { 138 ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder(); 139 modules().stream() 140 .filter( 141 module -> 142 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance)) 143 .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())) 144 .forEach(requirements::add); 145 requirements.addAll(dependencies()); 146 requirements.addAll( 147 creatorDescriptor() 148 .map(ComponentCreatorDescriptor::boundInstanceRequirements) 149 .orElse(ImmutableSet.of())); 150 return requirements.build(); 151 } 152 153 /** 154 * This component's {@linkplain #dependencies() dependencies} keyed by each provision or 155 * production method defined by that dependency. Note that the dependencies' types are not simply 156 * the enclosing type of the method; a method may be declared by a supertype of the actual 157 * dependency. 158 */ 159 public abstract ImmutableMap<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod()160 dependenciesByDependencyMethod(); 161 162 /** The {@linkplain #dependencies() component dependency} that defines a method. */ getDependencyThatDefinesMethod(Element method)163 public final ComponentRequirement getDependencyThatDefinesMethod(Element method) { 164 checkArgument( 165 method instanceof ExecutableElement, "method must be an executable element: %s", method); 166 return checkNotNull( 167 dependenciesByDependencyMethod().get(method), "no dependency implements %s", method); 168 } 169 170 /** The scopes of the component. */ scopes()171 public 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 public 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 public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> childComponentsDeclaredByFactoryMethods()198 childComponentsDeclaredByFactoryMethods(); 199 200 /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */ 201 @Memoized childComponentsByElement()202 public 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 public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods(); 240 241 /** Returns the first component method associated with this binding request, if one exists. */ firstMatchingComponentMethod(BindingRequest request)242 public 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 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