• 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.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