blob: ecdc8f288daf37bf5924d6da55803d7eeb147886 [file] [log] [blame]
/*
* Copyright (C) 2017 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.internal.codegen.writing;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.RequestKinds.requestType;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
import static dagger.model.BindingKind.DELEGATE;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindsTypeChecker;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.model.RequestKind;
import javax.lang.model.type.TypeMirror;
/** A {@link dagger.internal.codegen.writing.BindingExpression} for {@code @Binds} methods. */
final class DelegateBindingExpression extends BindingExpression {
private final ContributionBinding binding;
private final RequestKind requestKind;
private final ComponentBindingExpressions componentBindingExpressions;
private final DaggerTypes types;
private final BindsTypeChecker bindsTypeChecker;
DelegateBindingExpression(
ContributionBinding binding,
RequestKind requestKind,
ComponentBindingExpressions componentBindingExpressions,
DaggerTypes types,
DaggerElements elements) {
this.binding = checkNotNull(binding);
this.requestKind = checkNotNull(requestKind);
this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
this.types = checkNotNull(types);
this.bindsTypeChecker = new BindsTypeChecker(types, elements);
}
/**
* Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
* binding it depends on.
*/
static boolean isBindsScopeStrongerThanDependencyScope(
ContributionBinding bindsBinding, BindingGraph graph) {
checkArgument(bindsBinding.kind().equals(DELEGATE));
Binding dependencyBinding =
graph
.contributionBindings()
.get(getOnlyElement(bindsBinding.dependencies()).key())
.binding();
ScopeKind bindsScope = ScopeKind.get(bindsBinding);
ScopeKind dependencyScope = ScopeKind.get(dependencyBinding);
return bindsScope.isStrongerScopeThan(dependencyScope);
}
@Override
Expression getDependencyExpression(ClassName requestingClass) {
Expression delegateExpression =
componentBindingExpressions.getDependencyExpression(
bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
requestingClass);
TypeMirror contributedType = binding.contributedType();
switch (requestKind) {
case INSTANCE:
return instanceRequiresCast(delegateExpression, requestingClass)
? delegateExpression.castTo(contributedType)
: delegateExpression;
default:
return castToRawTypeIfNecessary(
delegateExpression, requestType(requestKind, contributedType, types));
}
}
private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
// delegateExpression.type() could be Object if expression is satisfied with a raw
// Provider's get() method.
return !bindsTypeChecker.isAssignable(
delegateExpression.type(), binding.contributedType(), binding.contributionType())
&& isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
}
/**
* If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
* delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
* type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
* returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
* to the raw type of {@code desiredType}.
*/
// TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
private Expression castToRawTypeIfNecessary(
Expression delegateExpression, TypeMirror desiredType) {
if (types.isAssignable(delegateExpression.type(), desiredType)) {
return delegateExpression;
}
return delegateExpression.castTo(types.erasure(desiredType));
}
private enum ScopeKind {
UNSCOPED,
SINGLE_CHECK,
DOUBLE_CHECK,
;
static ScopeKind get(Binding binding) {
return binding
.scope()
.map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
.orElse(UNSCOPED);
}
boolean isStrongerScopeThan(ScopeKind other) {
return this.ordinal() > other.ordinal();
}
}
}