Skip to content

Commit

Permalink
Use a raw framework class to avoid Java 7's poor type inference when …
Browse files Browse the repository at this point in the history
…scoping generic @Inject classes.

Fixes #671

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=154187714
  • Loading branch information
netdpb authored and ronshapiro committed Apr 25, 2017
1 parent 3fb80c6 commit df43ccb
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 12 deletions.
25 changes: 17 additions & 8 deletions java/dagger/internal/codegen/AbstractComponentWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static dagger.internal.codegen.BindingKey.contribution;
import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD;
import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
import static dagger.internal.codegen.MemberSelect.emptyFrameworkMapFactory;
Expand Down Expand Up @@ -967,10 +968,7 @@ private Optional<CodeBlock> initializeContributionBinding(BindingKey bindingKey)
ImmutableList.of(
initializeDeferredDependencies(binding),
initializeMember(
bindingKey,
binding.scope().isPresent()
? decorateForScope(delegatingCodeBlock, binding.scope().get())
: delegatingCodeBlock))));
bindingKey, decorateForScope(delegatingCodeBlock, binding.scope())))));
case SINGLETON_INSTANCE:
if (!binding.scope().isPresent()) {
return Optional.empty();
Expand Down Expand Up @@ -1201,9 +1199,16 @@ private CodeBlock initializeFactoryForContributionBinding(ContributionBinding bi
"$T.create($L)",
generatedClassNameForBinding(binding),
makeParametersCodeBlock(arguments));
return binding.scope().isPresent()
? decorateForScope(factoryCreate, binding.scope().get())
: factoryCreate;

// If scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the
// type properly, so cast to a raw framework type before scoping.
if (binding.bindingKind().equals(INJECTION)
&& binding.unresolved().isPresent()
&& binding.scope().isPresent()) {
factoryCreate =
CodeBlock.of("($T) $L", binding.bindingType().frameworkClass(), factoryCreate);
}
return decorateForScope(factoryCreate, binding.scope());
}

case COMPONENT_PRODUCTION:
Expand Down Expand Up @@ -1278,7 +1283,11 @@ private TypeElement dependencyTypeForBinding(ContributionBinding binding) {
return graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement().get());
}

private CodeBlock decorateForScope(CodeBlock factoryCreate, Scope scope) {
private CodeBlock decorateForScope(CodeBlock factoryCreate, Optional<Scope> maybeScope) {
if (!maybeScope.isPresent()) {
return factoryCreate;
}
Scope scope = maybeScope.get();
if (requiresReleasableReferences(scope)) {
return CodeBlock.of(
"$T.create($L, $L)",
Expand Down
2 changes: 1 addition & 1 deletion java/dagger/internal/codegen/ContributionBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ FactoryCreationStrategy factoryCreationStrategy() {

/**
* The {@link TypeMirror type} for the {@code Factory<T>} or {@code Producer<T>} which is created
* for this binding. Uses the binding's key, V in the came of {@code Map<K, FrameworkClass<V>>>},
* for this binding. Uses the binding's key, V in the case of {@code Map<K, FrameworkClass<V>>>},
* and E {@code Set<E>} for {@link dagger.multibindings.IntoSet @IntoSet} methods.
*/
final TypeMirror contributedType() {
Expand Down
18 changes: 15 additions & 3 deletions javatests/dagger/functional/GenericTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,29 @@ public class GenericTest {
PublicSubclass publicSubclass = component.publicSubclass();
assertThat(((Generic)publicSubclass).t).isNotNull();
}

@Test public void singletonScopesAppliesToEachResolvedType() {
SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
ScopedGeneric<A> a = component.scopedGenericA();
assertThat(a).isSameAs(component.scopedGenericA());
assertThat(a.t).isNotNull();

ScopedGeneric<B> b = component.scopedGenericB();
assertThat(b).isSameAs(component.scopedGenericB());
assertThat(b.t).isNotNull();


assertThat(a).isNotSameAs(b);
}

@Test // See https://github.com/google/dagger/issues/671
public void scopedSimpleGenerics() {
SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
ScopedSimpleGeneric<A> a = component.scopedSimpleGenericA();
assertThat(a).isSameAs(component.scopedSimpleGenericA());

ScopedSimpleGeneric<B> b = component.scopedSimpleGenericB();
assertThat(b).isSameAs(component.scopedSimpleGenericB());

assertThat(a).isNotSameAs(b);
}

Expand Down
33 changes: 33 additions & 0 deletions javatests/dagger/functional/ScopedSimpleGeneric.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.functional;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
* An {@link Inject Inject}ed generic class with no dependencies. Its factory class will have a
* generic {@code create()} method returning an object whose type parameters cannot be inferred from
* its arguments. Since it's scoped, the initialization of its field in a generated component must
* use a raw {@link javax.inject.Provider} in order to allow casting from {@code
* Provider<ScopedSimpleGeneric<Object>>} to {@code Provider<ScopedSimpleGeneric<Foo>>}.
*/
@Singleton
class ScopedSimpleGeneric<T> {
@Inject
ScopedSimpleGeneric() {}
}
5 changes: 5 additions & 0 deletions javatests/dagger/functional/SingletonGenericComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
interface SingletonGenericComponent {

ScopedGeneric<A> scopedGenericA();

ScopedGeneric<B> scopedGenericB();

ScopedSimpleGeneric<A> scopedSimpleGenericA();

ScopedSimpleGeneric<B> scopedSimpleGenericB();

}

0 comments on commit df43ccb

Please sign in to comment.