1 /*
2  * Copyright (C) 2017 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.writing;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.collect.Iterables.getOnlyElement;
22 import static dagger.internal.codegen.base.RequestKinds.requestType;
23 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
24 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
25 import static dagger.model.BindingKind.DELEGATE;
26 
27 import com.squareup.javapoet.ClassName;
28 import dagger.internal.codegen.binding.Binding;
29 import dagger.internal.codegen.binding.BindingGraph;
30 import dagger.internal.codegen.binding.BindsTypeChecker;
31 import dagger.internal.codegen.binding.ContributionBinding;
32 import dagger.internal.codegen.javapoet.Expression;
33 import dagger.internal.codegen.langmodel.DaggerElements;
34 import dagger.internal.codegen.langmodel.DaggerTypes;
35 import dagger.model.RequestKind;
36 import javax.lang.model.type.TypeMirror;
37 
38 /** A {@link dagger.internal.codegen.writing.BindingExpression} for {@code @Binds} methods. */
39 final class DelegateBindingExpression extends BindingExpression {
40   private final ContributionBinding binding;
41   private final RequestKind requestKind;
42   private final ComponentBindingExpressions componentBindingExpressions;
43   private final DaggerTypes types;
44   private final BindsTypeChecker bindsTypeChecker;
45 
DelegateBindingExpression( ContributionBinding binding, RequestKind requestKind, ComponentBindingExpressions componentBindingExpressions, DaggerTypes types, DaggerElements elements)46   DelegateBindingExpression(
47       ContributionBinding binding,
48       RequestKind requestKind,
49       ComponentBindingExpressions componentBindingExpressions,
50       DaggerTypes types,
51       DaggerElements elements) {
52     this.binding = checkNotNull(binding);
53     this.requestKind = checkNotNull(requestKind);
54     this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
55     this.types = checkNotNull(types);
56     this.bindsTypeChecker = new BindsTypeChecker(types, elements);
57   }
58 
59   /**
60    * Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
61    * binding it depends on.
62    */
isBindsScopeStrongerThanDependencyScope( ContributionBinding bindsBinding, BindingGraph graph)63   static boolean isBindsScopeStrongerThanDependencyScope(
64       ContributionBinding bindsBinding, BindingGraph graph) {
65     checkArgument(bindsBinding.kind().equals(DELEGATE));
66     Binding dependencyBinding =
67         graph.contributionBinding(getOnlyElement(bindsBinding.dependencies()).key());
68     ScopeKind bindsScope = ScopeKind.get(bindsBinding);
69     ScopeKind dependencyScope = ScopeKind.get(dependencyBinding);
70     return bindsScope.isStrongerScopeThan(dependencyScope);
71   }
72 
73   @Override
getDependencyExpression(ClassName requestingClass)74   Expression getDependencyExpression(ClassName requestingClass) {
75     Expression delegateExpression =
76         componentBindingExpressions.getDependencyExpression(
77             bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
78             requestingClass);
79 
80     TypeMirror contributedType = binding.contributedType();
81     switch (requestKind) {
82       case INSTANCE:
83         return instanceRequiresCast(delegateExpression, requestingClass)
84             ? delegateExpression.castTo(contributedType)
85             : delegateExpression;
86       default:
87         return castToRawTypeIfNecessary(
88             delegateExpression, requestType(requestKind, contributedType, types));
89     }
90   }
91 
instanceRequiresCast(Expression delegateExpression, ClassName requestingClass)92   private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
93     // delegateExpression.type() could be Object if expression is satisfied with a raw
94     // Provider's get() method.
95     return !bindsTypeChecker.isAssignable(
96         delegateExpression.type(), binding.contributedType(), binding.contributionType())
97         && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
98   }
99 
100   /**
101    * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
102    * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
103    * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
104    * returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
105    * to the raw type of {@code desiredType}.
106    */
107   // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
castToRawTypeIfNecessary( Expression delegateExpression, TypeMirror desiredType)108   private Expression castToRawTypeIfNecessary(
109       Expression delegateExpression, TypeMirror desiredType) {
110     if (types.isAssignable(delegateExpression.type(), desiredType)) {
111       return delegateExpression;
112     }
113     return delegateExpression.castTo(types.erasure(desiredType));
114   }
115 
116   private enum ScopeKind {
117     UNSCOPED,
118     SINGLE_CHECK,
119     DOUBLE_CHECK,
120     ;
121 
get(Binding binding)122     static ScopeKind get(Binding binding) {
123       return binding
124           .scope()
125           .map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
126           .orElse(UNSCOPED);
127     }
128 
isStrongerScopeThan(ScopeKind other)129     boolean isStrongerScopeThan(ScopeKind other) {
130       return this.ordinal() > other.ordinal();
131     }
132   }
133 }
134