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.xprocessing.XElements.getSimpleName; 23 import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation; 24 import static dagger.internal.codegen.xprocessing.XTypeElements.isNested; 25 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 26 27 import androidx.room.compiler.processing.XElement; 28 import androidx.room.compiler.processing.XMethodElement; 29 import androidx.room.compiler.processing.XType; 30 import androidx.room.compiler.processing.XTypeElement; 31 import com.google.auto.value.AutoValue; 32 import com.squareup.javapoet.ParameterSpec; 33 import com.squareup.javapoet.TypeName; 34 import dagger.internal.codegen.javapoet.TypeNames; 35 import dagger.internal.codegen.model.BindingKind; 36 import dagger.internal.codegen.model.Key; 37 import dagger.internal.codegen.xprocessing.Nullability; 38 import dagger.internal.codegen.xprocessing.XTypeElements; 39 import java.util.Optional; 40 41 /** A type that a component needs an instance of. */ 42 @AutoValue 43 public abstract class ComponentRequirement { 44 /** The kind of the {@link ComponentRequirement}. */ 45 public enum Kind { 46 /** A type listed in the component's {@code dependencies} attribute. */ 47 DEPENDENCY, 48 49 /** A type listed in the component or subcomponent's {@code modules} attribute. */ 50 MODULE, 51 52 /** 53 * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method. 54 */ 55 BOUND_INSTANCE, 56 ; 57 isBoundInstance()58 public boolean isBoundInstance() { 59 return equals(BOUND_INSTANCE); 60 } 61 isModule()62 public boolean isModule() { 63 return equals(MODULE); 64 } 65 } 66 67 private XType type; 68 69 /** The kind of requirement. */ kind()70 public abstract Kind kind(); 71 72 /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */ 73 // TODO(ronshapiro): consider removing this and inlining the usages isBoundInstance()74 final boolean isBoundInstance() { 75 return kind().isBoundInstance(); 76 } 77 78 /** The type of the instance the component must have. */ typeName()79 abstract TypeName typeName(); 80 81 /** The type of the instance the component must have. */ type()82 public XType type() { 83 return type; 84 } 85 86 /** The element associated with the type of this requirement. */ typeElement()87 public XTypeElement typeElement() { 88 return type.getTypeElement(); 89 } 90 91 /** The action a component builder should take if it {@code null} is passed. */ 92 public enum NullPolicy { 93 /** Make a new instance. */ 94 NEW, 95 /** Throw an exception. */ 96 THROW, 97 /** Allow use of null values. */ 98 ALLOW, 99 } 100 101 /** 102 * An override for the requirement's null policy. If set, this is used as the null policy instead 103 * of the default behavior in {@link #nullPolicy}. 104 * 105 * <p>Some implementations' null policy can be determined upon construction (e.g., for binding 106 * instances), but others' require Elements which must wait until {@link #nullPolicy} is called. 107 */ overrideNullPolicy()108 abstract Optional<NullPolicy> overrideNullPolicy(); 109 110 /** 111 * The nullability of the requirement. If set, this is used to determine the nullability of the 112 * requirement's type. 113 */ getNullability()114 public Nullability getNullability() { 115 return nullability; 116 } 117 118 private Nullability nullability = Nullability.NOT_NULLABLE; 119 120 /** The requirement's null policy. */ nullPolicy()121 public NullPolicy nullPolicy() { 122 if (overrideNullPolicy().isPresent()) { 123 return overrideNullPolicy().get(); 124 } 125 switch (kind()) { 126 case MODULE: 127 return componentCanMakeNewInstances(typeElement()) 128 ? NullPolicy.NEW 129 : requiresAPassedInstance() ? 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()141 public boolean requiresAPassedInstance() { 142 if (!kind().isModule()) { 143 // Bound instances and dependencies always require the user to provide an instance. 144 return true; 145 } 146 return requiresModuleInstance() && !componentCanMakeNewInstances(typeElement()); 147 } 148 149 /** 150 * Returns {@code true} if an instance is needed for this (module) requirement. 151 * 152 * <p>An instance is only needed if there is a binding method on the module that is neither {@code 153 * abstract} nor {@code static}; if all bindings are one of those, then there should be no 154 * possible dependency on instance state in the module's bindings. 155 * 156 * <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered 157 * {@code static}, requiring no module instance. 158 */ requiresModuleInstance()159 public boolean requiresModuleInstance() { 160 if (typeElement().isKotlinObject() || typeElement().isCompanionObject()) { 161 return false; 162 } 163 return XTypeElements.getAllNonPrivateInstanceMethods(typeElement()).stream() 164 .filter(this::isBindingMethod) 165 .anyMatch(method -> !method.isAbstract() && !method.isStatic()); 166 } 167 isBindingMethod(XMethodElement method)168 private boolean isBindingMethod(XMethodElement method) { 169 // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff 170 // in one place; listing individual annotations all over the place is brittle. 171 return hasAnyAnnotation( 172 method, 173 TypeNames.PROVIDES, 174 TypeNames.PRODUCES, 175 // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe 176 // these, like @AbstractBindingMethod 177 TypeNames.BINDS, 178 TypeNames.MULTIBINDS, 179 TypeNames.BINDS_OPTIONAL_OF); 180 } 181 182 /** The key for this requirement, if one is available. */ key()183 public abstract Optional<Key> key(); 184 185 /** Returns the name for this requirement that could be used as a variable. */ variableName()186 public abstract String variableName(); 187 188 /** Returns a parameter spec for this requirement. */ toParameterSpec()189 public ParameterSpec toParameterSpec() { 190 return ParameterSpec.builder(type().getTypeName(), variableName()).build(); 191 } 192 forDependency(ComponentDependencyBinding binding)193 public static ComponentRequirement forDependency(ComponentDependencyBinding binding) { 194 return forDependency(binding.key().type().xprocessing()); 195 } 196 forDependency(XType type)197 public static ComponentRequirement forDependency(XType type) { 198 checkArgument(isDeclared(checkNotNull(type))); 199 return create(Kind.DEPENDENCY, type); 200 } 201 forModule(XType type)202 public static ComponentRequirement forModule(XType type) { 203 checkArgument(isDeclared(checkNotNull(type))); 204 return create(Kind.MODULE, type); 205 } 206 forBoundInstance(BoundInstanceBinding binding)207 public static ComponentRequirement forBoundInstance(BoundInstanceBinding binding) { 208 checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE)); 209 return forBoundInstance( 210 binding.key(), binding.isNullable(), binding.bindingElement().get(), binding.nullability()); 211 } 212 forBoundInstance( Key key, boolean nullable, XElement elementForVariableName, Nullability nullability)213 static ComponentRequirement forBoundInstance( 214 Key key, boolean nullable, XElement elementForVariableName, Nullability nullability) { 215 return create( 216 Kind.BOUND_INSTANCE, 217 key.type().xprocessing(), 218 nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(), 219 Optional.of(key), 220 nullability, 221 getSimpleName(elementForVariableName)); 222 } 223 create(Kind kind, XType type)224 private static ComponentRequirement create(Kind kind, XType type) { 225 return create( 226 kind, 227 type, 228 /* overrideNullPolicy= */ Optional.empty(), 229 /* key= */ Optional.empty(), 230 Nullability.NOT_NULLABLE, 231 simpleVariableName(type.getTypeElement().asClassName())); 232 } 233 create( Kind kind, XType type, Optional<NullPolicy> overrideNullPolicy, Optional<Key> key, Nullability nullability, String variableName)234 private static ComponentRequirement create( 235 Kind kind, 236 XType type, 237 Optional<NullPolicy> overrideNullPolicy, 238 Optional<Key> key, 239 Nullability nullability, 240 String variableName) { 241 ComponentRequirement requirement = 242 new AutoValue_ComponentRequirement( 243 kind, type.getTypeName(), overrideNullPolicy, key, variableName); 244 requirement.nullability = nullability; 245 requirement.type = type; 246 return requirement; 247 } 248 249 /** 250 * Returns true if and only if a component can instantiate new instances (typically of a module) 251 * rather than requiring that they be passed. 252 */ 253 // TODO(bcorso): Should this method throw if its called knowing that an instance is not needed? componentCanMakeNewInstances(XTypeElement typeElement)254 public static boolean componentCanMakeNewInstances(XTypeElement typeElement) { 255 // TODO(gak): still need checks for visibility 256 return typeElement.isClass() 257 && !typeElement.isAbstract() 258 && !requiresEnclosingInstance(typeElement) 259 && hasVisibleDefaultConstructor(typeElement); 260 } 261 requiresEnclosingInstance(XTypeElement typeElement)262 private static boolean requiresEnclosingInstance(XTypeElement typeElement) { 263 return isNested(typeElement) && !typeElement.isStatic(); 264 } 265 hasVisibleDefaultConstructor(XTypeElement typeElement)266 private static boolean hasVisibleDefaultConstructor(XTypeElement typeElement) { 267 return typeElement.getConstructors().stream() 268 .anyMatch(constructor -> !constructor.isPrivate() && constructor.getParameters().isEmpty()); 269 } 270 } 271