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.internal.codegen.model.BindingKind.DELEGATE; 26 27 import androidx.room.compiler.processing.XProcessingEnv; 28 import androidx.room.compiler.processing.XType; 29 import com.squareup.javapoet.ClassName; 30 import com.squareup.javapoet.CodeBlock; 31 import dagger.assisted.Assisted; 32 import dagger.assisted.AssistedFactory; 33 import dagger.assisted.AssistedInject; 34 import dagger.internal.codegen.binding.Binding; 35 import dagger.internal.codegen.binding.BindingGraph; 36 import dagger.internal.codegen.binding.BindsTypeChecker; 37 import dagger.internal.codegen.binding.ContributionBinding; 38 import dagger.internal.codegen.javapoet.Expression; 39 import dagger.internal.codegen.javapoet.TypeNames; 40 import dagger.internal.codegen.model.RequestKind; 41 import dagger.internal.codegen.xprocessing.XTypes; 42 43 /** A {@link dagger.internal.codegen.writing.RequestRepresentation} for {@code @Binds} methods. */ 44 final class DelegateRequestRepresentation extends RequestRepresentation { 45 private final ContributionBinding binding; 46 private final RequestKind requestKind; 47 private final ComponentRequestRepresentations componentRequestRepresentations; 48 private final XProcessingEnv processingEnv; 49 private final BindsTypeChecker bindsTypeChecker; 50 51 @AssistedInject DelegateRequestRepresentation( @ssisted ContributionBinding binding, @Assisted RequestKind requestKind, ComponentRequestRepresentations componentRequestRepresentations, BindsTypeChecker bindsTypeChecker, XProcessingEnv processingEnv)52 DelegateRequestRepresentation( 53 @Assisted ContributionBinding binding, 54 @Assisted RequestKind requestKind, 55 ComponentRequestRepresentations componentRequestRepresentations, 56 BindsTypeChecker bindsTypeChecker, 57 XProcessingEnv processingEnv) { 58 this.binding = checkNotNull(binding); 59 this.requestKind = checkNotNull(requestKind); 60 this.componentRequestRepresentations = componentRequestRepresentations; 61 this.processingEnv = processingEnv; 62 this.bindsTypeChecker = bindsTypeChecker; 63 } 64 65 /** 66 * Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the 67 * binding it depends on. 68 */ isBindsScopeStrongerThanDependencyScope( ContributionBinding bindsBinding, BindingGraph graph)69 static boolean isBindsScopeStrongerThanDependencyScope( 70 ContributionBinding bindsBinding, BindingGraph graph) { 71 checkArgument(bindsBinding.kind().equals(DELEGATE)); 72 Binding dependencyBinding = 73 graph.contributionBinding(getOnlyElement(bindsBinding.dependencies()).key()); 74 ScopeKind bindsScope = ScopeKind.get(bindsBinding); 75 ScopeKind dependencyScope = ScopeKind.get(dependencyBinding); 76 return bindsScope.isStrongerScopeThan(dependencyScope); 77 } 78 79 @Override getDependencyExpression(ClassName requestingClass)80 Expression getDependencyExpression(ClassName requestingClass) { 81 Expression delegateExpression = 82 componentRequestRepresentations.getDependencyExpression( 83 bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind), 84 requestingClass); 85 86 XType contributedType = binding.contributedType(); 87 switch (requestKind) { 88 case INSTANCE: 89 return instanceRequiresCast(binding, delegateExpression, requestingClass, bindsTypeChecker) 90 ? delegateExpression.castTo(contributedType) 91 : delegateExpression; 92 default: 93 XType requestedType = requestType(requestKind, contributedType, processingEnv); 94 if (XTypes.isTypeOf(requestedType, TypeNames.PROVIDER)) { 95 // Even though the user may have requested a javax Provider, our generated code and 96 // factories only work in the Dagger Provider type, so swap to that one before doing 97 // a cast. 98 requestedType = XTypes.rewrapType(requestedType, TypeNames.DAGGER_PROVIDER); 99 } 100 return castToRawTypeIfNecessary(delegateExpression, requestedType); 101 } 102 } 103 instanceRequiresCast( ContributionBinding binding, Expression delegateExpression, ClassName requestingClass, BindsTypeChecker bindsTypeChecker)104 static boolean instanceRequiresCast( 105 ContributionBinding binding, 106 Expression delegateExpression, 107 ClassName requestingClass, 108 BindsTypeChecker bindsTypeChecker) { 109 // delegateExpression.type() could be Object if expression is satisfied with a raw 110 // Provider's get() method. 111 XType contributedType = binding.contributedType(); 112 return !bindsTypeChecker.isAssignable( 113 delegateExpression.type(), contributedType, binding.contributionType()) 114 && isTypeAccessibleFrom(contributedType, requestingClass.packageName()); 115 } 116 117 /** 118 * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code 119 * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw 120 * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise, 121 * returns a {@link Expression#castTo(XType) casted} version of {@code delegateExpression} to the 122 * raw type of {@code desiredType}. 123 */ 124 // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods castToRawTypeIfNecessary(Expression delegateExpression, XType desiredType)125 private Expression castToRawTypeIfNecessary(Expression delegateExpression, XType desiredType) { 126 if (delegateExpression.type().isAssignableTo(desiredType)) { 127 return delegateExpression; 128 } 129 Expression castedExpression = delegateExpression.castTo(desiredType.getRawType()); 130 // Casted raw type provider expression has to be wrapped parentheses, otherwise there 131 // will be an error when DerivedFromFrameworkInstanceRequestRepresentation appends a `get()` to 132 // it. 133 // TODO(wanyingd): change the logic to only add parenthesis when necessary. 134 return Expression.create( 135 castedExpression.type(), CodeBlock.of("($L)", castedExpression.codeBlock())); 136 } 137 138 private enum ScopeKind { 139 UNSCOPED, 140 SINGLE_CHECK, 141 DOUBLE_CHECK, 142 ; 143 get(Binding binding)144 static ScopeKind get(Binding binding) { 145 return binding 146 .scope() 147 .map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK) 148 .orElse(UNSCOPED); 149 } 150 isStrongerScopeThan(ScopeKind other)151 boolean isStrongerScopeThan(ScopeKind other) { 152 return this.ordinal() > other.ordinal(); 153 } 154 } 155 156 @AssistedFactory 157 static interface Factory { create(ContributionBinding binding, RequestKind requestKind)158 DelegateRequestRepresentation create(ContributionBinding binding, RequestKind requestKind); 159 } 160 } 161