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