• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.CaseFormat.LOWER_CAMEL;
20 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
21 import static com.google.common.base.Verify.verify;
22 import static com.google.common.collect.Collections2.transform;
23 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
24 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
25 import static dagger.internal.codegen.binding.SourceFiles.classFileName;
26 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
27 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
28 import static dagger.internal.codegen.xprocessing.XElements.asMethod;
29 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
30 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
31 
32 import androidx.room.compiler.processing.XMethodElement;
33 import androidx.room.compiler.processing.XProcessingEnv;
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.collect.ImmutableSet;
39 import com.google.common.graph.Traverser;
40 import com.squareup.javapoet.ClassName;
41 import com.squareup.javapoet.TypeName;
42 import dagger.Binds;
43 import dagger.BindsOptionalOf;
44 import dagger.Module;
45 import dagger.internal.codegen.base.ClearableCache;
46 import dagger.internal.codegen.base.DaggerSuperficialValidation;
47 import dagger.internal.codegen.base.ModuleKind;
48 import dagger.internal.codegen.javapoet.TypeNames;
49 import dagger.internal.codegen.model.Key;
50 import dagger.internal.codegen.xprocessing.XTypeElements;
51 import java.util.Collection;
52 import java.util.HashMap;
53 import java.util.LinkedHashSet;
54 import java.util.Map;
55 import java.util.Set;
56 import javax.inject.Inject;
57 import javax.inject.Singleton;
58 
59 /** Contains metadata that describes a module. */
60 @AutoValue
61 public abstract class ModuleDescriptor {
62 
moduleElement()63   public abstract XTypeElement moduleElement();
64 
bindings()65   public abstract ImmutableSet<ContributionBinding> bindings();
66 
67   /** The multibinding declarations contained in this module. */
multibindingDeclarations()68   abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
69 
70   /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */
subcomponentDeclarations()71   abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
72 
73   /** The {@link Binds} method declarations that define delegate bindings. */
delegateDeclarations()74   abstract ImmutableSet<DelegateDeclaration> delegateDeclarations();
75 
76   /** The {@link BindsOptionalOf} method declarations that define optional bindings. */
optionalDeclarations()77   abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations();
78 
79   /** The kind of the module. */
kind()80   public abstract ModuleKind kind();
81 
82   /** Whether the module is implicitly included rather than directly referenced in annotation. */
isImplicitlyIncluded()83   public abstract boolean isImplicitlyIncluded();
84 
85   /** Returns all of the bindings declared in this module. */
86   @Memoized
allBindingDeclarations()87   public ImmutableSet<Declaration> allBindingDeclarations() {
88     return ImmutableSet.<Declaration>builder()
89         .addAll(bindings())
90         .addAll(delegateDeclarations())
91         .addAll(multibindingDeclarations())
92         .addAll(optionalDeclarations())
93         .addAll(subcomponentDeclarations())
94         .build();
95   }
96 
97   /** Returns the keys of all bindings declared by this module. */
allBindingKeys()98   ImmutableSet<Key> allBindingKeys() {
99     return allBindingDeclarations().stream().map(Declaration::key).collect(toImmutableSet());
100   }
101 
102   /** A {@link ModuleDescriptor} factory. */
103   @Singleton
104   public static final class Factory implements ClearableCache {
105     private final XProcessingEnv processingEnv;
106     private final BindingFactory bindingFactory;
107     private final MultibindingDeclaration.Factory multibindingDeclarationFactory;
108     private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory;
109     private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory;
110     private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory;
111     private final DaggerSuperficialValidation superficialValidation;
112     private final Map<XTypeElement, ModuleDescriptor> cache = new HashMap<>();
113     private final Set<XTypeElement> implicitlyIncludedModules = new LinkedHashSet<>();
114 
115     @Inject
Factory( XProcessingEnv processingEnv, BindingFactory bindingFactory, MultibindingDeclaration.Factory multibindingDeclarationFactory, DelegateDeclaration.Factory bindingDelegateDeclarationFactory, SubcomponentDeclaration.Factory subcomponentDeclarationFactory, OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory, DaggerSuperficialValidation superficialValidation)116     Factory(
117         XProcessingEnv processingEnv,
118         BindingFactory bindingFactory,
119         MultibindingDeclaration.Factory multibindingDeclarationFactory,
120         DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
121         SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
122         OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory,
123         DaggerSuperficialValidation superficialValidation) {
124       this.processingEnv = processingEnv;
125       this.bindingFactory = bindingFactory;
126       this.multibindingDeclarationFactory = multibindingDeclarationFactory;
127       this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
128       this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
129       this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
130       this.superficialValidation = superficialValidation;
131     }
132 
create(XTypeElement moduleElement)133     public ModuleDescriptor create(XTypeElement moduleElement) {
134       return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
135     }
136 
createUncached(XTypeElement moduleElement)137     public ModuleDescriptor createUncached(XTypeElement moduleElement) {
138       ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
139       ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
140       ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
141           ImmutableSet.builder();
142       ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations =
143           ImmutableSet.builder();
144 
145       XTypeElements.getAllMethods(moduleElement).stream()
146           .forEach(
147               moduleMethod -> {
148                 if (moduleMethod.hasAnnotation(TypeNames.PROVIDES)) {
149                   bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
150                 }
151                 if (moduleMethod.hasAnnotation(TypeNames.PRODUCES)) {
152                   bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
153                 }
154                 if (moduleMethod.hasAnnotation(TypeNames.BINDS)) {
155                   delegates.add(
156                       bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
157                 }
158                 if (moduleMethod.hasAnnotation(TypeNames.MULTIBINDS)) {
159                   multibindingDeclarations.add(
160                       multibindingDeclarationFactory.forMultibindsMethod(
161                           moduleMethod, moduleElement));
162                 }
163                 if (moduleMethod.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF)) {
164                   optionalDeclarations.add(
165                       optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
166                 }
167               });
168 
169       moduleElement.getEnclosedTypeElements().stream()
170           .filter(XTypeElement::isCompanionObject)
171           .collect(toOptional())
172           .ifPresent(companionModule -> collectCompanionModuleBindings(companionModule, bindings));
173 
174       return new AutoValue_ModuleDescriptor(
175           moduleElement,
176           bindings.build(),
177           multibindingDeclarations.build(),
178           subcomponentDeclarationFactory.forModule(moduleElement),
179           delegates.build(),
180           optionalDeclarations.build(),
181           ModuleKind.forAnnotatedElement(moduleElement).get(),
182           implicitlyIncludedModules.contains(moduleElement));
183     }
184 
collectCompanionModuleBindings( XTypeElement companionModule, ImmutableSet.Builder<ContributionBinding> bindings)185     private void collectCompanionModuleBindings(
186         XTypeElement companionModule, ImmutableSet.Builder<ContributionBinding> bindings) {
187       ImmutableSet<String> bindingElementDescriptors =
188           bindings.build().stream()
189               .map(binding -> asMethod(binding.bindingElement().get()).getJvmDescriptor())
190               .collect(toImmutableSet());
191 
192       XTypeElements.getAllMethods(companionModule).stream()
193           // Binding methods in companion objects with @JvmStatic are mirrored in the enclosing
194           // class, therefore we should ignore it or else it'll be a duplicate binding.
195           .filter(method -> !method.hasAnnotation(TypeNames.JVM_STATIC))
196           // Fallback strategy for de-duping contributing bindings in the companion module with
197           // @JvmStatic by comparing descriptors. Contributing bindings are the only valid bindings
198           // a companion module can declare. See: https://youtrack.jetbrains.com/issue/KT-35104
199           // TODO(danysantiago): Checks qualifiers too.
200           .filter(method -> !bindingElementDescriptors.contains(method.getJvmDescriptor()))
201           .forEach(
202               method -> {
203                 if (method.hasAnnotation(TypeNames.PROVIDES)) {
204                   bindings.add(bindingFactory.providesMethodBinding(method, companionModule));
205                 }
206                 if (method.hasAnnotation(TypeNames.PRODUCES)) {
207                   bindings.add(bindingFactory.producesMethodBinding(method, companionModule));
208                 }
209               });
210     }
211 
212     /** Returns all the modules transitively included by given modules, including the arguments. */
transitiveModules(Collection<XTypeElement> modules)213     ImmutableSet<ModuleDescriptor> transitiveModules(Collection<XTypeElement> modules) {
214       // Traverse as a graph to automatically handle modules with cyclic includes.
215       return ImmutableSet.copyOf(
216           Traverser.forGraph(
217                   (ModuleDescriptor module) -> transform(includedModules(module), this::create))
218               .depthFirstPreOrder(transform(modules, this::create)));
219     }
220 
includedModules(ModuleDescriptor moduleDescriptor)221     private ImmutableSet<XTypeElement> includedModules(ModuleDescriptor moduleDescriptor) {
222       return ImmutableSet.copyOf(
223           collectIncludedModules(new LinkedHashSet<>(), moduleDescriptor.moduleElement()));
224     }
225 
collectIncludedModules( Set<XTypeElement> includedModules, XTypeElement moduleElement)226     private Set<XTypeElement> collectIncludedModules(
227         Set<XTypeElement> includedModules, XTypeElement moduleElement) {
228       XType superclass = moduleElement.getSuperType();
229       if (superclass != null) {
230         verify(isDeclared(superclass));
231         if (!TypeName.OBJECT.equals(superclass.getTypeName())) {
232           collectIncludedModules(includedModules, superclass.getTypeElement());
233         }
234       }
235       moduleAnnotation(moduleElement, superficialValidation)
236           .ifPresent(
237               moduleAnnotation -> {
238                 includedModules.addAll(moduleAnnotation.includes());
239                 ImmutableSet<XTypeElement> daggerAndroidModules =
240                     implicitlyIncludedModules(moduleElement);
241                 includedModules.addAll(daggerAndroidModules);
242                 implicitlyIncludedModules.addAll(daggerAndroidModules);
243               });
244       return includedModules;
245     }
246 
247     private static final ClassName CONTRIBUTES_ANDROID_INJECTOR =
248         ClassName.get("dagger.android", "ContributesAndroidInjector");
249 
250     // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing
251     // module
implicitlyIncludedModules(XTypeElement module)252     private ImmutableSet<XTypeElement> implicitlyIncludedModules(XTypeElement module) {
253       if (processingEnv.findTypeElement(CONTRIBUTES_ANDROID_INJECTOR) == null) {
254         return ImmutableSet.of();
255       }
256       return module.getDeclaredMethods().stream()
257           .filter(method -> method.hasAnnotation(CONTRIBUTES_ANDROID_INJECTOR))
258           .map(
259               method ->
260                   DaggerSuperficialValidation.requireTypeElement(
261                       processingEnv, implicitlyIncludedModuleName(module, method)))
262           .collect(toImmutableSet());
263     }
264 
implicitlyIncludedModuleName(XTypeElement module, XMethodElement method)265     private ClassName implicitlyIncludedModuleName(XTypeElement module, XMethodElement method) {
266       return ClassName.get(
267           module.getPackageName(),
268           String.format(
269               "%s_%s",
270               classFileName(module.asClassName()),
271               LOWER_CAMEL.to(UPPER_CAMEL, getSimpleName(method))));
272     }
273 
274     @Override
clearCache()275     public void clearCache() {
276       cache.clear();
277     }
278   }
279 }
280