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