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