forked from square/dagger
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a rollforward of e2bff35 and fixes an issue where Expressions for inline Foo_Factory.create() were not properly typed internally. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171578370
- Loading branch information
1 parent
6d1e6ac
commit e8d7cd4
Showing
15 changed files
with
1,502 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* 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; | ||
|
||
import static com.google.common.collect.Iterables.getOnlyElement; | ||
import static javax.lang.model.util.ElementFilter.methodsIn; | ||
|
||
import com.google.auto.common.MoreElements; | ||
import com.google.auto.common.MoreTypes; | ||
import com.google.common.collect.ImmutableList; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import javax.lang.model.element.ExecutableElement; | ||
import javax.lang.model.element.TypeElement; | ||
import javax.lang.model.type.DeclaredType; | ||
import javax.lang.model.type.TypeMirror; | ||
import javax.lang.model.util.Elements; | ||
import javax.lang.model.util.Types; | ||
|
||
/** | ||
* Checks the assignability of one type to another, given a {@link ContributionType} context. This | ||
* is used by {@link BindsMethodValidator} to validate that the right-hand-side of a {@link | ||
* dagger.Binds} method is valid, as well as in {@link DelegateBindingExpression} when the | ||
* right-hand-side in generated code might be an erased type due to accessibility. | ||
*/ | ||
final class BindsTypeChecker { | ||
private final Types types; | ||
private final Elements elements; | ||
|
||
BindsTypeChecker(Types types, Elements elements) { | ||
this.types = types; | ||
this.elements = elements; | ||
} | ||
|
||
/** | ||
* Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link | ||
* ContributionType} context. | ||
*/ | ||
boolean isAssignable( | ||
TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) { | ||
return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType)); | ||
} | ||
|
||
private TypeMirror desiredAssignableType( | ||
TypeMirror leftHandSide, ContributionType contributionType) { | ||
switch (contributionType) { | ||
case UNIQUE: | ||
return leftHandSide; | ||
case SET: | ||
DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide); | ||
return methodParameterType(parameterizedSetType, "add"); | ||
case SET_VALUES: | ||
return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll"); | ||
case MAP: | ||
DeclaredType parameterizedMapType = | ||
types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide); | ||
return methodParameterTypes(parameterizedMapType, "put").get(1); | ||
default: | ||
throw new AssertionError("Unknown contribution type: " + contributionType); | ||
} | ||
} | ||
|
||
private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) { | ||
ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder(); | ||
for (ExecutableElement method : | ||
methodsIn(MoreElements.asType(type.asElement()).getEnclosedElements())) { | ||
if (method.getSimpleName().contentEquals(methodName)) { | ||
methodsForName.add(method); | ||
} | ||
} | ||
ExecutableElement method = getOnlyElement(methodsForName.build()); | ||
return ImmutableList.copyOf( | ||
MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes()); | ||
} | ||
|
||
private TypeMirror methodParameterType(DeclaredType type, String methodName) { | ||
return getOnlyElement(methodParameterTypes(type, methodName)); | ||
} | ||
|
||
private TypeElement setElement() { | ||
return elements.getTypeElement(Set.class.getName()); | ||
} | ||
|
||
private TypeElement mapElement() { | ||
return elements.getTypeElement(Map.class.getName()); | ||
} | ||
|
||
private TypeMirror unboundedWildcard() { | ||
return types.getWildcardType(null, null); | ||
} | ||
} |
137 changes: 137 additions & 0 deletions
137
java/dagger/internal/codegen/DelegateBindingExpression.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* 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; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
import static com.google.common.collect.Iterables.getOnlyElement; | ||
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom; | ||
import static dagger.internal.codegen.Scope.reusableScope; | ||
|
||
import com.squareup.javapoet.ClassName; | ||
import javax.lang.model.type.TypeMirror; | ||
import javax.lang.model.util.Elements; | ||
|
||
/** A {@link BindingExpression} for {@code @Binds} methods. */ | ||
final class DelegateBindingExpression extends BindingExpression { | ||
private final ContributionBinding binding; | ||
private final ComponentBindingExpressions componentBindingExpressions; | ||
private final DaggerTypes types; | ||
private final Elements elements; | ||
private final BindsTypeChecker bindsTypeChecker; | ||
|
||
private DelegateBindingExpression( | ||
ResolvedBindings resolvedBindings, | ||
ComponentBindingExpressions componentBindingExpressions, | ||
DaggerTypes types, | ||
Elements elements) { | ||
super(resolvedBindings); | ||
this.binding = checkNotNull(resolvedBindings.contributionBinding()); | ||
this.componentBindingExpressions = checkNotNull(componentBindingExpressions); | ||
this.types = checkNotNull(types); | ||
this.elements = checkNotNull(elements); | ||
this.bindsTypeChecker = new BindsTypeChecker(types, elements); | ||
} | ||
|
||
static BindingExpression create( | ||
BindingGraph graph, | ||
BindingExpression bindingExpression, | ||
ComponentBindingExpressions componentBindingExpressions, | ||
DaggerTypes types, | ||
Elements elements) { | ||
ResolvedBindings resolvedBindings = bindingExpression.resolvedBindings(); | ||
ContributionBinding binding = resolvedBindings.contributionBinding(); | ||
Binding delegateBinding = | ||
graph | ||
.resolvedBindings() | ||
.get(getOnlyElement(binding.dependencies()).bindingKey()) | ||
.binding(); | ||
ScopeKind bindsScope = ScopeKind.get(binding, graph, elements); | ||
ScopeKind delegateScope = ScopeKind.get(delegateBinding, graph, elements); | ||
if (bindsScope.isSimilarOrWeakerScopeThan(delegateScope)) { | ||
return new DelegateBindingExpression( | ||
resolvedBindings, componentBindingExpressions, types, elements); | ||
} | ||
return bindingExpression; | ||
} | ||
|
||
@Override | ||
Expression getDependencyExpression( | ||
DependencyRequest.Kind requestKind, ClassName requestingClass) { | ||
Expression delegateExpression = | ||
componentBindingExpressions.getDependencyExpression( | ||
getOnlyElement(binding.dependencies()).bindingKey(), requestKind, requestingClass); | ||
|
||
TypeMirror contributedType = binding.contributedType(); | ||
switch (requestKind) { | ||
case INSTANCE: | ||
return instanceRequiresCast(delegateExpression, requestingClass) | ||
? delegateExpression.castTo(contributedType) | ||
: delegateExpression; | ||
default: | ||
return castToRawTypeIfNecessary( | ||
delegateExpression, requestKind.type(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, | ||
RELEASABLE, | ||
SINGLE_CHECK, | ||
DOUBLE_CHECK, | ||
; | ||
|
||
static ScopeKind get(Binding binding, BindingGraph graph, Elements elements) { | ||
if (!binding.scope().isPresent()) { | ||
return UNSCOPED; | ||
} | ||
|
||
Scope scope = binding.scope().get(); | ||
if (graph.scopesRequiringReleasableReferenceManagers().contains(scope)) { | ||
return RELEASABLE; | ||
} | ||
return scope.equals(reusableScope(elements)) ? SINGLE_CHECK : DOUBLE_CHECK; | ||
} | ||
|
||
boolean isSimilarOrWeakerScopeThan(ScopeKind other) { | ||
return ordinal() <= other.ordinal(); | ||
} | ||
} | ||
} |
Oops, something went wrong.