• 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   /** Returns all of the bindings declared in this module. */
83   @Memoized
allBindingDeclarations()84   public ImmutableSet<BindingDeclaration> allBindingDeclarations() {
85     return ImmutableSet.<BindingDeclaration>builder()
86         .addAll(bindings())
87         .addAll(delegateDeclarations())
88         .addAll(multibindingDeclarations())
89         .addAll(optionalDeclarations())
90         .addAll(subcomponentDeclarations())
91         .build();
92   }
93 
94   /** Returns the keys of all bindings declared by this module. */
allBindingKeys()95   ImmutableSet<Key> allBindingKeys() {
96     return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet());
97   }
98 
99   /** A {@link ModuleDescriptor} factory. */
100   @Singleton
101   public static final class Factory implements ClearableCache {
102     private final XProcessingEnv processingEnv;
103     private final BindingFactory bindingFactory;
104     private final MultibindingDeclaration.Factory multibindingDeclarationFactory;
105     private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory;
106     private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory;
107     private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory;
108     private final DaggerSuperficialValidation superficialValidation;
109     private final Map<XTypeElement, ModuleDescriptor> cache = new HashMap<>();
110 
111     @Inject
Factory( XProcessingEnv processingEnv, BindingFactory bindingFactory, MultibindingDeclaration.Factory multibindingDeclarationFactory, DelegateDeclaration.Factory bindingDelegateDeclarationFactory, SubcomponentDeclaration.Factory subcomponentDeclarationFactory, OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory, DaggerSuperficialValidation superficialValidation)112     Factory(
113         XProcessingEnv processingEnv,
114         BindingFactory bindingFactory,
115         MultibindingDeclaration.Factory multibindingDeclarationFactory,
116         DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
117         SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
118         OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory,
119         DaggerSuperficialValidation superficialValidation) {
120       this.processingEnv = processingEnv;
121       this.bindingFactory = bindingFactory;
122       this.multibindingDeclarationFactory = multibindingDeclarationFactory;
123       this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
124       this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
125       this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
126       this.superficialValidation = superficialValidation;
127     }
128 
create(XTypeElement moduleElement)129     public ModuleDescriptor create(XTypeElement moduleElement) {
130       return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
131     }
132 
createUncached(XTypeElement moduleElement)133     public ModuleDescriptor createUncached(XTypeElement moduleElement) {
134       ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
135       ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
136       ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
137           ImmutableSet.builder();
138       ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations =
139           ImmutableSet.builder();
140 
141       XTypeElements.getAllMethods(moduleElement).stream()
142           .forEach(
143               moduleMethod -> {
144                 if (moduleMethod.hasAnnotation(TypeNames.PROVIDES)) {
145                   bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
146                 }
147                 if (moduleMethod.hasAnnotation(TypeNames.PRODUCES)) {
148                   bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
149                 }
150                 if (moduleMethod.hasAnnotation(TypeNames.BINDS)) {
151                   delegates.add(
152                       bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
153                 }
154                 if (moduleMethod.hasAnnotation(TypeNames.MULTIBINDS)) {
155                   multibindingDeclarations.add(
156                       multibindingDeclarationFactory.forMultibindsMethod(
157                           moduleMethod, moduleElement));
158                 }
159                 if (moduleMethod.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF)) {
160                   optionalDeclarations.add(
161                       optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
162                 }
163               });
164 
165       moduleElement.getEnclosedTypeElements().stream()
166           .filter(XTypeElement::isCompanionObject)
167           .collect(toOptional())
168           .ifPresent(companionModule -> collectCompanionModuleBindings(companionModule, bindings));
169 
170       return new AutoValue_ModuleDescriptor(
171           moduleElement,
172           bindings.build(),
173           multibindingDeclarations.build(),
174           subcomponentDeclarationFactory.forModule(moduleElement),
175           delegates.build(),
176           optionalDeclarations.build(),
177           ModuleKind.forAnnotatedElement(moduleElement).get());
178     }
179 
collectCompanionModuleBindings( XTypeElement companionModule, ImmutableSet.Builder<ContributionBinding> bindings)180     private void collectCompanionModuleBindings(
181         XTypeElement companionModule, ImmutableSet.Builder<ContributionBinding> bindings) {
182       ImmutableSet<String> bindingElementDescriptors =
183           bindings.build().stream()
184               .map(binding -> asMethod(binding.bindingElement().get()).getJvmDescriptor())
185               .collect(toImmutableSet());
186 
187       XTypeElements.getAllMethods(companionModule).stream()
188           // Binding methods in companion objects with @JvmStatic are mirrored in the enclosing
189           // class, therefore we should ignore it or else it'll be a duplicate binding.
190           .filter(method -> !method.hasAnnotation(TypeNames.JVM_STATIC))
191           // Fallback strategy for de-duping contributing bindings in the companion module with
192           // @JvmStatic by comparing descriptors. Contributing bindings are the only valid bindings
193           // a companion module can declare. See: https://youtrack.jetbrains.com/issue/KT-35104
194           // TODO(danysantiago): Checks qualifiers too.
195           .filter(method -> !bindingElementDescriptors.contains(method.getJvmDescriptor()))
196           .forEach(
197               method -> {
198                 if (method.hasAnnotation(TypeNames.PROVIDES)) {
199                   bindings.add(bindingFactory.providesMethodBinding(method, companionModule));
200                 }
201                 if (method.hasAnnotation(TypeNames.PRODUCES)) {
202                   bindings.add(bindingFactory.producesMethodBinding(method, companionModule));
203                 }
204               });
205     }
206 
207     /** Returns all the modules transitively included by given modules, including the arguments. */
transitiveModules(Collection<XTypeElement> modules)208     ImmutableSet<ModuleDescriptor> transitiveModules(Collection<XTypeElement> modules) {
209       // Traverse as a graph to automatically handle modules with cyclic includes.
210       return ImmutableSet.copyOf(
211           Traverser.forGraph(
212                   (ModuleDescriptor module) -> transform(includedModules(module), this::create))
213               .depthFirstPreOrder(transform(modules, this::create)));
214     }
215 
includedModules(ModuleDescriptor moduleDescriptor)216     private ImmutableSet<XTypeElement> includedModules(ModuleDescriptor moduleDescriptor) {
217       return ImmutableSet.copyOf(
218           collectIncludedModules(new LinkedHashSet<>(), moduleDescriptor.moduleElement()));
219     }
220 
collectIncludedModules( Set<XTypeElement> includedModules, XTypeElement moduleElement)221     private Set<XTypeElement> collectIncludedModules(
222         Set<XTypeElement> includedModules, XTypeElement moduleElement) {
223       XType superclass = moduleElement.getSuperType();
224       if (superclass != null) {
225         verify(isDeclared(superclass));
226         if (!TypeName.OBJECT.equals(superclass.getTypeName())) {
227           collectIncludedModules(includedModules, superclass.getTypeElement());
228         }
229       }
230       moduleAnnotation(moduleElement, superficialValidation)
231           .ifPresent(
232               moduleAnnotation -> {
233                 includedModules.addAll(moduleAnnotation.includes());
234                 includedModules.addAll(implicitlyIncludedModules(moduleElement));
235               });
236       return includedModules;
237     }
238 
239     private static final ClassName CONTRIBUTES_ANDROID_INJECTOR =
240         ClassName.get("dagger.android", "ContributesAndroidInjector");
241 
242     // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing
243     // module
implicitlyIncludedModules(XTypeElement module)244     private ImmutableSet<XTypeElement> implicitlyIncludedModules(XTypeElement module) {
245       if (processingEnv.findTypeElement(CONTRIBUTES_ANDROID_INJECTOR) == null) {
246         return ImmutableSet.of();
247       }
248       return module.getDeclaredMethods().stream()
249           .filter(method -> method.hasAnnotation(CONTRIBUTES_ANDROID_INJECTOR))
250           .map(
251               method ->
252                   DaggerSuperficialValidation.requireTypeElement(
253                       processingEnv, implicitlyIncludedModuleName(module, method)))
254           .collect(toImmutableSet());
255     }
256 
implicitlyIncludedModuleName(XTypeElement module, XMethodElement method)257     private ClassName implicitlyIncludedModuleName(XTypeElement module, XMethodElement method) {
258       return ClassName.get(
259           module.getPackageName(),
260           String.format(
261               "%s_%s",
262               classFileName(module.getClassName()),
263               LOWER_CAMEL.to(UPPER_CAMEL, getSimpleName(method))));
264     }
265 
266     @Override
clearCache()267     public void clearCache() {
268       cache.clear();
269     }
270   }
271 }
272