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