From df43ccb63d9d21979398c753756e9a9d843f22d5 Mon Sep 17 00:00:00 2001 From: dpb Date: Tue, 25 Apr 2017 10:19:10 -0700 Subject: [PATCH] Use a raw framework class to avoid Java 7's poor type inference when scoping generic @Inject classes. Fixes https://github.com/google/dagger/issues/671 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154187714 --- .../codegen/AbstractComponentWriter.java | 25 +++++++++----- .../internal/codegen/ContributionBinding.java | 2 +- javatests/dagger/functional/GenericTest.java | 18 ++++++++-- .../functional/ScopedSimpleGeneric.java | 33 +++++++++++++++++++ .../functional/SingletonGenericComponent.java | 5 +++ 5 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 javatests/dagger/functional/ScopedSimpleGeneric.java diff --git a/java/dagger/internal/codegen/AbstractComponentWriter.java b/java/dagger/internal/codegen/AbstractComponentWriter.java index 8262d7a9bd7..97b89c9e1ff 100644 --- a/java/dagger/internal/codegen/AbstractComponentWriter.java +++ b/java/dagger/internal/codegen/AbstractComponentWriter.java @@ -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; @@ -967,10 +968,7 @@ private Optional 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(); @@ -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: @@ -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 maybeScope) { + if (!maybeScope.isPresent()) { + return factoryCreate; + } + Scope scope = maybeScope.get(); if (requiresReleasableReferences(scope)) { return CodeBlock.of( "$T.create($L, $L)", diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java index ee6edd717c4..b95f04a8921 100644 --- a/java/dagger/internal/codegen/ContributionBinding.java +++ b/java/dagger/internal/codegen/ContributionBinding.java @@ -226,7 +226,7 @@ FactoryCreationStrategy factoryCreationStrategy() { /** * The {@link TypeMirror type} for the {@code Factory} or {@code Producer} which is created - * for this binding. Uses the binding's key, V in the came of {@code Map>>}, + * for this binding. Uses the binding's key, V in the case of {@code Map>>}, * and E {@code Set} for {@link dagger.multibindings.IntoSet @IntoSet} methods. */ final TypeMirror contributedType() { diff --git a/javatests/dagger/functional/GenericTest.java b/javatests/dagger/functional/GenericTest.java index 09c1fd4a1bf..be17446ef32 100644 --- a/javatests/dagger/functional/GenericTest.java +++ b/javatests/dagger/functional/GenericTest.java @@ -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 = component.scopedGenericA(); assertThat(a).isSameAs(component.scopedGenericA()); assertThat(a.t).isNotNull(); - + ScopedGeneric 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 = component.scopedSimpleGenericA(); + assertThat(a).isSameAs(component.scopedSimpleGenericA()); + + ScopedSimpleGeneric b = component.scopedSimpleGenericB(); + assertThat(b).isSameAs(component.scopedSimpleGenericB()); + assertThat(a).isNotSameAs(b); } diff --git a/javatests/dagger/functional/ScopedSimpleGeneric.java b/javatests/dagger/functional/ScopedSimpleGeneric.java new file mode 100644 index 00000000000..8e48eba2774 --- /dev/null +++ b/javatests/dagger/functional/ScopedSimpleGeneric.java @@ -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>} to {@code Provider>}. + */ +@Singleton +class ScopedSimpleGeneric { + @Inject + ScopedSimpleGeneric() {} +} diff --git a/javatests/dagger/functional/SingletonGenericComponent.java b/javatests/dagger/functional/SingletonGenericComponent.java index adb610a8100..d9823cde75c 100644 --- a/javatests/dagger/functional/SingletonGenericComponent.java +++ b/javatests/dagger/functional/SingletonGenericComponent.java @@ -24,6 +24,11 @@ interface SingletonGenericComponent { ScopedGeneric scopedGenericA(); + ScopedGeneric scopedGenericB(); + + ScopedSimpleGeneric scopedSimpleGenericA(); + + ScopedSimpleGeneric scopedSimpleGenericB(); }