1 /* 2 * Copyright (C) 2016 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.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName; 22 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent; 23 import static javax.lang.model.element.ElementKind.CONSTRUCTOR; 24 import static javax.lang.model.element.Modifier.ABSTRACT; 25 import static javax.lang.model.element.Modifier.PRIVATE; 26 import static javax.lang.model.element.Modifier.STATIC; 27 28 import com.google.auto.common.MoreElements; 29 import com.google.auto.common.MoreTypes; 30 import com.google.auto.value.AutoValue; 31 import com.google.common.base.Equivalence; 32 import com.google.common.collect.ImmutableSet; 33 import com.squareup.javapoet.ParameterSpec; 34 import com.squareup.javapoet.TypeName; 35 import dagger.Binds; 36 import dagger.BindsOptionalOf; 37 import dagger.Provides; 38 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 39 import dagger.internal.codegen.langmodel.DaggerElements; 40 import dagger.model.BindingKind; 41 import dagger.model.Key; 42 import dagger.multibindings.Multibinds; 43 import dagger.producers.Produces; 44 import java.util.Optional; 45 import javax.lang.model.element.Element; 46 import javax.lang.model.element.ExecutableElement; 47 import javax.lang.model.element.TypeElement; 48 import javax.lang.model.type.TypeMirror; 49 50 /** A type that a component needs an instance of. */ 51 @AutoValue 52 public abstract class ComponentRequirement { 53 /** The kind of the {@link ComponentRequirement}. */ 54 public enum Kind { 55 /** A type listed in the component's {@code dependencies} attribute. */ 56 DEPENDENCY, 57 58 /** A type listed in the component or subcomponent's {@code modules} attribute. */ 59 MODULE, 60 61 /** 62 * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method. 63 */ 64 BOUND_INSTANCE, 65 ; 66 isBoundInstance()67 public boolean isBoundInstance() { 68 return equals(BOUND_INSTANCE); 69 } 70 isModule()71 public boolean isModule() { 72 return equals(MODULE); 73 } 74 } 75 76 /** The kind of requirement. */ kind()77 public abstract Kind kind(); 78 79 /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */ 80 // TODO(ronshapiro): consider removing this and inlining the usages isBoundInstance()81 final boolean isBoundInstance() { 82 return kind().isBoundInstance(); 83 } 84 85 /** 86 * The type of the instance the component must have, wrapped so that requirements can be used as 87 * value types. 88 */ wrappedType()89 public abstract Equivalence.Wrapper<TypeMirror> wrappedType(); 90 91 /** The type of the instance the component must have. */ type()92 public TypeMirror type() { 93 return wrappedType().get(); 94 } 95 96 /** The element associated with the type of this requirement. */ typeElement()97 public TypeElement typeElement() { 98 return MoreTypes.asTypeElement(type()); 99 } 100 101 /** The action a component builder should take if it {@code null} is passed. */ 102 public enum NullPolicy { 103 /** Make a new instance. */ 104 NEW, 105 /** Throw an exception. */ 106 THROW, 107 /** Allow use of null values. */ 108 ALLOW, 109 } 110 111 /** 112 * An override for the requirement's null policy. If set, this is used as the null policy instead 113 * of the default behavior in {@link #nullPolicy}. 114 * 115 * <p>Some implementations' null policy can be determined upon construction (e.g., for binding 116 * instances), but others' require Elements which must wait until {@link #nullPolicy} is called. 117 */ overrideNullPolicy()118 abstract Optional<NullPolicy> overrideNullPolicy(); 119 120 /** The requirement's null policy. */ nullPolicy(DaggerElements elements, KotlinMetadataUtil metadataUtil)121 public NullPolicy nullPolicy(DaggerElements elements, KotlinMetadataUtil metadataUtil) { 122 if (overrideNullPolicy().isPresent()) { 123 return overrideNullPolicy().get(); 124 } 125 switch (kind()) { 126 case MODULE: 127 return componentCanMakeNewInstances(typeElement(), metadataUtil) 128 ? NullPolicy.NEW 129 : requiresAPassedInstance(elements, metadataUtil) ? NullPolicy.THROW : NullPolicy.ALLOW; 130 case DEPENDENCY: 131 case BOUND_INSTANCE: 132 return NullPolicy.THROW; 133 } 134 throw new AssertionError(); 135 } 136 137 /** 138 * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to 139 * be used within a component. 140 */ requiresAPassedInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil)141 public boolean requiresAPassedInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil) { 142 if (!kind().isModule()) { 143 // Bound instances and dependencies always require the user to provide an instance. 144 return true; 145 } 146 return requiresModuleInstance(elements, metadataUtil) 147 && !componentCanMakeNewInstances(typeElement(), metadataUtil); 148 } 149 150 /** 151 * Returns {@code true} if an instance is needed for this (module) requirement. 152 * 153 * <p>An instance is only needed if there is a binding method on the module that is neither {@code 154 * abstract} nor {@code static}; if all bindings are one of those, then there should be no 155 * possible dependency on instance state in the module's bindings. 156 * 157 * <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered 158 * {@code static}, requiring no module instance. 159 */ requiresModuleInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil)160 private boolean requiresModuleInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil) { 161 boolean isKotlinObject = 162 metadataUtil.isObjectClass(typeElement()) 163 || metadataUtil.isCompanionObjectClass(typeElement()); 164 if (isKotlinObject) { 165 return false; 166 } 167 168 ImmutableSet<ExecutableElement> methods = elements.getLocalAndInheritedMethods(typeElement()); 169 return methods.stream() 170 .filter(this::isBindingMethod) 171 .map(ExecutableElement::getModifiers) 172 .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC)); 173 } 174 isBindingMethod(ExecutableElement method)175 private boolean isBindingMethod(ExecutableElement method) { 176 // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff 177 // in one place; listing individual annotations all over the place is brittle. 178 return isAnyAnnotationPresent( 179 method, 180 Provides.class, 181 Produces.class, 182 // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe 183 // these, like @AbstractBindingMethod 184 Binds.class, 185 Multibinds.class, 186 BindsOptionalOf.class); 187 } 188 189 /** The key for this requirement, if one is available. */ key()190 public abstract Optional<Key> key(); 191 192 /** Returns the name for this requirement that could be used as a variable. */ variableName()193 public abstract String variableName(); 194 195 /** Returns a parameter spec for this requirement. */ toParameterSpec()196 public ParameterSpec toParameterSpec() { 197 return ParameterSpec.builder(TypeName.get(type()), variableName()).build(); 198 } 199 forDependency(TypeMirror type)200 public static ComponentRequirement forDependency(TypeMirror type) { 201 return new AutoValue_ComponentRequirement( 202 Kind.DEPENDENCY, 203 MoreTypes.equivalence().wrap(checkNotNull(type)), 204 Optional.empty(), 205 Optional.empty(), 206 simpleVariableName(MoreTypes.asTypeElement(type))); 207 } 208 forModule(TypeMirror type)209 public static ComponentRequirement forModule(TypeMirror type) { 210 return new AutoValue_ComponentRequirement( 211 Kind.MODULE, 212 MoreTypes.equivalence().wrap(checkNotNull(type)), 213 Optional.empty(), 214 Optional.empty(), 215 simpleVariableName(MoreTypes.asTypeElement(type))); 216 } 217 forBoundInstance(Key key, boolean nullable, String variableName)218 static ComponentRequirement forBoundInstance(Key key, boolean nullable, String variableName) { 219 return new AutoValue_ComponentRequirement( 220 Kind.BOUND_INSTANCE, 221 MoreTypes.equivalence().wrap(key.type()), 222 nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(), 223 Optional.of(key), 224 variableName); 225 } 226 forBoundInstance(ContributionBinding binding)227 public static ComponentRequirement forBoundInstance(ContributionBinding binding) { 228 checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE)); 229 return forBoundInstance( 230 binding.key(), 231 binding.nullableType().isPresent(), 232 binding.bindingElement().get().getSimpleName().toString()); 233 } 234 235 /** 236 * Returns true if and only if a component can instantiate new instances (typically of a module) 237 * rather than requiring that they be passed. 238 */ 239 // TODO(bcorso): Should this method throw if its called knowing that an instance is not needed? componentCanMakeNewInstances( TypeElement typeElement, KotlinMetadataUtil metadataUtil)240 public static boolean componentCanMakeNewInstances( 241 TypeElement typeElement, KotlinMetadataUtil metadataUtil) { 242 switch (typeElement.getKind()) { 243 case CLASS: 244 break; 245 case ENUM: 246 case ANNOTATION_TYPE: 247 case INTERFACE: 248 return false; 249 default: 250 throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind()); 251 } 252 253 if (typeElement.getModifiers().contains(ABSTRACT)) { 254 return false; 255 } 256 257 if (requiresEnclosingInstance(typeElement)) { 258 return false; 259 } 260 261 if (metadataUtil.isObjectClass(typeElement) 262 || metadataUtil.isCompanionObjectClass(typeElement)) { 263 return false; 264 } 265 266 for (Element enclosed : typeElement.getEnclosedElements()) { 267 if (enclosed.getKind().equals(CONSTRUCTOR) 268 && MoreElements.asExecutable(enclosed).getParameters().isEmpty() 269 && !enclosed.getModifiers().contains(PRIVATE)) { 270 return true; 271 } 272 } 273 274 // TODO(gak): still need checks for visibility 275 276 return false; 277 } 278 requiresEnclosingInstance(TypeElement typeElement)279 private static boolean requiresEnclosingInstance(TypeElement typeElement) { 280 switch (typeElement.getNestingKind()) { 281 case TOP_LEVEL: 282 return false; 283 case MEMBER: 284 return !typeElement.getModifiers().contains(STATIC); 285 case ANONYMOUS: 286 case LOCAL: 287 return true; 288 } 289 throw new AssertionError( 290 "TypeElement cannot have nesting kind: " + typeElement.getNestingKind()); 291 } 292 } 293