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.auto.common.MoreElements.asExecutable; 20 import static com.google.auto.common.MoreElements.getPackage; 21 import static com.google.auto.common.MoreElements.isAnnotationPresent; 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.Preconditions.checkArgument; 25 import static com.google.common.base.Verify.verify; 26 import static com.google.common.collect.Iterables.transform; 27 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation; 28 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; 29 import static dagger.internal.codegen.binding.SourceFiles.classFileName; 30 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 31 import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor; 32 import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent; 33 import static javax.lang.model.type.TypeKind.DECLARED; 34 import static javax.lang.model.type.TypeKind.NONE; 35 import static javax.lang.model.util.ElementFilter.methodsIn; 36 37 import com.google.auto.common.MoreElements; 38 import com.google.auto.common.MoreTypes; 39 import com.google.auto.value.AutoValue; 40 import com.google.auto.value.extension.memoized.Memoized; 41 import com.google.common.collect.ImmutableSet; 42 import com.google.common.graph.Traverser; 43 import com.google.errorprone.annotations.CanIgnoreReturnValue; 44 import com.squareup.javapoet.ClassName; 45 import dagger.Binds; 46 import dagger.BindsOptionalOf; 47 import dagger.Module; 48 import dagger.Provides; 49 import dagger.internal.codegen.base.ClearableCache; 50 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 51 import dagger.internal.codegen.langmodel.DaggerElements; 52 import dagger.model.Key; 53 import dagger.multibindings.Multibinds; 54 import dagger.producers.Produces; 55 import java.util.HashMap; 56 import java.util.LinkedHashSet; 57 import java.util.Map; 58 import java.util.Set; 59 import javax.inject.Inject; 60 import javax.inject.Singleton; 61 import javax.lang.model.element.ExecutableElement; 62 import javax.lang.model.element.TypeElement; 63 import javax.lang.model.type.TypeMirror; 64 65 /** Contains metadata that describes a module. */ 66 @AutoValue 67 public abstract class ModuleDescriptor { 68 moduleElement()69 public abstract TypeElement moduleElement(); 70 includedModules()71 abstract ImmutableSet<TypeElement> includedModules(); 72 bindings()73 public abstract ImmutableSet<ContributionBinding> bindings(); 74 75 /** The multibinding declarations contained in this module. */ multibindingDeclarations()76 abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations(); 77 78 /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */ subcomponentDeclarations()79 abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations(); 80 81 /** The {@link Binds} method declarations that define delegate bindings. */ delegateDeclarations()82 abstract ImmutableSet<DelegateDeclaration> delegateDeclarations(); 83 84 /** The {@link BindsOptionalOf} method declarations that define optional bindings. */ optionalDeclarations()85 abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations(); 86 87 /** The kind of the module. */ kind()88 public abstract ModuleKind kind(); 89 90 /** Returns all of the bindings declared in this module. */ 91 @Memoized allBindingDeclarations()92 public ImmutableSet<BindingDeclaration> allBindingDeclarations() { 93 return ImmutableSet.<BindingDeclaration>builder() 94 .addAll(bindings()) 95 .addAll(delegateDeclarations()) 96 .addAll(multibindingDeclarations()) 97 .addAll(optionalDeclarations()) 98 .addAll(subcomponentDeclarations()) 99 .build(); 100 } 101 102 /** Returns the keys of all bindings declared by this module. */ allBindingKeys()103 ImmutableSet<Key> allBindingKeys() { 104 return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet()); 105 } 106 107 /** A {@link ModuleDescriptor} factory. */ 108 @Singleton 109 public static final class Factory implements ClearableCache { 110 private final DaggerElements elements; 111 private final KotlinMetadataUtil metadataUtil; 112 private final BindingFactory bindingFactory; 113 private final MultibindingDeclaration.Factory multibindingDeclarationFactory; 114 private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory; 115 private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory; 116 private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory; 117 private final Map<TypeElement, ModuleDescriptor> cache = new HashMap<>(); 118 119 @Inject Factory( DaggerElements elements, KotlinMetadataUtil metadataUtil, BindingFactory bindingFactory, MultibindingDeclaration.Factory multibindingDeclarationFactory, DelegateDeclaration.Factory bindingDelegateDeclarationFactory, SubcomponentDeclaration.Factory subcomponentDeclarationFactory, OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory)120 Factory( 121 DaggerElements elements, 122 KotlinMetadataUtil metadataUtil, 123 BindingFactory bindingFactory, 124 MultibindingDeclaration.Factory multibindingDeclarationFactory, 125 DelegateDeclaration.Factory bindingDelegateDeclarationFactory, 126 SubcomponentDeclaration.Factory subcomponentDeclarationFactory, 127 OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) { 128 this.elements = elements; 129 this.metadataUtil = metadataUtil; 130 this.bindingFactory = bindingFactory; 131 this.multibindingDeclarationFactory = multibindingDeclarationFactory; 132 this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory; 133 this.subcomponentDeclarationFactory = subcomponentDeclarationFactory; 134 this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory; 135 } 136 create(TypeElement moduleElement)137 public ModuleDescriptor create(TypeElement moduleElement) { 138 return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached); 139 } 140 createUncached(TypeElement moduleElement)141 public ModuleDescriptor createUncached(TypeElement moduleElement) { 142 ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder(); 143 ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder(); 144 ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = 145 ImmutableSet.builder(); 146 ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations = 147 ImmutableSet.builder(); 148 149 for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) { 150 if (isAnnotationPresent(moduleMethod, Provides.class)) { 151 bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement)); 152 } 153 if (isAnnotationPresent(moduleMethod, Produces.class)) { 154 bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement)); 155 } 156 if (isAnnotationPresent(moduleMethod, Binds.class)) { 157 delegates.add(bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement)); 158 } 159 if (isAnnotationPresent(moduleMethod, Multibinds.class)) { 160 multibindingDeclarations.add( 161 multibindingDeclarationFactory.forMultibindsMethod(moduleMethod, moduleElement)); 162 } 163 if (isAnnotationPresent(moduleMethod, BindsOptionalOf.class)) { 164 optionalDeclarations.add( 165 optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement)); 166 } 167 } 168 169 if (metadataUtil.hasEnclosedCompanionObject(moduleElement)) { 170 collectCompanionModuleBindings(moduleElement, bindings); 171 } 172 173 return new AutoValue_ModuleDescriptor( 174 moduleElement, 175 ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)), 176 bindings.build(), 177 multibindingDeclarations.build(), 178 subcomponentDeclarationFactory.forModule(moduleElement), 179 delegates.build(), 180 optionalDeclarations.build(), 181 ModuleKind.forAnnotatedElement(moduleElement).get()); 182 } 183 collectCompanionModuleBindings( TypeElement moduleElement, ImmutableSet.Builder<ContributionBinding> bindings)184 private void collectCompanionModuleBindings( 185 TypeElement moduleElement, ImmutableSet.Builder<ContributionBinding> bindings) { 186 checkArgument(metadataUtil.hasEnclosedCompanionObject(moduleElement)); 187 TypeElement companionModule = metadataUtil.getEnclosedCompanionObject(moduleElement); 188 ImmutableSet<String> bindingElementDescriptors = 189 bindings.build().stream() 190 .map(binding -> getMethodDescriptor(asExecutable(binding.bindingElement().get()))) 191 .collect(toImmutableSet()); 192 methodsIn(elements.getAllMembers(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 -> !KotlinMetadataUtil.isJvmStaticPresent(method)) 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(getMethodDescriptor(method))) 201 .forEach( 202 method -> { 203 if (isAnnotationPresent(method, Provides.class)) { 204 bindings.add(bindingFactory.providesMethodBinding(method, companionModule)); 205 } 206 if (isAnnotationPresent(method, Produces.class)) { 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(Iterable<TypeElement> modules)213 ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) { 214 return ImmutableSet.copyOf( 215 Traverser.forGraph( 216 (ModuleDescriptor module) -> transform(module.includedModules(), this::create)) 217 .depthFirstPreOrder(transform(modules, this::create))); 218 } 219 220 @CanIgnoreReturnValue collectIncludedModules( Set<TypeElement> includedModules, TypeElement moduleElement)221 private Set<TypeElement> collectIncludedModules( 222 Set<TypeElement> includedModules, TypeElement moduleElement) { 223 TypeMirror superclass = moduleElement.getSuperclass(); 224 if (!superclass.getKind().equals(NONE)) { 225 verify(superclass.getKind().equals(DECLARED)); 226 TypeElement superclassElement = MoreTypes.asTypeElement(superclass); 227 if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) { 228 collectIncludedModules(includedModules, superclassElement); 229 } 230 } 231 moduleAnnotation(moduleElement) 232 .ifPresent( 233 moduleAnnotation -> { 234 includedModules.addAll(moduleAnnotation.includes()); 235 includedModules.addAll(implicitlyIncludedModules(moduleElement)); 236 }); 237 return includedModules; 238 } 239 240 // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing 241 // module implicitlyIncludedModules(TypeElement moduleElement)242 private ImmutableSet<TypeElement> implicitlyIncludedModules(TypeElement moduleElement) { 243 TypeElement contributesAndroidInjector = 244 elements.getTypeElement("dagger.android.ContributesAndroidInjector"); 245 if (contributesAndroidInjector == null) { 246 return ImmutableSet.of(); 247 } 248 return methodsIn(moduleElement.getEnclosedElements()).stream() 249 .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType())) 250 .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method))) 251 .collect(toImmutableSet()); 252 } 253 implicitlyIncludedModuleName(ExecutableElement method)254 private String implicitlyIncludedModuleName(ExecutableElement method) { 255 return getPackage(method).getQualifiedName() 256 + "." 257 + classFileName(ClassName.get(MoreElements.asType(method.getEnclosingElement()))) 258 + "_" 259 + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString()); 260 } 261 262 @Override clearCache()263 public void clearCache() { 264 cache.clear(); 265 } 266 } 267 } 268