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